diff --git a/big_vision/__init__.py b/big_vision/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/__pycache__/__init__.cpython-310.pyc b/big_vision/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfa66cf82b641aa87e35835af5a6af722ca8682a Binary files /dev/null and b/big_vision/__pycache__/__init__.cpython-310.pyc differ diff --git a/big_vision/__pycache__/sharding.cpython-310.pyc b/big_vision/__pycache__/sharding.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e86032655dc7aac2f218b0e167c95ef5db86c99a Binary files /dev/null and b/big_vision/__pycache__/sharding.cpython-310.pyc differ diff --git a/big_vision/__pycache__/utils.cpython-310.pyc b/big_vision/__pycache__/utils.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..908193120372b743c0c98453e81115d65ec0b2d1 Binary files /dev/null and b/big_vision/__pycache__/utils.cpython-310.pyc differ diff --git a/big_vision/configs/__init__.py b/big_vision/configs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/configs/bit_i1k.py b/big_vision/configs/bit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..8bd53c318b108bab483923a95e8d3c0df42d709d --- /dev/null +++ b/big_vision/configs/bit_i1k.py @@ -0,0 +1,102 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training BiT on ILSVRC-2012 as in https://arxiv.org/abs/1912.11370 + +Run training of a BiT-ResNet-50x1 variant, which takes ~32min on v3-128: + +big_vision.train \ + --config big_vision/configs/bit_i1k.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.model.depth 50 --config.model.width 1 +""" + +# from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + + +def get_config(runlocal=False): + """Config for training on ImageNet-1k.""" + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 90 + config.num_classes = 1000 + config.loss = 'softmax_xent' + + config.input = dict() + config.input.data = dict( + name='imagenet2012', + split='train[:99%]', + ) + config.input.batch_size = 4096 + config.input.cache_raw = True # Needs up to 120GB of RAM! + config.input.shuffle_buffer_size = 250_000 # Per host. + + pp_common = '|onehot(1000, key="{lbl}", key_result="labels")' + pp_common += '|value_range(-1, 1)|keep("image", "labels")' + config.input.pp = 'decode_jpeg_and_inception_crop(224)|flip_lr' + pp_common.format(lbl='label') + pp_eval = 'decode|resize_small(256)|central_crop(224)' + pp_common + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'bit' + config.model = dict( + depth=50, # You can also pass e.g. [3, 5, 10, 2] + width=1.0, + ) + + # Optimizer section + config.optax_name = 'big_vision.momentum_hp' + config.grad_clip_norm = 1.0 + + # linear scaling rule. Don't forget to sweep if sweeping batch_size. + config.wd = (1e-4 / 256) * config.input.batch_size + config.lr = (0.1 / 256) * config.input.batch_size + config.schedule = dict(decay_type='cosine', warmup_steps=1000) + + # Eval section + def get_eval(split, dataset='imagenet2012'): + return dict( + type='classification', + data=dict(name=dataset, split=split), + pp_fn=pp_eval.format(lbl='label'), + loss_name=config.loss, + log_steps=1000, # Very fast O(seconds) so it's fine to run it often. + cache='final_data', + ) + config.evals = {} + config.evals.train = get_eval('train[:2%]') + config.evals.minival = get_eval('train[99%:]') + config.evals.val = get_eval('validation') + config.evals.v2 = get_eval('test', dataset='imagenet_v2') + config.evals.real = get_eval('validation', dataset='imagenet2012_real') + config.evals.real.pp_fn = pp_eval.format(lbl='real_label') + + # config.evals.fewshot = get_fewshot_lsr(runlocal=runlocal) + # config.evals.fewshot.log_steps = 1000 + + if runlocal: + config.input.batch_size = 32 + config.input.cache_raw = False + config.input.shuffle_buffer_size = 100 + + local_eval = config.evals.val + config.evals = {'val': local_eval} + config.evals.val.cache = 'none' + + return config \ No newline at end of file diff --git a/big_vision/configs/bit_i21k.py b/big_vision/configs/bit_i21k.py new file mode 100644 index 0000000000000000000000000000000000000000..c42342e9ab8ff513211954efab79dd4309fbe101 --- /dev/null +++ b/big_vision/configs/bit_i21k.py @@ -0,0 +1,85 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for pre-training BiT on ImageNet-21k. + +This config relies on the Imagenet-21k tfds dataset, which is not yet +available publicly in TFDS. We intend to add the dataset to public TFDS soon, +and this config will then be runnable. +""" + +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + + +def get_config(): + """Config for training on imagenet-21k.""" + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 90 + config.num_classes = 21843 + config.init_head_bias = -10.0 + config.loss = 'sigmoid_xent' + + config.input = dict() + config.input.data = dict( + name='imagenet21k', + split='full[51200:]', + ) + config.input.batch_size = 4096 + config.input.shuffle_buffer_size = 250_000 # Per host, so small-ish is ok. + + pp_common = '|value_range(-1, 1)|onehot({onehot_args})|keep("image", "labels")' + pp_common_i21k = pp_common.format(onehot_args=f'{config.num_classes}') + pp_common_i1k = pp_common.format(onehot_args='1000, key="label", key_result="labels"') + config.input.pp = 'decode_jpeg_and_inception_crop(224)|flip_lr' + pp_common_i21k + pp_eval = 'decode|resize_small(256)|central_crop(224)' + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'bit_paper' + config.model = dict(depth=50, width=1.0) + + # Optimizer section + config.optax_name = 'big_vision.momentum_hp' + config.grad_clip_norm = 1.0 + + # linear scaling rule. Don't forget to sweep if sweeping batch_size. + config.lr = (0.03 / 256) * config.input.batch_size + config.wd = (3e-5 / 256) * config.input.batch_size + config.schedule = dict(decay_type='cosine', warmup_steps=5000) + + # Evaluations on i21k itself. + def eval_i21k(split): + return dict( + type='classification', + data={**config.input.data, 'split': split}, + pp_fn=pp_eval + pp_common_i21k, + loss_name=config.loss, + log_steps=1000, # Very fast O(seconds) so it's fine to run it often. + ) + config.evals = {} + config.evals.test = eval_i21k('full[:25_600]') + config.evals.val = eval_i21k('full[25_600:51_200]') + config.evals.train = eval_i21k('full[51_200:76_800]') + + # Few-shot evaluators + config.evals.fewshot = get_fewshot_lsr() + config.evals.fewshot.log_steps = 25_000 + + return config \ No newline at end of file diff --git a/big_vision/configs/common.py b/big_vision/configs/common.py new file mode 100644 index 0000000000000000000000000000000000000000..c1628c3ccaa554eb5d2a39e2317fb06953542a6d --- /dev/null +++ b/big_vision/configs/common.py @@ -0,0 +1,188 @@ +# Copyright 2024 Big Vision 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. + +"""A few things commonly used across A LOT of config files.""" + +import string + +import ml_collections as mlc + + +def input_for_quicktest(config_input, quicktest): + if quicktest: + config_input.batch_size = 8 + config_input.shuffle_buffer_size = 10 + config_input.cache_raw = False + + +def parse_arg(arg, lazy=False, **spec): + """Makes ConfigDict's get_config single-string argument more usable. + + Example use in the config file: + + import big_vision.configs.common as bvcc + def get_config(arg): + arg = bvcc.parse_arg(arg, + res=(224, int), + runlocal=False, + schedule='short', + ) + + # ... + + config.shuffle_buffer = 250_000 if not arg.runlocal else 50 + + Ways that values can be passed when launching: + + --config amazing.py:runlocal,schedule=long,res=128 + --config amazing.py:res=128 + --config amazing.py:runlocal # A boolean needs no value for "true". + --config amazing.py:runlocal=False # Explicit false boolean. + --config amazing.py:128 # The first spec entry may be passed unnamed alone. + + Uses strict bool conversion (converting 'True', 'true' to True, and 'False', + 'false', '' to False). + + Args: + arg: the string argument that's passed to get_config. + lazy: allow lazy parsing of arguments, which are not in spec. For these, + the type is auto-extracted in dependence of most complex possible type. + **spec: the name and default values of the expected options. + If the value is a tuple, the value's first element is the default value, + and the second element is a function called to convert the string. + Otherwise the type is automatically extracted from the default value. + + Returns: + ConfigDict object with extracted type-converted values. + """ + # Normalize arg and spec layout. + arg = arg or '' # Normalize None to empty string + spec = {k: get_type_with_default(v) for k, v in spec.items()} + + result = mlc.ConfigDict(type_safe=False) # For convenient dot-access only. + + # Expand convenience-cases for a single parameter without = sign. + if arg and ',' not in arg and '=' not in arg: + # (think :runlocal) If it's the name of sth in the spec (or there is no + # spec), it's that in bool. + if arg in spec or not spec: + arg = f'{arg}=True' + # Otherwise, it is the value for the first entry in the spec. + else: + arg = f'{list(spec.keys())[0]}={arg}' + # Yes, we rely on Py3.7 insertion order! + + # Now, expand the `arg` string into a dict of keys and values: + raw_kv = {raw_arg.split('=')[0]: + raw_arg.split('=', 1)[-1] if '=' in raw_arg else 'True' + for raw_arg in arg.split(',') if raw_arg} + + # And go through the spec, using provided or default value for each: + for name, (default, type_fn) in spec.items(): + val = raw_kv.pop(name, None) + result[name] = type_fn(val) if val is not None else default + + if raw_kv: + if lazy: # Process args which are not in spec. + for k, v in raw_kv.items(): + result[k] = autotype(v) + else: + raise ValueError(f'Unhandled config args remain: {raw_kv}') + + return result + + +def get_type_with_default(v): + """Returns (v, string_to_v_type) with lenient bool parsing.""" + # For bool, do safe string conversion. + if isinstance(v, bool): + def strict_bool(x): + assert x.lower() in {'true', 'false', ''} + return x.lower() == 'true' + return (v, strict_bool) + # If already a (default, type) tuple, use that. + if isinstance(v, (tuple, list)): + assert len(v) == 2 and isinstance(v[1], type), ( + 'List or tuple types are currently not supported because we use `,` as' + ' dumb delimiter. Contributions (probably using ast) welcome. You can' + ' unblock by using a string with eval(s.replace(";", ",")) or similar') + return (v[0], v[1]) + # Otherwise, derive the type from the default value. + return (v, type(v)) + + +def autotype(x): + """Auto-converts string to bool/int/float if possible.""" + assert isinstance(x, str) + if x.lower() in {'true', 'false'}: + return x.lower() == 'true' # Returns as bool. + try: + return int(x) # Returns as int. + except ValueError: + try: + return float(x) # Returns as float. + except ValueError: + return x # Returns as str. + + +def pack_arg(**kw): + """Packs key-word args as a string to be parsed by `parse_arg()`.""" + for v in kw.values(): + assert ',' not in f'{v}', f"Can't use `,` in config_arg value: {v}" + return ','.join([f'{k}={v}' for k, v in kw.items()]) + + +def arg(**kw): + """Use like `add(**bvcc.arg(res=256, foo=bar), lr=0.1)` to pass config_arg.""" + return {'config_arg': pack_arg(**kw), **kw} + + +def _get_field_ref(config_dict, field_name): + path = field_name.split('.') + for field in path[:-1]: + config_dict = getattr(config_dict, field) + return config_dict.get_ref(path[-1]) + + +def format_str(format_string, config): + """Format string with reference fields from config. + + This makes it easy to build preprocess strings that contain references to + fields tha are edited after. E.g.: + + ``` + config = mlc.ConficDict() + config.res = (256, 256) + config.pp = bvcc.format_str('resize({res})', config) + ... + # if config.res is modified (e.g. via sweeps) it will propagate to pp field: + config.res = (512, 512) + assert config.pp == 'resize((512, 512))' + ``` + + Args: + format_string: string to format with references. + config: ConfigDict to get references to format the string. + + Returns: + A reference field which renders a string using references to config fields. + """ + output = '' + parts = string.Formatter().parse(format_string) + for (literal_text, field_name, format_spec, conversion) in parts: + assert not format_spec and not conversion + output += literal_text + if field_name: + output += _get_field_ref(config, field_name).to_str() + return output diff --git a/big_vision/configs/common_fewshot.py b/big_vision/configs/common_fewshot.py new file mode 100644 index 0000000000000000000000000000000000000000..21fdc586a3266c51400e22d6315cd9d130821f09 --- /dev/null +++ b/big_vision/configs/common_fewshot.py @@ -0,0 +1,56 @@ +# Copyright 2024 Big Vision 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. + +"""Most common few-shot eval configuration.""" + +import ml_collections as mlc + + +def get_fewshot_lsr(target_resolution=224, resize_resolution=256, + runlocal=False, **kw): + """Returns a standard-ish fewshot eval configuration.""" + kw.setdefault('representation_layer', 'pre_logits') + kw.setdefault('shots', (1, 5, 10, 25)) + kw.setdefault('l2_reg', 2.0 ** 10) + kw.setdefault('num_seeds', 3) + kw.setdefault('prefix', '') # No prefix as we already use a/ z/ and zz/ + + # Backward-compatible default: + if not any(f'log_{x}' in kw for x in ['steps', 'percent', 'examples', 'epochs']): # pylint: disable=line-too-long + kw['log_steps'] = 25_000 + + config = mlc.ConfigDict(kw) + config.type = 'fewshot_lsr' + config.datasets = { + 'caltech': ('caltech101', 'train', 'test'), # copybara:srtip + 'cars': ('cars196:2.1.0', 'train', 'test'), + 'cifar100': ('cifar100', 'train', 'test'), + 'dtd': ('dtd', 'train', 'test'), + # The first 65000 ImageNet samples have at least 30 shots per any class. + # Commented out by default because needs manual download. + # 'imagenet': ('imagenet2012', 'train[:65000]', 'validation'), + 'pets': ('oxford_iiit_pet', 'train', 'test'), + 'uc_merced': ('uc_merced', 'train[:1000]', 'train[1000:]'), + } if not runlocal else { + 'pets': ('oxford_iiit_pet', 'train', 'test'), + } + config.pp_train = (f'decode|resize({resize_resolution})|' + f'central_crop({target_resolution})|' + f'value_range(-1,1)|keep("image", "label")') + config.pp_eval = (f'decode|resize({resize_resolution})|' + f'central_crop({target_resolution})|' + f'value_range(-1,1)|keep("image", "label")') + config.display_first = [('imagenet', 10)] if not runlocal else [('pets', 10)] + + return config diff --git a/big_vision/configs/load_and_eval.py b/big_vision/configs/load_and_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..7e102b0f561f2cc6ec59439f831e9e289488b7b0 --- /dev/null +++ b/big_vision/configs/load_and_eval.py @@ -0,0 +1,143 @@ +# Copyright 2024 Big Vision 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. + +# pytype: disable=not-writable,attribute-error +# pylint: disable=line-too-long,missing-function-docstring +r"""A config to load and eval key model using the core train.py. + +The runtime varies widely depending on the model, but each one should reproduce +the corresponding paper's numbers. +This configuration makes use of the "arg" to get_config to select which model +to run, so a few examples are given below: + +Run and evaluate a BiT-M ResNet-50x1 model that was transferred to i1k: + +big_vision.train \ + --config big_vision/configs/load_and_eval.py:name=bit_paper,batch_size=8 \ + --config.model_init M-imagenet2012 --config.model.width 1 --config.model.depth 50 + +Run and evaluate the recommended ViT-B/32 from "how to train your vit" paper: + +big_vision.train \ + --config big_vision/configs/load_and_eval.py:name=vit_i21k,batch_size=8 \ + --config.model.variant B/32 --config.model_init howto-i21k-B/32 +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr + + +def eval_only(config, batch_size, spec_for_init): + """Set a few configs that turn trainer into (almost) eval-only.""" + config.total_steps = 0 + config.input = {} + config.input.batch_size = batch_size + config.input.data = dict(name='bv:dummy', spec=spec_for_init) + config.optax_name = 'identity' + config.lr = 0.0 + + config.mesh = [('data', -1)] + config.sharding_strategy = [('params/.*', 'fsdp(axis="data")')] + config.sharding_rules = [('act_batch', ('data',))] + + return config + + +def get_config(arg=''): + config = bvcc.parse_arg(arg, name='bit_paper', batch_size=4) + + # Make the config eval-only by setting some dummies. + eval_only(config, config.batch_size, spec_for_init=dict( + image=dict(shape=(224, 224, 3), dtype='float32'), + )) + + config.evals = dict(fewshot=get_fewshot_lsr()) + + # Just calls the function with the name given as `config`. + # Could also be a giant if-block if you're into that kind of thing. + globals()[config.name](config) + return config + + +def bit_paper(config): + config.num_classes = 1000 + + config.model_name = 'bit_paper' + config.model_init = 'M-imagenet2012' # M = i21k, -imagenet2012 = fine-tuned + config.model = dict(width=1, depth=50) + + def get_eval(split, lbl, dataset='imagenet2012_real'): + return dict( + type='classification', + data=dict(name=dataset, split=split), + loss_name='softmax_xent', + cache='none', # Only run once, on low-mem machine. + pp_fn=( + 'decode|resize(384)|value_range(-1, 1)' + f'|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ), + ) + config.evals.test = get_eval('validation', 'original_label') + config.evals.real = get_eval('validation', 'real_label') + config.evals.v2 = get_eval('test', 'label', 'imagenet_v2') + + +def vit_i1k(config): + config.num_classes = 1000 + + config.model_name = 'vit' + config.model_init = '' # Will be set in sweep. + config.model = dict(variant='S/16', pool_type='gap', posemb='sincos2d', + rep_size=True) + + config.evals.val = dict( + type='classification', + data=dict(name='imagenet2012', split='validation'), + pp_fn='decode|resize_small(256)|central_crop(224)|value_range(-1, 1)|onehot(1000, key="label", key_result="labels")|keep("image", "labels")', + loss_name='softmax_xent', + cache='none', # Only run once, on low-mem machine. + ) + + +def mlp_mixer_i1k(config): + config.num_classes = 1000 + + config.model_name = 'mlp_mixer' + config.model_init = '' # Will be set in sweep. + config.model = dict(variant='L/16') + + config.evals.val = dict( + type='classification', + data=dict(name='imagenet2012', split='validation'), + pp_fn='decode|resize_small(256)|central_crop(224)|value_range(-1, 1)|onehot(1000, key="label", key_result="labels")|keep("image", "labels")', + loss_name='softmax_xent', + cache='none', # Only run once, on low-mem machine. + ) + + +def vit_i21k(config): + config.num_classes = 21843 + + config.model_name = 'vit' + config.model_init = '' # Will be set in sweep. + config.model = dict(variant='B/32', pool_type='tok') + + config.evals.val = dict( + type='classification', + data=dict(name='imagenet21k', split='full[:51200]'), + pp_fn='decode|resize_small(256)|central_crop(224)|value_range(-1, 1)|onehot(21843)|keep("image", "labels")', + loss_name='sigmoid_xent', + cache='none', # Only run once, on low-mem machine. + ) diff --git a/big_vision/configs/mlp_mixer_i1k.py b/big_vision/configs/mlp_mixer_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..8afe9abfd31f4ecb4e53466ea3e2b2794e8af7e7 --- /dev/null +++ b/big_vision/configs/mlp_mixer_i1k.py @@ -0,0 +1,120 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training MLP-Mixer-B/16 model on ILSVRC-2012 ("ImageNet-1k"). + +Achieves 76.3% top-1 accuracy on the test split in 2h11m on TPU v3-128 +with 300 epochs. A shorter 60 epochs run is expected to get to 70.5% in 27m. + +big_vision.train \ + --config big_vision/configs/mlp_mixer_i1k.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ +""" + +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + + +def get_config(mode=None): + """Config for training Mixer on i1k.""" + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 300 + config.num_classes = 1000 + config.loss = 'sigmoid_xent' + config.init_head_bias = -6.9 + + config.input = dict() + config.input.data = dict( + name='imagenet2012', + split='train[:99%]', + ) + config.input.batch_size = 4096 + config.input.cache_raw = True # Needs up to 120GB of RAM! + config.input.shuffle_buffer_size = 250_000 + + config.input.pp = ( + 'decode_jpeg_and_inception_crop(224)' + '|flip_lr' + '|randaug(2,15)' + '|value_range(-1, 1)' + '|onehot(1000, key="label", key_result="labels")' + '|keep("image", "labels")' + ) + pp_eval = ( + 'decode' + '|resize_small(256)|central_crop(224)' + '|value_range(-1, 1)' + '|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ) + + # To continue using the near-defunct randaug op. + config.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'archive.randaug'] + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + config.prefetch_to_device = 2 + + # Model section + config.model_name = 'mlp_mixer' + config.model = dict() + config.model.variant = 'B/16' + config.model.stoch_depth = 0.1 + + config.mixup = dict(fold_in=None, p=0.5) + + # Optimizer section + config.optax_name = 'scale_by_adam' + config.grad_clip_norm = 1. + + config.lr = 0.001 + config.wd = 1e-4 + config.schedule = dict( + decay_type='linear', + warmup_steps=10_000, + linear_end=1e-5, + ) + + # Eval section + def get_eval(split, dataset='imagenet2012'): + return dict( + type='classification', + data=dict(name=dataset, split=split), + pp_fn=pp_eval.format(lbl='label'), + loss_name=config.loss, + log_steps=2500, # Very fast O(seconds) so it's fine to run it often. + cache_final=mode != 'gpu8', + ) + config.evals = {} + config.evals.train = get_eval('train[:2%]') + config.evals.minival = get_eval('train[99%:]') + config.evals.val = get_eval('validation') + config.evals.v2 = get_eval('test', dataset='imagenet_v2') + config.evals.real = get_eval('validation', dataset='imagenet2012_real') + config.evals.real.pp_fn = pp_eval.format(lbl='real_label') + + config.fewshot = get_fewshot_lsr() + + if mode == 'gpu8': + config.total_epochs = 60 + config.input.batch_size = 512 + config.input.cache_raw = False + if mode == 'regression_test': + config.total_epochs = 60 + + return config diff --git a/big_vision/configs/proj/cappa/README.md b/big_vision/configs/proj/cappa/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d5aa147205f0f4139bf40b2ac5d9825120c22522 --- /dev/null +++ b/big_vision/configs/proj/cappa/README.md @@ -0,0 +1,37 @@ +# Image Captioners Are Scalable Vision Learners Too + +*by Michael Tschannen, Manoj Kumar, Andreas Steiner, Xiaohua Zhai, Neil Houlsby, Lucas Beyer* [[arxiv]](https://arxiv.org/abs/2306.07915) + +![CapPa Architecture](./cappa_architecture.png) + +This directory contains a config for training a CapPa model from scratch. +Note that most models in the paper were trained on a proprietary dataset +(WebLI), but similar results can be obtained by training on [LAION](https://laion.ai/). + +By default, this config trains on COCO captions as this data set is readily +available in [TFDS](https://www.tensorflow.org/datasets) without manual steps. +This is not meant to produce a meaningful model, but +provides a way for the user to run the config out of the box. Please update the +config with with a TFDS-wrapped variant of your favorite image/text data set to +train capable models. + +After setting up `big_vision` as described in the [main README](https://github.com/google-research/big_vision#cloud-tpu-vm-setup), training can be launched as follows + +``` +python -m big_vision.trainers.proj.cappa.generative \ + --config big_vision/configs/proj/cappa/pretrain.py \ + --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%m-%d_%H%M'` +``` + +To run the Cap baseline (autoregressive captioning without parallel prediction), +set `config.model.masked_pred_prob = 0.0`. + +### Citation +``` +@inproceedings{tschannen2023image, + title={Image Captioners Are Scalable Vision Learners Too}, + author={Tschannen, Michael and Kumar, Manoj and Steiner, Andreas and Zhai, Xiaohua and Houlsby, Neil and Beyer, Lucas}, + booktitle={Neural Information Processing Systems (NeurIPS)}, + year={2023} +} +``` diff --git a/big_vision/configs/proj/cappa/cappa_architecture.png b/big_vision/configs/proj/cappa/cappa_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf85b74beb97abd4e97d71bd10ca1d8a83c45ab Binary files /dev/null and b/big_vision/configs/proj/cappa/cappa_architecture.png differ diff --git a/big_vision/configs/proj/cappa/pretrain.py b/big_vision/configs/proj/cappa/pretrain.py new file mode 100644 index 0000000000000000000000000000000000000000..8b0df3c1df1c12c50f820ca05237d52f14ce20ad --- /dev/null +++ b/big_vision/configs/proj/cappa/pretrain.py @@ -0,0 +1,140 @@ +# Copyright 2023 Big Vision 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. + +# pylint: disable=line-too-long +r"""Trains a CapPa model (https://arxiv.org/abs/2306.07915) on coco_captions. + +This config is for reference, we never ran a full training on a large +image/text data set on public infrastructure. + +big_vision.trainers.proj.cappa.generative \ + --config big_vision/configs/proj/cappa/pretrain.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` +""" + + +from big_vision.configs import common_fewshot +import big_vision.configs.common as bvcc +import ml_collections + + +def get_config(arg=None): + """Returns the base config.""" + config = bvcc.parse_arg(arg, + runlocal=False, + total_steps=366_500, + batch_size=8*1024, + warmup_steps=10_000, + ) + + config.evals = {} + config.input = {} + config.input.batch_size = config.batch_size if not config.runlocal else 8 + shuffle_buffer_size = 50_000 if not config.runlocal else 50 + + res = 224 + patch_size = 16 + max_text_tokens = 64 + + pp_image = (f'resize({res})|value_range(-1,1)') + + def tokenizer(inkey, outkey): + return (f'tokenize(max_len={max_text_tokens}, model="c4_en", ' + f'eos="sticky", inkey="{inkey}", outkey="{outkey}")') + + pp_coco = (f'decode|{pp_image}|' + 'coco_captions("captions")|choice(inkey="captions", outkey="text")|' + f'{tokenizer("text", "labels")}|keep("image", "labels")') + config.input.pp = pp_coco + + # NOTE: "coco_captions" is way too small a dataset to train on. It's simply + # used here to serve as a smoke test that the implementation works correctly. + config.input.data = dict(name='coco_captions', split='train') # num_examples=82_783 + config.input.shuffle_buffer_size = shuffle_buffer_size + + config.evals.val_coco = { + 'type': 'proj.cappa.perplexity', + 'pred': 'perplexity', + 'log_steps': 1000, + 'data': dict(name='coco_captions', split='val'), # num_examples=5_000 + 'pp_fn': pp_coco, + } + + # Few-shot metrics + config.evals.fewshot = common_fewshot.get_fewshot_lsr( + target_resolution=res, resize_resolution=int(256 / 224 * res)) + config.evals.fewshot.type = 'fewshot_lsr' + config.evals.fewshot.log_steps = 5_000 if not config.runlocal else 5 + config.evals.fewshot.representation_layer = 'pre_logits' + config.evals.fewshot.pred = 'enc_rep' + config.evals.fewshot.pp_eval = config.evals.fewshot.pp_train + + # NOTE: Scoring of the entire imagenet validation set is rather slow: + # ~100 secs / 1k classes / host. + config.evals['imagenet/scoring'] = dict( + type='proj.cappa.scoring_classifier', + pred='score', + log_percent=0.1, + data=dict(name='imagenet2012', split='validation'), + pp_fn=f'decode|{pp_image}|keep("image", "label")', + pp_txt=tokenizer('label', 'labels'), + ) + + for e in config.evals.values(): + e.skip_first = True + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None # 10_000 + + # Model section + config.model_name = 'proj.cappa.cappa' + config.model = ml_collections.ConfigDict() + config.model.num_layers = 12 + config.model.num_heads = 12 + config.model.mlp_dim = 3072 + config.model.emb_dim = 768 + config.model.vocab_size = 32_000 + config.model.patches = (patch_size, patch_size) + config.model.seq_len = max_text_tokens + config.model.posemb_type = 'learn' + + # Decoder + config.model.decoder_num_layers = 6 + # 0 values here mean to use the same value as for the encoder + config.model.decoder_num_heads = 0 + config.model.decoder_mlp_dim = 0 + config.model.decoder_emb_dim = 0 + config.model.dec_dropout_rate = 0.0 + config.model.masked_pred_prob = 0.75 + config.model.masking_ratio = 1.0 + config.model.decoder_bias = False + + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.999) + config.grad_clip_norm = 1.0 + config.label_smoothing = 0.0 + + schedule = dict(decay_type='cosine', + warmup_steps=config.warmup_steps + if not config.runlocal else 5) + + # Standard schedule + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = schedule + + config.seed = 0 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/clippo/README.md b/big_vision/configs/proj/clippo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e33f891363053fad22b1ad4f84c6af6d316e72c5 --- /dev/null +++ b/big_vision/configs/proj/clippo/README.md @@ -0,0 +1,85 @@ +## Image-and-Language Understanding from Pixels Only + +*by Michael Tschannen, Basil Mustafa, Neil Houlsby* [[arxiv]](https://arxiv.org/abs/2212.08045) [[colab]](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/clippo/clippo_colab.ipynb) + +We provide pretrained CLIP with Pixels Only (CLIPPO) models and code to train such models on image/alt-text data sets. + +### Pretrained models + +Six ViT-B/16 models trained on a mix of [`YFCC-100M`](https://arxiv.org/abs/1503.01817) and [`C4`](https://arxiv.org/abs/1910.10683) (some initialized with an [ImageNet21k-pretrained checkpoint](https://github.com/google-research/vision_transformer#vision-transformer)\) are available. +These models were trained using the schedules and hyperparameters described in the paper. We use the full `YFCC-100M` data set, sampling one of the available `title/description/tag` annotations at random for each each example. We drop non-descriptive annotations (e.g. descriptions consisting of digits only) following the filtering procedure outlined in the [LiT paper](https://arxiv.org/abs/2303.04671), Appendix E. The preprocessing for the `C4` data is as described in the paper. + +The tables below show details about the checkpoints and their performance on Vision & Language benchmarks, and [`GLUE`](https://arxiv.org/abs/1804.07461). We also provide a [colab](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/clippo/clippo_colab.ipynb) to load the models, compute embeddings, and perform zero-shot classification. + +##### Checkpoint details + +| model | training dataset | #param. | steps | checkpoint | +|:-----------------|:-------------------|:----------|:--------|:-----------| +| CLIPPO | YFCC-100M | 93M | 250k | `gs://big_vision/clippo/clippo_b16_yfcc100m.npz` | +| CLIPPO I21k init | YFCC-100M | 93M | 250k | `gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init.npz` | +| CLIPPO I21k init | YFCC-100M + 25%C4 | 93M | 333k | `gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_25c4.npz` | +| CLIPPO I21k init | YFCC-100M + 50%C4 | 93M | 500k | `gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_50c4.npz` | +| CLIPPO I21k init | YFCC-100M + 75%C4 | 93M | 500k | `gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_75c4.npz` | +| CLIPPO | C4 | 93M | 250k | `gs://big_vision/clippo/clippo_b16_100c4.npz` | + +##### Vision \& Language results + +| model | training dataset | ImageNet 10-shot | ImageNet 0-shot | MS-COCO I→T | MS-COCO T→I | +|:-----------------|:-------------------|-----------:|----------:|--------:|--------:| +| CLIPPO | YFCC-100M | 38.2 | 43.4 | 34.7 | 19.7 | +| CLIPPO I21k init | YFCC-100M | 44.7 | 47.4 | 36.1 | 21.3 | +| CLIPPO I21k init | YFCC-100M + 25%C4 | 43.8 | 44.8 | 33.3 | 19.4 | +| CLIPPO I21k init | YFCC-100M + 50%C4 | 41.2 | 42.0 | 31.4 | 17.8 | +| CLIPPO I21k init | YFCC-100M + 75%C4 | 34.5 | 33.4 | 26.6 | 14.6 | + +##### GLUE results + +| model | training dataset | MNLI-M/MM | QQP | QNLI | SST-2 | COLA | STS-B | MRPC | RTE | avg | +|:-----------------|:-------------------|:------------|------:|-------:|--------:|-------:|--------:|-------:|------:|------:| +| CLIPPO | YFCC-100M | 71.3 / 71.5 | 79.1 | 67.9 | 85.7 | 0.0 | 14.0 | 83.4 | 54.9 | 58.6 | +| CLIPPO I21k init | YFCC-100M | 70.0 / 70.1 | 83.7 | 81.6 | 86.1 | 0.0 | 18.5 | 83.0 | 53.1 | 60.7 | +| CLIPPO I21k init | YFCC-100M + 25%C4 | 75.7 / 75.1 | 85.2 | 83.5 | 89.6 | 0.0 | 82.3 | 82.7 | 52.7 | 69.7 | +| CLIPPO I21k init | YFCC-100M + 50%C4 | 77.4 / 77.4 | 86.0 | 83.9 | 91.7 | 34.5 | 84.5 | 85.1 | 56.3 | 75.2 | +| CLIPPO I21k init | YFCC-100M + 75%C4 | 79.8 / 79.1 | 86.5 | 84.3 | 92.0 | 44.5 | 85.3 | 88.2 | 58.5 | 77.6 | +| CLIPPO | C4 | 79.9 / 80.2 | 86.7 | 85.2 | 93.3 | 50.9 | 84.7 | 86.3 | 58.5 | 78.4 | + +### Training your own models + +To train your own CLIPPO model, please follow the setup instructions in the [`big_vision` main README](https://github.com/google-research/big_vision#cloud-tpu-vm-setup). In the following, we provide the CLIPPO-specific commands required in addition to the setup, assume you are using the Google Cloud TPU setup (potentially with adapted TPU configuration, see table below). If you are using GPUs, please set up your machine directly and only execute the `--command` portions of the commands below from the `big_vision` repository root. + +The text rendering preproprocessing function requires manual download of the Unifont .hex files from [Unifoundry](https://unifoundry.com/unifont/) (please follow link for license): + +```bash +gcloud alpha compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=all \ +--command "bash big_vision/pp/proj/clippo/download_unifont.sh" +``` + +Launch the training by running + +```bash +gcloud alpha compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=all \ +--command "TFDS_DATA_DIR=gs://$GS_BUCKET_NAME/tensorflow_datasets bash big_vision/run_tpu.sh big_vision.trainers.proj.image_text.contrastive --config big_vision/configs/proj/clippo/train_clippo.py --workdir gs://$GS_BUCKET_NAME/big_vision/workdir/`date '+%m-%d_%H%M'`" +``` + +*Important note:* The input pipeline relies on [TensorFlow Datasets (TFDS)](https://www.tensorflow.org/datasets) which does not provide automatic integration with large image/alt-text datasets out of the box. The above config therefore trains by default on MS-COCO Captions which can be automatically downloaded via TFDS, and additionally initializes the CLIPPO ViT backbone with weights pretrained on ImageNet21k. This setup is not meant to produce good accuracy, but to provide the user with a way to sanity-check their setup. If you want to train on a large data set such as [`LAION-400M`](https://arxiv.org/abs/2111.02114) or [`YFCC-100M`](https://arxiv.org/abs/1503.01817), please follow [these instructions](https://www.tensorflow.org/datasets/add_dataset) to wrap your data set using TFDS, and update the dataset in the config accordingly. Also note that the ImageNet1k evaluations require manual download of the data, see [these instructions](https://github.com/google-research/big_vision#preparing-tfds-data). To train with your own data set and with ImageNet1k-based evaluations, use `--config big_vision/configs/proj/clippo/train_clippo.py:test_with_coco=False,i1k_eval=True` in the command above. + +##### Expected results + +| train dataset | batch size | #steps | TPU chips | ImageNet 0-shot | MS-COCO I→T | MS-COCO T→I | Config `arg` | +| :--- | ---: | ---: | ---: | :---: | :---: | :---: | :--- | +| *MS-COCO (sanity check)* | 4000 | 400 | 32 v3 | 4.2 | 12.6 | 8.6 | `i1k_eval=True` | +| LAION-400M | 8192 | 100k |128 v2 | 51.5 | 44.8 | 29.3 | `test_with_coco=False,i1k_eval=True` | +| LAION-400M | 10240\* | 100k | 128 v3 | 53.6 | 46.7 | 30.3 | `test_with_coco=False,i1k_eval=True` | + +\* The experiments in the paper use a batch size of 10240 which requires a memory-optimized ViT implementation to run on 128 TPU v2 chips or 128 TPU v3 chips (in which case the TPU memory capacity allows to increase the batch size beyond 10240). + +### Citation + +``` +@inproceedings{tschannen2023image, + title={Image-and-Language Understanding from Pixels Only}, + author={Tschannen, Michael and Mustafa, Basil and Houlsby, Neil}, + booktitle={Proc. IEEE/CVF Conference on Computer Vision and Pattern Recognition (CVPR)}, + year={2023} +} +``` diff --git a/big_vision/configs/proj/clippo/clippo_colab.ipynb b/big_vision/configs/proj/clippo/clippo_colab.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..dd540a85114007630b052ee024bd1cd62da45204 --- /dev/null +++ b/big_vision/configs/proj/clippo/clippo_colab.ipynb @@ -0,0 +1 @@ +{"cells":[{"cell_type":"markdown","metadata":{"id":"HRDyfbk2klCU"},"source":["# CLIPPO colab\n","\n","Paper: [Image-and-Language Understanding from Pixels Only](https://arxiv.org/abs/2212.08045)\n","\n","This colab shows how to\n","- load pretrained CLIP with Pixels Only (CLIPPO) models,\n","- use them to compute image and text embeddings,\n","- perform zero-shot image and text classification.\n","\n","Six ViT-B/16 models trained on a mix of [YFCC-100M](https://arxiv.org/abs/1503.01817) and [C4](https://arxiv.org/abs/1910.10683) (some initialized with an [ImageNet21k-pretrained checkpoint](https://github.com/google-research/vision_transformer#vision-transformer)\\) are available. Please refer to the [GitHub readme](https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/clippo/README.md) for training code and details on the checkpoints.\n","\n","This colab is derived from the [colab](https://colab.research.google.com/github/google-research/vision_transformer/blob/main/lit.ipynb) accompanying the paper [LiT: Zero-Shot Transfer with Locked-Image Text Tuning](https://arxiv.org/abs/2111.07991)."]},{"cell_type":"markdown","metadata":{"id":"Vauyt3WMehFx"},"source":["## Set up the environment"]},{"cell_type":"code","execution_count":1,"metadata":{"id":"JHkfIOBXp-2J","colab":{"base_uri":"https://localhost:8080/"},"outputId":"2f09c28a-8c36-4379-c670-164a74cd95e8","executionInfo":{"status":"ok","timestamp":1678890204634,"user_tz":-60,"elapsed":2243,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"stream","name":"stdout","text":["Cloning into 'big_vision'...\n","remote: Enumerating objects: 210, done.\u001b[K\n","remote: Counting objects: 100% (210/210), done.\u001b[K\n","remote: Compressing objects: 100% (181/181), done.\u001b[K\n","remote: Total 210 (delta 40), reused 102 (delta 18), pack-reused 0\u001b[K\n","Receiving objects: 100% (210/210), 470.30 KiB | 10.94 MiB/s, done.\n","Resolving deltas: 100% (40/40), done.\n","Already up to date.\n"]}],"source":["# Clone the big_vision repository\n","!git clone --branch=main --depth=1 https://github.com/google-research/big_vision\n","!cd big_vision && git pull"]},{"cell_type":"code","source":["# Install the python dependencies\n","!pip install -qr big_vision/big_vision/requirements.txt"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"NWLvz5yvH7La","executionInfo":{"status":"ok","timestamp":1678890254330,"user_tz":-60,"elapsed":49697,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"6388a22b-e079-4103-cc03-127b38820d65"},"execution_count":2,"outputs":[{"output_type":"stream","name":"stdout","text":[" Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m96.8/96.8 KB\u001b[0m \u001b[31m2.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.6/41.6 KB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m214.2/214.2 KB\u001b[0m \u001b[31m9.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.4/5.4 MB\u001b[0m \u001b[31m12.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m26.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.8/5.8 MB\u001b[0m \u001b[31m25.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m367.1/367.1 KB\u001b[0m \u001b[31m11.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 KB\u001b[0m \u001b[31m4.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m8.4/8.4 MB\u001b[0m \u001b[31m22.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m238.7/238.7 KB\u001b[0m \u001b[31m20.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m74.2/74.2 KB\u001b[0m \u001b[31m7.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m87.9/87.9 KB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m349.9/349.9 KB\u001b[0m \u001b[31m18.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.0/3.0 MB\u001b[0m \u001b[31m9.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.1/1.1 MB\u001b[0m \u001b[31m32.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m84.5/84.5 KB\u001b[0m \u001b[31m8.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n","\u001b[?25h Building wheel for flaxformer (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Building wheel for optax (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Building wheel for panopticapi (setup.py) ... \u001b[?25l\u001b[?25hdone\n"," Building wheel for ml-collections (setup.py) ... \u001b[?25l\u001b[?25hdone\n","\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n","ipython 7.9.0 requires jedi>=0.10, which is not installed.\u001b[0m\u001b[31m\n","\u001b[0m"]}]},{"cell_type":"code","source":["# Download Unifont for text rendering\n","!wget https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont-9.0.06.hex.gz https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont_upper-9.0.06.hex.gz\n","!gunzip unifont-9.0.06.hex.gz unifont_upper-9.0.06.hex.gz\n","!mv unifont-9.0.06.hex unifont_upper-9.0.06.hex big_vision/big_vision/pp/proj/clippo/"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"VAkDMh3yQF1A","executionInfo":{"status":"ok","timestamp":1678890255858,"user_tz":-60,"elapsed":1535,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"0e2851b4-95c6-4260-c3b7-58c2e745aa99"},"execution_count":3,"outputs":[{"output_type":"stream","name":"stdout","text":["--2023-03-15 14:24:14-- https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont-9.0.06.hex.gz\n","Resolving unifoundry.com (unifoundry.com)... 107.180.4.157\n","Connecting to unifoundry.com (unifoundry.com)|107.180.4.157|:443... connected.\n","HTTP request sent, awaiting response... 200 OK\n","Length: 939547 (918K) [application/x-gzip]\n","Saving to: ‘unifont-9.0.06.hex.gz’\n","\n","unifont-9.0.06.hex. 100%[===================>] 917.53K 1.14MB/s in 0.8s \n","\n","2023-03-15 14:24:15 (1.14 MB/s) - ‘unifont-9.0.06.hex.gz’ saved [939547/939547]\n","\n","--2023-03-15 14:24:15-- https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont_upper-9.0.06.hex.gz\n","Reusing existing connection to unifoundry.com:443.\n","HTTP request sent, awaiting response... 200 OK\n","Length: 112342 (110K) [application/x-gzip]\n","Saving to: ‘unifont_upper-9.0.06.hex.gz’\n","\n","unifont_upper-9.0.0 100%[===================>] 109.71K --.-KB/s in 0.001s \n","\n","2023-03-15 14:24:15 (165 MB/s) - ‘unifont_upper-9.0.06.hex.gz’ saved [112342/112342]\n","\n","FINISHED --2023-03-15 14:24:15--\n","Total wall clock time: 1.3s\n","Downloaded: 2 files, 1.0M in 0.8s (1.27 MB/s)\n"]}]},{"cell_type":"code","source":["%cd big_vision"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"Ri6EirCxRCLW","executionInfo":{"status":"ok","timestamp":1678890255858,"user_tz":-60,"elapsed":4,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"e262823d-d502-4159-a731-0c1649a85a85"},"execution_count":4,"outputs":[{"output_type":"stream","name":"stdout","text":["/content/big_vision\n"]}]},{"cell_type":"markdown","source":["## Load a checkpoint and initialize the model"],"metadata":{"id":"TskButpkoWBz"}},{"cell_type":"code","execution_count":5,"metadata":{"id":"4DS88TsHsli7","executionInfo":{"status":"ok","timestamp":1678890260586,"user_tz":-60,"elapsed":4730,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[],"source":["import jax\n","import jax.numpy as jnp\n","from matplotlib import pyplot as plt\n","import numpy as np\n","import pandas as pd\n","import tensorflow as tf\n","import tensorflow_datasets as tfds\n","import tqdm\n","import importlib"]},{"cell_type":"code","source":["# Select the checkpoint and download it\n","checkpoint_paths = {\n"," 'clippo_b16_yfcc100m': 'gs://big_vision/clippo/clippo_b16_yfcc100m.npz',\n"," 'clippo_b16_yfcc100m_i21k_init': 'gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init.npz',\n"," 'clippo_b16_yfcc100m_i21k_init_25c4': 'gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_25c4.npz',\n"," 'clippo_b16_yfcc100m_i21k_init_50c4': 'gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_50c4.npz',\n"," 'clippo_b16_yfcc100m_i21k_init_75c4': 'gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_75c4.npz',\n"," 'clippo_b16_100c4': 'gs://big_vision/clippo/clippo_b16_100c4.npz'\n","}\n","\n","checkpoint = 'clippo_b16_yfcc100m_i21k_init_25c4'\n","checkpoint_path = checkpoint_paths[checkpoint]\n","!gsutil cp $checkpoint_path ."],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"y7DA8gxrSe0B","executionInfo":{"status":"ok","timestamp":1678890270393,"user_tz":-60,"elapsed":9812,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"f76d861d-f55c-4a6d-cc2e-a41c6eb81dde"},"execution_count":6,"outputs":[{"output_type":"stream","name":"stdout","text":["Copying gs://big_vision/clippo/clippo_b16_yfcc100m_i21k_init_25c4.npz...\n","/ [0 files][ 0.0 B/540.4 MiB] \r==> NOTE: You are downloading one or more large file(s), which would\n","run significantly faster if you enabled sliced object downloads. This\n","feature is enabled by default but requires that compiled crcmod be\n","installed (see \"gsutil help crcmod\").\n","\n","\\ [1 files][540.4 MiB/540.4 MiB] \n","Operation completed over 1 objects/540.4 MiB. \n"]}]},{"cell_type":"code","source":["from big_vision.configs.proj.clippo import train_clippo\n","from big_vision import utils\n","\n","# The models are trained for resolution 224\n","RES = 224\n","\n","# Load model module\n","config = train_clippo.get_config()\n","model_module = importlib.import_module(f'big_vision.models.{config.model_name}')\n","model = model_module.Model(**config.model)\n","\n","# Load model parameters\n","params = utils.load_checkpoint(None, checkpoint_path)['params']"],"metadata":{"id":"dC2pE8r8GF4g","executionInfo":{"status":"ok","timestamp":1678890287922,"user_tz":-60,"elapsed":17532,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":7,"outputs":[]},{"cell_type":"code","source":["# Define and load preprocessing functions\n","from big_vision.pp import builder as pp_builder\n","for pp_modules in config.pp_modules:\n"," importlib.import_module(f'big_vision.pp.{pp_modules}')\n","\n","# Unifont renderer\n","def tokenizer(inkey='text', outkey='text'):\n"," return (f'render_unifont('\n"," f'inkey=\"{inkey}\", '\n"," f'outkey=\"{outkey}\", '\n"," f'image_size={RES}, '\n"," f'lower=True, '\n"," f'font_size=16, '\n"," f'text_brightness=0, '\n"," f'background_brightness=127)|'\n"," f'value_range(-1, 1, inkey=\"{outkey}\", outkey=\"{outkey}\")')\n","\n","pp_image_str = f'resize({RES})|value_range(-1,1)'\n","pp_text_str = tokenizer()\n","\n","pp_image_fn = pp_builder.get_preprocess_fn(pp_image_str)\n","pp_text_fn = pp_builder.get_preprocess_fn(pp_text_str)\n","\n","def preprocess_images(images):\n"," return [np.array(pp_image_fn({'image': img})['image']) for img in images]\n","\n","def preprocess_texts(texts):\n"," return [np.array(pp_text_fn({'text': text})['text']) for text in texts]"],"metadata":{"id":"pSXto6vnMEcB","executionInfo":{"status":"ok","timestamp":1678890294214,"user_tz":-60,"elapsed":6296,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":8,"outputs":[]},{"cell_type":"markdown","metadata":{"id":"fzQg_w-Cn8a2"},"source":["## Use the model"]},{"cell_type":"markdown","source":["### Compute and compare image/text embeddings"],"metadata":{"id":"Y4LxH_faqXG8"}},{"cell_type":"code","execution_count":9,"metadata":{"id":"YBd39mOzmjPK","outputId":"b090c383-bda3-4c10-c816-0f810a06304e","colab":{"base_uri":"https://localhost:8080/","height":185,"referenced_widgets":["dccdc3dac09849d89bb4e33cd84f342e","5375b788c4434c70ab1174814f8f8c01","d2ed5d9e693649dbbce268b311751c42","3bb94f09d06b4f21a36d797603167e0e","daecd79e3f4d4f8ea5bdfaddfa979047","8fdbc2c453284869844620f0b3d48118","0b6bae24732c4f4e8c6533057db1c320","eeca15fa7845458aac9d0ac4564eb683","181c3157bb5c4d0682092d4362c71e38","0dae83dd44984685b6b547ea563f2ac5","499675f6f43449e1b457a943bb89f5e6","ad89503ecf0a4ce09e5d817b6aeca758","774eb9564b324fb398ec9f4257f67673","1b3a0f324a4f477eade4332e2f40b115","1986deffd06e4ed3bf882576f218ecb8","93da5eeb980f416da18ed48ebd3d025d","2a068826d3d94f9199c42bdc4381de7c","4a14f7780cae4c9ba6dd0b3588e713ea","ecfea453250a49caadb5162d3b02ac40","9879ce5353e04f738660e92d78a3abcd","e3bc1b53a7f94ed3bd409c309ca7fa3a","f0122c02070d44e9ab72d6189e65cfb5","1756f9ca57194e05a21e86be3390c160","353e41bf89064820b79b2d0c0ea7df31","bb8c6693952a4dba95e0d16305b6bd3f","7d00a1fa21354a51805a415db2410a7c","5c4eafb7fe01476e8306e62dda1d46f6","e53866d027f64bd4b299ecfdcf4f0a1c","7f7af62a6dd14868a0b35e520fec9d6a","e1e62a44c0b74e9781e93342838a744e","a84b52168d624ca1b442e989950b3b97","c5497a566d504a6d926c4a74d24fea75","c7cf618be98a4ef4954e83ca3b49874d","b157a99f67604979b1cd918b68fca6ac","6c00492230ed4b93af80dfc8e92b029b","b86e74d1b6ff42129cffb98dec1f3f7b","651f7d28f1a24bb3b7e48b3692a220e2","3498b3fee9b54ee0889f21bb893c78be","a781b7bb93a7451ba0c80d0f29986898","0d321d1d25fd40ef83074eba17b66080","4c0e7be5d0a94baaaadc5a6500dd9b6c","d34fe0909ab249058650c2cf8ceb35c4","7669118fa50947c5b9ae662fb6267f1d","6c6e27d70026499abaee7600b26fde29","ff8597efa78344a09d83e4a6db42abfd","0725627ce3414d1c92b340d3d3f8f281","de52e4ca1d6f4e0092d6c68274d3af8f","78fa9b95d9c649d9be746399446eec77","cdf7a866e43a4bdaa0c809b78741f757","4a10c07fa0e1434984871acb73d5f46a","53893397fee74fd5b6a34dada619b6d1","3af6c8755e7c49f6aa01f45364a7e77e","7c4f5b790f1d4c00aa975141a8ede738","b5d6c41c9a9141dc94963faa2d4a676a","8e2d89124d264321ab04e8b7716c7d33","5c711a79f5bc4b08b3dd2015e948adad","d78bc7be8f3847569d3134d5db2b5c8f","55ca92dd00c74bff961c46f74aa95558","c0cd6e7644ce4769b1cf9a15ee503d0d","9b2109cef3404964b4b3e0e299369ca3","c7a7c829ed2e40d6b9aeba7cd2792361","d103db0b223d4b6a927321aaee909f1c","df02c258cb1f47a48a5bda4dbd1cd01a","1c2f0535a5494ae1b557dd8426fd69c1","1ea41bb66d8c41639cf8d9273fa8c6d3","07a7298243ea44e5aa95adb338c2b5fa","4e71c9ec77e04e0fb85f83f739f48949","0d2f9fb372734a2cb58c1027afd2ab64","8ffb631b45d7432193da5c756028a061","9e1179561ab6434eabea9eb1238dae43","d41263cf6e4845c29612dc1f8a113a64","e0cb8d6dfb7349d7b7cfe1698140b29b","5e4a96dea2154789be89dc50523167d1","fa2922365ee043908b03abf0cc8b3d46","6c7f68bc82da44c89df1fe69883af09b","c9c3213ba5184b9ca4f46601d372a291","825b35a2d7cf4606ad7056d60f59e907","4464bdf63c684c748e530de69bdb8da3","b4a8f48c427642b7a5e574794c67c450","353d24473b444907b18c2d8eb1853d1b","785f446240fb476a8aa5cb1b8fb38573","8a813d3ace9e4c9f8defa24c29ee96ac","cf0747e7cf8a4fb6af9a8a2d2ff4885f","4cf0aab25b1c43338c8761e1ff492631","3993f8faf3794a9dae2c55e13b8938e4","33a70dcd558646f09e73060acdb80a98","af99dc606a7c4da0931365780439f632","e4047f4ac6824cacb4a0f2f1c5da6b48"]},"executionInfo":{"status":"ok","timestamp":1678890417359,"user_tz":-60,"elapsed":123162,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"stream","name":"stdout","text":["Downloading and preparing dataset Unknown size (download: Unknown size, generated: Unknown size, total: Unknown size) to /root/tensorflow_datasets/imagenette/full-size-v2/1.0.0...\n"]},{"output_type":"display_data","data":{"text/plain":["Dl Completed...: 0 url [00:00, ? url/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"dccdc3dac09849d89bb4e33cd84f342e"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Dl Size...: 0 MiB [00:00, ? MiB/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"ad89503ecf0a4ce09e5d817b6aeca758"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Extraction completed...: 0 file [00:00, ? file/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"1756f9ca57194e05a21e86be3390c160"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Generating splits...: 0%| | 0/2 [00:00"],"image/png":"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(internal link)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(internal link)/HssG31fSpCZJq22zpfOkqS96taGJKuzlwBXY04zMLFZuDp6SXLowV+s+Zs64mcolyDKYHw5BmuJJbB87W/9OtEq3F9z6JfcHp2zm4Y9phljBN/5zd/C9f2/B/+r/8Rh63k2sEH1pdrhuDJ3oMP1YOncHLzJqP3PHr6uFaOBOzvOsfmIklRXhW61tIag+sbjFfEksQfKof9vd8NW5RWvPHGj3jy/IzHvvAVIwz6OIW9GWih+vykRHOwIMaIShHTtjX/h7Zx3D5e8pdef5VV6yRfzlkY1HW909rQGsk3cxYPDAnvNE3bslgtmUoh5LIv8KScsEq8zkR+tBpeq7LXxX8BmKxjUM/zd7piTiulpeu3YplzwbtkefaLgtWyYc/yRmRmhBh+RQiYwfTrxKh99139fSau7TuAqeC6uQLQz083nJ5d8Nqr919grWul98/LRx+7SvQLgZwk9l0sl/Uz87Ndrn1v7fy85qkwh1chhX1eWfYAuxRThDQ4X1lVJWA+fDzzd4HbH3MF6fex9JX86OdFyX7mEjF/Wtuc6F69wLXKyrWfP3EHn+M9++/6KJD+RbfP2scVo+7T3q9e+Dv8DI6LL3bZ/jS3z/PdXxT0vf7+n7Tb4ot854eNUf80tp/mGnyR93wYlPrw3z9OduHzfucXPabP2n8phWmaODs72wOeUl2U5yqGgC2F1oqBTds0pJg4Pz/nn/yTf8wffvMbTNVAbbfbMlMB529QMVJChgiuCEu8Xy5Z9D3HywUrayEndCnYxZI0jZAyThuWrsF23d71W+mCaQRUQCuigo1PXE4en1NN+ZS07y560U6OmewcqoBJGazDJuF4CECYUFnYDOIA7kALY9toWfhTKTgnbDa/2+FDEGB9BkO1MDVjzhgE3BQQdG5Zq8CPNiir8N6ji5jCTtOAUoaua3HO7RkXpczSLTBNfi8XorVmtTrg4vKSd999jyePH3Ny45h7D+6L5rzWpJToqjktKKyVQLVpGqbJs9vtJBgpV3qK1jbMjG01B7AK4SnkRNu2wDUN8xBoW8vh4SFfef0rvPXGPS6ePmK7lnbcN994k/uvfIVxDJw+P+fh4yf8xl//DR49fswb3/8BbdMxDAPn5+d88MEHLJfLvfntejvw/PzH/MJf+Etordlst3zvh2+xXq+JIaG1YeUcy+WKEBI+ZMZpqsC1sOjtLP6NBDTGaLyXKr8waK0E8LVVWAogE2Iad+1ZqkWSOREWPXdhvCsMuUR0mSWC8h4kz1l0nDOSbMYkuu8K9qx5YbDH/fuvEhKukwqYWdzXjqq+XzoI5rbGUmAaAic3jzharTg+OODOzRNiEkM5oxQv3b2LIuOnkU1KTLWwo5zFOCuu9R8KUOdx/uK8drWQzkWX/UulSm2gSVXHmZm9pLV0CnxoDtNa7zsHFIWY4r64k1LGuVakF3LB1OfZWSdsRoS91zpD3zYkXQi7yKJvWSw6TNOKTvM0kVNmmKY9W8ua6v3iLGOIKJ8Yx4nOOrrO4YxmHLYYnTBjogye3TjRPLhH1xcW1rEylien5/SHDSUK4G2VRhXF+mLH+nzLX3j9ZVSGw4OW2zeWJJdYlw27KRF8oTeJ7W5LipHGaL58cpdLE9l5MencDhOthpu95rBVtCZydHJI14js0NOLDb/5y/c4XQ+MIWFty3aC3/vOW7z/5BnvPLngX/21r/LdH7+HLplXXr3Ly/cNPi9YX1zywftb/vjbZ7Ww9MJQQ2vQOhHSBmNFWmuaPN4L70cSz/3b9w9O8gP37t4gDp6l0qzaJTl5nCvEWFgphXNw62gJSvPkIvD0zYecvHwfrQzLZUdGE6eAK5m7xwvU5Ckp7QEeXZ8PKsNGA34axTBQK0qCGKLolNZ5WVepK5R0K9iaXFaVkjrGk4CkCAMzxoD3E1oZ1hcb/LgkR8OUE8tWwNKYAuvtlq5b0LaGUhSj98KOnQKmCLg++IkTu8QZIwzzwZMUhAKBlpXRXPhMXxR9Y9AEvC9kGrCGZFqibUjFkoMipELb9UxxxziMuM6hIqQcSCWhjGVMqZ5LS8qe9vCIYbpkjBNHx7cJmwXHd1fkOLE539LfvUU635B9xC4y/WsvEzdb1G6HU+C1YqksRiuykWeHXIjIXJpR+FwgQ8oKrRtU0+PajqZxxFRN7HItqM1TimSdNQapaesnMSZltmHuJtJKmLM+ZYiJPGyY/MTCtPyKtbjjO3wjJ06nia0fwPa44yNWfUdjRKpiiiKNkrVcQ2MVxViscyQiOSs2l1t2QYoRq/sndH2L8XJ9o8q0jUWRmfxEDCMqOXKIPNu8zfHxy9jGopXC+1A1y1uUciJnEZOArVhCgMmvUbWgbU1LLpm+W4FSxDjhpw0mVoM011NywDlJnrVKnJx8FWscOYMPnu1mx2rhsM6B1uS0AaXR1qGUpahJQGcKpWhU7jEqkFVTE2XR3Zfn3mFNS4lexplWKKeIUYnMTKEy4keamz+PAsZpIIUz0uSxqqVtVvj4mOjPSFNLag9olSL3B7TLBU2/YvKXJK3IWRNCYPSnUBRtc4hreqZ4BhyiWjGQ18bS6EzQWWShmgajxjrOAkp5jDWktMEPl4RLzeLoDo1zNPawsnU1RUcint1UMAYa1yMgoJc5R8n4MNZSgoZ2CVr8brrmBIwlMZLyhGGF1musUxSl2Y5rASFMA6WQpgtCuSTWglpIke2jZyyPT+iXK1JqKKmw3Qxsd2t22zNQHWOIaNNwcHib8/PHDBuPAQ6XDU4Fmm4BaCa/o2kahmmLypF+teTuvftcrp+JwSwKYsPXvvIqaDGF3u7OScWRQ6DkAdVZhmFFpyxN0xKLBj0S0sgYRkzZMG1HTOuwVtO4joPj+yjTUgCfdjTBc7A8wbolu2HHcnmDELaElHG5AXuCGTao6ZK0O2fZrZjGNX7yhKw4eOll1uv3MbqjXzxg0Ryyi4/JOTOFBqM0x8f3xbh12kqUb5IYY6JRxtI0kKInxQnvd6B7dBaW/a07t3nzh+9wdrZlvY1EROu8BGFwZ1Vou8zZ+RM++Kcf8E9/9/f4u//mv8FLX76H05633vkRv/TLv8RbP37E+eUavdnSdx27Teb2nVv8/M+/yunlBU+++c/4wQ/eZhpHusbxc1+7xcmNJb1rieNOJOpWmsWqp6Ml6SKSaiVIQVArTBG5s6bTRAwqa0osBHak1ND1qjK9M4UtED9xFrXWklPeE4ZKBTRz1U83IdSAtLIcPmvLVyFeYWYDV3CxFFKRN6WYiEGICxI2CLC+9TsMkL0njp44TWIwKe6JEEG7DqUslxcbvvnH30AvW+J2JMcknl0ZlFXkkthtNvQHrawtrqVbthw3S2alaNs4vvLVr/Dej9/HGEvb9QybQbowa/AtxTTkOpiCbhRow7tPTnnn4XNoG/7Gz73KnXs3iQp+93f+U37jN3+b3/39f8a7b73FYCP/t//J/5T//P/+/+Af/e7v8tZmzWurW7g7N/mlX/llyWnDJGPNSkFemcoYz5mYQamMiQmVRA+/aRrGYSLsPNqLwSWlcOPoiNdeeZVnjx6SQ0Ylhc6GrCCHhM4JUxKXg3QUO20JOnO229EddHz7h2f86L0nvP71XybFgB9Hhu2Wi/OnvLKUrm6TC65oVkcrHr7xFqTIq199BdcYvJ84Puj4r/32X+Lf+/f/bX7/H34He7lDxUx/cIh+dorKghPYxmGwTDkTihS4tTHihZWz5CfMBuiKpm32hAirlBTtFCIXW5nVGfb5A+pacFGB/HFIhJyEBFWEICVeawJA5zpyjw5afv71Oxz2lm5TMCWRsuXgYIlpO0xdR69v+/woJwqKrNNV94ZSqJqzSGwthcFYwE6JH/7oKe89fMZf/2u/wunpBePgKVnwhU8qb5UiRqld15OydDQul8u6Bl/laqpeE1XN7pWq1+vaNo6jxPV5XzKQIhi1sJKLdAyVWX7nQyC7msM1uaYatS80xJJRRTTojVbsKe2fY/tMgP2nYbX+LBmxczv31Quf8PMn7uDFXz/tEv0sDvmz9iHXpVz7ff7p42Hvj9vfx4HynwXsf9HL9lnbi2fxxT73acfw4Tb/z73fz3j/59nfJ73n00DpP83tixzPp73/877n83SqfNHr8HmKHV9k/yIB03JyciIMCFX1vhRMfqJxDY2zornuHM+fn/K973+Pb3/727z9ztsUozGtE5AvC6CoZxZRzpgp0VvHoms4bHtuHh5jnRMCQIg02qK08NjImfbwSJjRStPbBl8iJ7dOUFpxvhFG5zQJCFqcY8yJxdExC2fIqgjLoTGkAtFHwuQxlWXltGg4Yw1zW5ktmqZpmHWxNWKGE3MiemGvpgpizs/ROE7S9uoczolxUElXII+tBiMGS9u2ONdUs09hKoMsZM5Zbty4uWcR66pxFkKk7VoB91OWQFEZYhRg3xjD0dFxreAH3n33XXwM3L17lxtHx4QQODo6IsZECFHYXkozTRNaW5xr2O12L3Qr6HrcAnqkekwajTAFRIpBoazIiSwWLcc3DtFK8+jhQ27dusX25Qc8e6I5OzulXR1wcuMWSlnOz9c8uP8y777zHjEnXnrwMvfu3MV7Afu999y4cYN333+PV179MjfvvMQffOMb/OBHb0tVPUR8CGjXseokiVZaM05iDEUdryLhAKUC1gKsS6vqOIpO4b7LgMpcL6kC6AWtpahijIzf2Qx1r1+pCtVisT53MzCe0GUGzeMVWJ5hihOZudAi93JmUM3P7lzxn3+/AtNfLHDOkixQhBWmTTXfkRZDbSx3792jbRzLrmHRt0Bi3G24dfMmd27d4v133+PWyU3W6zXvDw/xMYK1zHrrsZSqX6g/li0xPwMvyMZUqRkJ9DJd0+CsQalCioYYCrOut7ZOTD1nKaXrhcD9/g0g3gEly78YQRl5Ds9Oz2nbjhAzYxrpnOhYynXQwmR08rPWhkXf4scR2zhyzviUq4+CJJDGFsZx4KDraYwDH7FaY11DyonzZ08xSaGnQJMLh53j5uExYbxg0WiODhpGr8AZMRPceToFXdPRtwuW7oBhG0R2JhR220Bcjpi2kfQuZSwrUk4crVoUid/5g2/w4O4thiniY2F1AAcHPed+y8VuoOSJxbMdVEYyWvGd/DZDKNXcLXJ8+y53uwXN3Zt85djySqPh1UOM6VgWzSvG4dNEPuh56UsP+L+M7/ACp2QumFiDbgTwS14RxsQ0BhnHgLredVE/Y4zmaHXAVx68wsXFGcGPbFPCmoZWKVKaKMOGe33DsjVsx8j2Ys1iecwH7z6hGDi+dwS2IydF41pu3b7H6cVAiHLOBZEFqDVOSaicoe0aYbdFYdhbrZk5c9T5nKKreaMUqXprsYAfq4QXVeImgXWaFBJ+9Ew+0TQ3WK5aDg+XGHPlm1ByxhgtxdYURRItF955+y1O7t7GOQHECnA6bhiTY9mIUXgKnjBFhuCxRvPWm29x8/iQV165g0bAhZwgxcKzzZobxWIrWIGJ+FSIPpJiwaVA03ZAoWQlABGOZOSZjlEYUdodopRmMyje/vFTFktL24LRmrSOON2ANmxTJKkDzMIxZs96nMj9kljB8ZhB0UIKpCwstVgMIY1yXyjEmFlPiSFlQqnyVrnmIrWaOBeiJUGrGuzMhcbCxwzNfeqolKLEiFUKbTQTmZAynVF0puFeWmLalgd65LRdMjYHhJJZHb2EygGnCq+9dJ8f/vAZUwjEup6kkAFDY3q2wwXWOEoqxCliG0dre0IsXO52HDASkhR3jTE407PZPGbuwjq++RJduyTmKJ4fBaZRYVzG2IyyDqsaduMOdKbrW2I0aBGvI6XM0eomU/SkyjQr2WHagtaekj0ynR/U+Twx+S1KHUg8lDOHh0sUk0gAxlkbuaDVDm00xixFroQIJpF0AlpyHkQC0Bh8kHjFaoNRDUlPcnw5E7ce0y7kXpaE1gHnWsLgRd6GwEF3l8gFkCh6pOkOUcFTSiL5LcG1uLhD6QyVZd42CwoiizFOBasdOQWmUWQAbdkSS6Sknv7ghBAHidm0peiEWE0EjMoYtyAkB2Wi5AmjPY3JlCxyKylLx5HVh/IMlQlVCuM4icazLSi9lC4RBL/JFHzMRC/zQNO1KNuToxBGMBBSQdGgVYMqGeNaKKILX3TB6o6mdWhrmeIxXd9wsb3g8fkTirIUP9J2C3R3TK9XxO1DaEztvBs56G9xtIwYlbEUpt2OxlqadsHq8Jhxd0GzrN9Pg1msuLm4RdydM14+xR1k7PKAVBTBCwiHkk6XYg9oVweMF88FcD8c6Q7u4aeENhbX9rj2LhfP3yZNGVQmqIHT+AO0XaC0xLNttySrTFaBxeGCFAz96jZKRdL0nLNnfySMcpvpD0TCZ9WtiN6zGwbGzSm5SJeZ3z7BFPA+oHSPtTdJ+YyL7QcyNo0lRYVx0vGDcmQLwUdySJRYUEqT0yTyFAmUCnz1qy/TNY958vSSUBy7kNFJk0LhfDvirGHZNhjEUPof/2f/mH/2j3vuvXSXv/Jrv8Rv/PJrqPQ7vPN24PIiMvotKjsIBZUz333zGT/4kz9is7lgtYJXX75D34mRZ9Mq+m5BiJ6cJrTqObp5QtYbwjiQJo9aHUj3RtEyh5fqfuAAAQAASURBVFJoGluJPxHbtphGHpaMpqiJrDoK5iPx3Lz5kHBEpjwx+q0AjzGTSUSlRG6Jq/H+mViFMsQs0k8F8SMKMYgJI4DOWBENIpAhimdJUTJ2FIroPatuiWlXNI3CKCesfGXIZNYXZ0zTRNt3PLj/gG3Y8Dyf4ccJq6pEZ4a26bh35z69OyVNG7JqyKWj9E7igiI5r4pJ5DJzEV3zkslkFqslXd9z9uQxMefa8dSQMugCGEMxkHXiu+9+wF9wlpfv3uav/tJfoE9bHtw54Be+9tv8G//t/y5fevlVODmke/k2v7V8nd/7g+/yta+9yo3bt0jWoIohGYO1CqczTZUJWR0d8aWf/0X0P/gnBCAqUEbjp8B7Tx7xnTe+x/e//z2mcaRQOH/2nCdvvcff+83f4nvnH/DeO4+4fO8ZSXvMYkkpUug7WK2YtMaRcaoQnaNtCotFi9bwoz/5Dq/d+SvYpuHOzZv85mt3+aNvvkW/ClxOHpxiuDynaS1GOVTKvPSlV/n9/+T/y6uv3Oe//9/773BjueB7P/wTtuffYnr4Pl/5SkvwEVfAmUKnNChhhWtt6KzFNQ06OtAGXcBagzUaa0RKR9UsLCmuPK4qySiWtJcqUiWTs8dE8UGSnuaEtTBtPGkS0kRWV5SNgpUic1Gcnm74o+FHUnAtmlQUpsDBwuG1xDZGVznDGKF2Bhs0GlvBbAvoymBXLzBzpTs6o41he7EjqERuNVuf2A5rSo4YDT5Nn/io5VTwY6LpelBxT9jLqhB0IWSg1K6DEskqS6djkK6SohRzK8w4DgQ/ybiu4VkqtXNfU4tcgIGcErZOKTNHYpZPzQBKCYZeu0SpxDKtJT+oVZDPmkmAzwGwf1Fw7/N89s9++6xp9vMc98ft4yf93Pz656yyzt+mPvqdn3rJP6lA8RNtV4WAn2xXny6u86c1dn6aos9P8yz8NNtPAqT/NIzyf1FSMV+EIf/hrRQxm+zaZq95umeE2Z7GWHKO7HYDHzx8kx+9+SPef/gBj58/ZTeNdH23n0BLThioC6GldYqDxYreOTrjWGjHwolpR8lZmOrkvf5YQVVJFUPRmpFCMYa1n8TANGeRM9GarBVFK0zXoZsGVWVr4ughK0ypVVYti74qWVr2C9LqpKqsgNZVC1bOQStVtf+qwVUuWNuILnsFcZxzkoyqvU82GOHEUyDGhLECelrrRHM7zq3jpWrPga3toc65eo9kbwLCX2kYSpJHBYxr5Rm5r9YYFosFMSUuLy8pSTTY53Ex33oxT7VVjqMT6YhqjhNri6YAy8IuzbkWDGobX2H+WdrcSgFjxSj12bNnfPDBQ9brLSFkjHYEH5kmT6sarO1YrQ44vzijW3QcH9/gzt27/N7v/XO89zjnKCi2my3dwRGL1YrDo2OGaWKWErGuwRgjAIqaC/MzGC2re4p5rzOptaoFBPbM8g93lGijyFH2n7OoCgrznMq2qQCy/OUaG6DK0GT5vlyqPneqrPU8s+IFaKtqhcKMuxZU5WqMex1QF4B9NvSZQfj6s6rnPP8ty//b1mGto2lajo+OiMGzWq64f/c21lq22x1TCGx2YgDcLRbYptk/h6YC9Ps5Y9+Oeb1wXYF1LYHifmClsi8+gZhliva8PGvz/rWVz6hqpDWfwlwg3++/UAOwapQFFbQUY2WlHWcXa7JSxFS17ZXIM6WcGaZADMKWGiaP95Fh8mgQSQijsVm0wI1VKJ1JRUy9jBEpJF8loXKRNtgcxShWG4XKmZIDy8WCvh3IyXNxuSblzGY7sh0CpWi0FZC2sRbTG1wMbAhMIWN3EWcL05jxUyYlRXe0ROcg3QUhMUTN+XqLosofIV0Q4xRIQeRowm5N07oqA2D48XunuKYjA2MYGcOa9ZgJyQANl+cTrW5osRjvef54QuuM1oY4KTGgtuYj1X5rNV0vha0YM5PP+Fj2RaJcuwhneQ+KtKkfrpb4MOGswekGH4LoXuaCQTFlhZ8yOiWG0TMNI3Zl8WNA95b+qOfifE0Ike0YeP/JBZMtVSrMyBi7RrBQQKogtzamMm7Yz8tiNCzMMF0fLK1kHdEG7P4RU8zDX8ywZGQrZdAabp6cUAoMu52Y42q9ny+ttfiUMEYg/ZwSrdEYMikF0hiJpdDplqjFeLkYi0ajUiaME6EamhYiISV260sOVgtiKgw+s9uNJLVm2S9prWM3BJpWE6MiJ00bFImINgWUJmEpOHTVDE0YSqLqWkOOga2fcAuDVo4pK/EC0FKIlgY0g85OAHUKeUzYaugZk4aosNriY2YbE8U1NMVUyQGZ9zZTkgJQlusrxt/XYpEaj1CuxS3Xi437pO86wrPvn6GUwhQmqAbcIUP35CnRDbSTRxP5si2cFsNbuiFszzDWkaJnjFtUGNmcPmEzTKjW0nZHaKeJw5oUJgEFtJP4ASgGxu2WD568z5sPf8TXb3mMcgLYpYxWHdY25CpRYs1SxmT0lBQAh3MiQ6eMdBz4aaCQJekPE85aUk2EtXGk3AAG77es188w+hjXWlmXEabfXCwtqeCMqcVhKRxT5gK6sPGNatBa5E5SBKOTMK8TlBBJaYNtOnkecqpMdZhbvIP3oAW2yFlACl27GAtF1oesyIi0i8qJ0W+wpqEg824u0BhTW9oLOz/Qmg6jBUjRupVuL6VAaZaLk0o8SIivRsFPT7DlUGRCEH1/qdcYnFtgqTI4IpROQlPQ6GIxGXKOxOSFKajm3phZ/syiSmLanmIbR9fdZBw8xmq0krkkK02KnlLXe3RD8BsxddMOtMPYTpjT2eOMg+xq54XBmBNikO6jOBWmIPr1PoHSLY3r65gSkRjjHMtbJ6Jxn2tx0Qohg5LQRrFYHQOFGKVrLyOdVTlH/DRgTWbVHdGsjmkbxeQ9pj8i54IzA353vn/0cskC+ExbIQwASjtQHY1thbySDLbriF4Yr1ppMetrCsZEjO0xTU+InpgLnV2g3RLRiS5Y04kcU/IUFEp3pBjJSSRe+sUxxhimaRDJtjxRtCMnK5I4ZUPJOzHLtQZVMqWIBITSAnL5UZjrKUUo1ZMpRdH5ViJXYa3jwSu3OL65ZD1EnjwdGXeeYBI3miV+3JJzNU4lsdsNDOMkBvYonj1+xvvvPeT8fMswBG7e6GidpV9aMIW3f/w+07Bh1Wtu3rzB3VvLOhoLKUZcYyVuKIo0RQGydKEYRVZW1rNaXC05E4uiNQZUAMSstXEJY2RNLlWf/VPhmlLzvnylvy5xnCyKPs7+LPNK++nbDCSKDE/CBy8xtgxWcsq1QzijZjnFSkwRT5mEj+JvFXzg3XffQiNeO+IZJXMMiI7+arlivAx7WYxcCiWJwa8xlqwyw7oab6rAbtxxMgZU71BKV+8Q0akOKRMroKmg+vs4nGskx7UiqVhq3jjnChqRRju93HGw2HHr5hElJQ6WC+7cusW9G7dptCNlxW4KXKZL1tNImiJx51k/P8dPQQg/WfT/U5FCwbgdePbBY8rsu1TZ1jkG/HZDZw0P7t9j8/QDUsqsDg+5df8OT54/Y2VbOgwpBlaHC8hFpHhywVmNRTyXcgZTMsfLnsZZUin4OBB8RheJrZ6cjTyfIn3eMQaPMopp8lhnuHV0zNe/+vNsT59wcueIB1+6y/HBUsyuteby/Ixv/tE3UOVLLA+XmLYhI91B1OKNVrOnjkjwlKLEcDOLH5jW8zomYytWY4xSmeil5mZXBfgisnjMuVsdm0Xtu+gaaxnrzZbObb3Pz6YQpVBUgeg59+paS5z3VfOZvZ9oJX3NKKT+EClpfxzl2s9A0zraxmGNYTcFSu1Qkdzs6vg/9vEthclPhCjvcc7t316QnA4DsyCfgiv2+hxaaS35as77+7D/fJnXfAmolb6mxz6/q/6a6z1QlSxRrh8HIjdmjBH7lM+5/cQM9s8DwP3Mgbqf2a4+lIl94S/4tBn/04Dj8pHfrt5btTk/9OpnHsb+rTMwef218gnv/fTtw2/7+LP9+PP80ND9XF/5sfv5UwJ5f5p9/jTPwk+z/SRA+E9T/PoXVRj7Igz2D2/SRgXWKihaDIpyJqSENYacEpvtmidPnvD7f/iH/PCNH+BzrNqoEmTKIiKJXWsMzjY0zrFwhnsHh7TGSk04ViA+VbZfzjida2gp+rQ+RjGRUmJe6kzDbhhk8VRglSJbQ9GKrDW26SlaTJhEUzmioiwQTluaRuPzFX/x+uUoSFTkY2SWmtLKUmIQeb36IWsMSqJVspICgBiGlKrvCLqy1nPOhBgoymKNwWpNGL2AvRWIFLkZU9vXlOhL76vMqmqk5wp0ZmlZL1empbP8iFJgrKVrDykKAVKHkRs3brBYLDBGmOtaC7BircE5ATi9nyRGp4heptb7mUgpkbGZL5iuRQhVmXlKi/Z4Qboczk49jz54xOb8nOQ9FEuOorvY9YquF9mhlAPWLFkulxjb8P4HDwE4ODhgs92x3uzAddi+5/j4htwfVfX6m0YkdnLaO1TNoAoIg9VXTfV5zIu2+fUiRdl3EAgDfA58qEFGJqYrkL5pmn1j4uwrMD9vVCBIWPCiuasywk6snQECPkdmS6BYx8p8PHk2zp0DkLrbGbi8ApPmlUMhLHpVA8yMMYpF19K2LU3T0jeOrfcsuo6TkxORqDCGMQTWux2maUmCe+zNH/fagHUE5Jz3ki4f6X5SiqLntsA53BRTo1lSKOWATrV5MGVc1ctURoOV8aNq1DaPOb3XJhSNYYOpBaarwM4Yi3OGZ0/P6JadJII5yXErLZqp2ZNTAWUYp4HgA2azY9G1qNWSrpXgXAwhqZrBkkwaJYXBGZAV7cF5hRbdW1KihMCy7zhY9lxejDw7O8c2DacXO9Y7Md3TFYxunKJtHE0BiidWXVfnC9MuEIp4DjSuRWeFjyNTSBjbMuwGunYhHTaq4L1nGMXkcLloIU40vegWK93w8NHIjeMGtObZOuPHNVut8SWTo2brd7SuwSwySQ+8eznStgatLdtRSReO0fPA2wcU1im63tB2olkfc5LnZI60aqFLU+rnhWV0sOgZp4FGiSZoyplxnCgxoQuMCYZdIOrMbprEAyTI/XPWsjhecPH8Eh8j29Gz++A55rDB1kS7ZKnGlH1PSS1OqlmPXNVBXqWX5meqMmt0zQlqOVMeL6BkVedjSbhyEf1JY6QwenBwBATGacCaDtfJfK2QYnXycd8NlUvicNlhFeQUIAj4qnojkio5EINm6Xop3qRECIm+NzgnHTCnpxe0TcOUC5djYBwjPm9I2dA3iqfnA0c3DTGIL8hhpwnZY50W3e6iyUpjUq7sQ02oayQImJA0hJp4+mQpsaCNJIq5GKaCgBCINnnwnqazFK2IqWAmKA7GWNgFYRUXpStLWq7tekoMPhPS1Vz3kS6ZUiob6kUpy3kKuv7u8pH/ws5P+BTkOo6Zw4cfkJsllBaTE19p4LHt+OE0MZ6d0nQrJr9hGk+JuztcPPmAi+0G1Rq6g5u0nQN/QfQDYYo426CVqfFKZlhf8vb7b/In7xzzczduSxEqTWQCxhqabkUIO3m2snRYieFgRClH04ikCUoKwsO4pWsduhSS34kmejXM1coQohjW+XHHs6ePObl1WFmshoJFOn9q4bcW832KwnZVihwGFFH8SnLBNYdo5fFRisvWBtBWJM1CQDHSGyEJlFzIpCo9J4SKkHZot19JoMpFqVq4Imt8msERoGhGf0m/uEkpBp+k06tpZZ1XwDRcQphET52CsQ0lTKANSlsWi5sM41a6YOqN93nAskCpTPBbpAtK1gVnGoyqkkQVvNU5kbVIhuhsSCmKAWnJKNsi5qUSyyhtyMkT/Rate7R2BL+mlEYkklQWeYIs4LYw7yH4NUYZjO1AaZztyCkQUsAZtV9blDZoe0SME7shsB2lM3HInr5zLLsFi24hACue2bng8PYd4jiQ/EROkURgW80kG2NZLI8Zt9I9RBHmZimQYmTyl+QxYw/usFwcY8wNTk/PUW5JiR5bCuMuiTEtEmvEFCF5yiTjTWtFu7yDc0cYt8CHQNP3EsP4yDBt6OlRytf1vUUZS5g25OzR7oiiG5K/ROVA61qaVgDsmAuolpQC0UeMaej6Axrn8L6SGEJCbIoaSo7EdElJUzWbzMTsxe+pXUlukBM5eTEvr0BR32iyz4BFKUujNNYY7t2/SVGZ88sNSm15/uyccSgc9UtOn3v2/pdUQkoqbC/P+OYfPuWNP+loGguIZNXRwRLVKkKKnF1ccvr0CTePF5zcWHLr5pLVqiVOooNdqk9PYy0GTZoSYRpRJUpBp6gKuhkpFlEEhFUKbS1OQdsqmsbirJFCXTJigM4nb3tyxQyyl7mAJXmdD9MeuJy1mT9tE4BO3phTJoYgoH3VYUs5gxNGPynXDuArgN9H6VqdvGdcrzl9fkZ3eLTvri5IXqaRtTrHjCoOlXUlViliFP8QrTU+BXabWQZOyEVMHlW9oHRBYpoipJiQIlRjSK1EI9tai8aK+TezhJlizpw0igScbnY0z89YtQ1TSXRNQwqB93/8FlZZNpcD6/XE+eUjtuPI9nLL8w+e8MZ3vsd2OxBTwubEFDyhceRS2Fxe8s4Pf0yWtjHRQkcA9jiN3DhYcfvWCWdP3kOjePX1L/Hg9Vf4g9/9HV699wodlpITXd8SUiXB5ESsxe+SKqkqKo4PV5L7KAFlh10kx8TkA28+WXMWAoOfSCGglfhiLK3h1uERv/DyV3jn99/na3/1q3zlq6+Qg0dpy63jYz6wT/nBO+9yuGz4yi9+iWWMMO1QZs4B5J/g0/KaFOFFlnMmRGgt7OeSC3kmo+W8zye01rQHS9J2Jzl7rt3Hdb9KSdEp1fjeWXsVb8wkIhK2cWgr3TKlzIC05P1tYxli1eI3lXRUQ2f2oPKcT8/eP/uXuJ7WzY9S27X0bYvTht0w7cF5rQQbuXpYP/75834kRIlXnZECowJUfa6Uvcp3lZK1mn1BWfLAVASnqJzHa7lmjaf3uamqebiZZw05KC05oBi1Xx2neCfIr8bULvQac3+e7SdmsH+e7WcJ1M1J8ycr+nziUfwE3/UioPVF9vsCtv2xb5kj7hc12Nl/5yd88TWsYr/76z9/7GG9uK8vdO0+x1s/fIof/l194ns++558XibzR47pczCfP2t/XwRI/7PUYP9pGOifxWD/uP3/rDXYf5r9S3t7JIadgKdZQ04kP/Ls7JRv/OE3eO+D93n2/BmbYeRguaJJpjKPNSZ62sbStw2H3YJV2++NEjurUHGgJE9Ek4vCorHKoq1FWwkEp2p4VlyDcg27JCy/3Ijhk5hYitxDBorREuRoaRRTWaGKQStLtzCQhOHprAVnGMJEms9bKeKU9guxNRbMFbu5aIW2wlTURrOwDSGIuUupYJKeweirNUVMHcn4nLCIZlpSkYQEXs6Kqau1V5q+OSeCDwSvKsAt+7NV+13mJlkEjbpiDRsjxlx1LSelQrfo0UoRvOe73/0em82Ol156iZOTEw5Xjt1uuwfCSikcHh7Sti2dnximicZavPdVEiVVGR0ZI7kUrJZ7MG+2caQcGceBROH+/Qd8/9lzhs2ENYZXvvoqL710H9su8Bm++71v07cGP42cnV+w3iV+4Rd/SSQ7vGe73bLZjjx9+pxUCk3X07WtBLVak2KsbX9yjWKsLPG5MKJVxT8TMSaGIbz44CgB1We39dk8T7oM5HrOrDi0ALs+DDXJmPXqy97wdO7yEBDfk0tAKU0sYhCVcoJcSKQKzl9j+ZWrfzMTcD/+5sBMIiNhd809uTUe0apKMOFoW7h5uMRaTQqei8ePadoF0zDx6PFjnj17jveee/fuc+f+fd59523efviQ9z94xOVuonEWikJlAUiF3VFZCtV4R9jkldFdEiUKo0LXFkylSi2wAdXUEMRzwBiNcMcyWmWskyjaqFz1DkWPXdIUMemUIDC/MF5LkcKVJD+Z1+7d5/T0jGf+lLZf8Pz0guXhksVyQQ4BZx1dp1DKk8KEMobdODKOAwrolsdiOqsKjXJo2wo71wdCSLhVj3UGIijjKBhyCBgMR03PTdvQ3T/hnTLywZP3uHPnAevdmu1uJMVAiIX1esQoR9N1pHRBGwPdyrI4dFxsthRTODo8plscsrnYoPKEagPNYmTBlpPVIVOMxHGDaReMMbGdBkzJ3GFBv+oJOeKnEXfQoG/cRR84IpldypRh4Ml2IGZDaxac7k4ZLjz3jhf8xa/dJm49zx5tGQJsvQFn5iaaq/qJEkPpprH0S1iuHJvLQkbMf9W1AKcws0SFaXnYGpbLlvX5OdMYOLl3h/8fdf/1bFu6nvdhvy+OMMMKO/XudPr0ScggAREEDRIgadMWbbFMu1SqcrywVUWVyy6WLly2y1cq/wcu39ku0yXJsiWXTEs0RUoCAZBigUgHwMFJOKFz944rzTTCl3zxjjnX6t354CBwVHXvvdeac8RvfN/7Pu/zPs87j94jjD1KZTaXZ5AgeEXQgFZsdz0qK+qqYnE8p5vmarSBaA76/lmBQgqWwpKZmJxaE8IoBcBcSYvxxDIt0ztlJgZ90dCXAkHY6xMyiEKSd+vMng88+X5AzoFHj97jCy+/wHPPzckhEcteFiqRx4xzjnEcMVpTNx6dItqIabhaena7AVeiGHtPbKmLfou1ltPTlgfvXnBrLprfj8+ecvlkReM9m5S56BOzZkmMme2jFaE75+nVwL2XMttuQ4oDP/biHYwG7QxokUpRjHINCgRUT5QyMdfIuMURT1cr6RSZLemz+Euo6b3NtsckJp31RI6Rq14oSFopatswbLeEyfS0W+24iGCdxVpDN/ScbwZWfaCLWSR3FYfix3V0O4lJ7Q2y9oWRZ+P2Q6IqSd9UZpUuHS262HlI3D9/yF1r6XVDmww/oiPfu3jImw++x+rynKqdSeGjjKSf+Ty7q8d06zW2MYThKbFqpEBdImHM0l5dMpRpfRx2vPba1/Dtln/j5/6H7DZXON/i3ZysEq5eYIcZaRwI8Qpn7mG8QZmRnKMUIjBo5akrh1ooQr8j5gHvM3FssM6DqSiqRqUr6nKF6h6zPV/z5R9ZTp4vicIgWq5RAHtlYCwd3RDY7cRbQqv3OJ2LzrYmM447hmFgtbpg6HuOTm6jymYaL1k00FeXkkdrBThyKCg1oHWP1lYAPCUdUSghTGg0SlmK9lgGFEvQmWwChRXb7RWlSJHTmBZla5GyyBuOlqf0m45QRHJw16+5feeWvMOx0JUtEaiaBd56YvAwfwHrFCn2vPPmd3nuha/gK5E5SEmkaQQ0bwRczQFyJBeIWuQwShxFNzcLozmUAMVCrtiM77I8eRFrPbvdWoqeWFKOUAasa/CVky6VHNBYZn5BTB3juCIja6F3M3LWnF2+Q101GNOQi2W7eZdldZtxuGK37qiNwpCpDTQ+4fUlRy/+GKjMGNZsNu9g3ALGTCkjIUWMgdoAxuKrihgS1jbSYUdBqZbKz1Fu8gmwDS987ic4OrqDLZmxfIu+u6Dv1+y6NTEbZrMlvppRVQuWJy/x2jf+IcY5fHvEZtuhzEjJa2H1KoWpPLeOTlBF8+id1+m3PZWy4AqmDIRdRIcdJY6sugu0qbDTe96HzOnsjnRkhI5xWFOApp6RcmG9Pufo6JRcHEpHTBVQ2qHV5AmgPCVYnLeUMBC2HfgKTCGMO4ahp7JeCuUTO3Q+n7HdaTGZLpJPOFdL0cPA6a2W42PFG99/wNMnlxRdiL2m8hXD6HF2pNBJUb9ojvHs+kBJmphhCCO/+/vfgwkI1lrxUz9xl+fuHuOdpuTEOG6xrsK7Cu8s3iviOABiCrvbXGFdNZECNFXjcN5NZI6IjxDHntnRCVVTE4cnVPOXqao5WmmC0jjN1N3x4dveqF4IHkyFznQwdBx2vaz3+/jw47YDoLIv9ElHUcpRGLJF4cJUi0+FrJJ4fWQgJeIYWI0dYz8wxJEuDcRxxA+tyGRm0Zz2OHSE1eaSr1/9Aa98+UcxaJx2zBYLzrrHmKzR2UB2uLbB7CLaWeZHC8alpzZG/LgUhCBm90MIpBsFdy1hAzEmThZzxpjoxygxxB5qn9j0ORaerracr3d883vvUFDcWjgap/mP/tN/wIu3XsTdeY4xOX7vD17D1Io3332bB48e8Pf+g3+XlCJjLnil2Ww7TuuGSGYbex6uzhmHjhwyuYgHWTd07MqIrSwvvXCff/Pf/l/y5175UTaXj3nn7e/wi7/0lwB4tLoiv+boxwFt2oNh5erqkp7EQlsaY9nimC2PmTctjXZscuaNB+8QSRRbOPMjTy7Oqb0VHfaSmRnPMmX8+YbN19/ir/9rf5uvvDxj1lq6kJjXFT/5E18hR8Vm+w0enV1x/qu/zeffXfGln96RqVFTbyHTMqOKdOOEFMlKCEH7EEBMSvNEiNMi4akEADfOcev0Fj/9N36R13/vG1NnjYxbo0Q+EhTGVSjjUVoKWXu20b6DwnnLV770FerZnDdefwNvpFgrhWqN9xVGe4pRDCXhK5HjlJDmOi+92YWn9bUJ9z6QKfs4u0hHuJtIZBfnTwhjK8C2egac/7BXLoOzQjpJ0/2Rr8jOc5FcV342aaCnxA0kHRRT55cUMlRBOoNUoWghZJgigD2liJE6lusnUyb/jkRM0o0L1+SWvTGsnp6FPhDHPnn7l8bk9AcD1+FjkO6POdYPcJgP7OTTHmRfWfmwD8m53wTT37/7m///IW6f8jY/e9wPO49P85mPPcZnfBifBPp+mv19lmP+aUip/DA+/1HFgg8wPz/iO5/1vn7Wc/qk/SuluFqv+PZr3+Xq8pLtesv52RlPHj1is7qSdnANRRcqrxj7NUZrZrWjqWacuIrKGpwW8JxSyCFKy5aCmDrcBCw732CyAM3aaKLN7Eym78Wg1HrIyjL2kbFkSoo0dYOdWNfCaFIMkya2QiZ8mdSlLXW369C5UDkvweYuk9QemQStLTqrA3BdJpbXHkAsU2C1T+qN0mRtDu2SOWcx8ppYBMZIG7zKIoswaysMCqsNqhQB+4yVZLFkYhxo25ZSJOHag4vS4ias0IyAmlqpSWfXHBa+vUxMzOkAyJcoQL1zjsrXvPTiy5yfXfD0yRlt2/DlL32J09PTg6741dWVAMvOiN6l0aQQSCkezFudcwdAPWcxPxqHXirh0/k5K9r8TVNztX1KSB3Lk4aXX3qROy+8zPz0NrEYGCLHt+5y+3jJrVt3MLbiP/yP/z51XbNerzk/P6cUMZL1Vtj9znpp3R4CKU+AdsrCclN6KqSWQ0ubUobdbncY01qb95mHAjgn8jp5kv1A7wsbwsYKYa/7LSVN+ZqmlHhgfOT0foA9xqm1PI9oZaSlOctYkjbbOAHlU+BTngXY9fv+fb3diLzkogCRHFEoKu+ZzVpOjhd0myvCmHHWsVjOWCyOcd4y9D3P3b3Ll7/0Zba7HY8ePaKkzDgMhDCSSsZYI+7u+5KtQjpIynVEm2+MPYsGZ9ElT7ICeQLEhGlhnMhGWGOwSuONQYUpISuZHCPaF5FH8oY4AXyBIOBp2bceamF+KAFRY86UPkygLtPYNeRSWK82tE3NEBPdxRVqCPRKY7TIm8znFbO6om1E0/XscsVm25NzxmjFvK3R0TJuR5QOqNQxUzVdDGKyZBWry3Nm1ZysFFfDjsurS56u16yuLmjqQlNbdnGkT0HMkLNivetxtqbWsKxaXjj25LYQZgPaAS3kMjCkNcb1zBpHzIZhY1hvd+ioxLy1rjmdnXCxecjMGjyWShVU6LG9ppnV3L99hz9499usY491jtvVnKscec7NqIGZLrgXXuFy3ZFzod8afH3EabVkjIpqm6E8+EDMopVIfukCb31tw4ttS9CKKwzjxHbbA9OlCACei7yDbe24XQF1zboYLjcd55uO+8cN80pz2XX45RGlKErsGcKa1W6Hc4ZbjebotOXJt96DQWERmZySLRkzFbYmOYaMdFUokXVomhrjPcNEsEmmIB7RwiNqnUUVaS/OsTCEEZOEQZuLJHlZQ9QFlSNGaUoaUSmyqOfMbx1RjCUkqLyj243kPas5ZYwSQ7pCEVZnbSlFS0HJKBrXcvX4gmgMi+MFtprTWMt6s+Lho0e8894O+/ItZk0lhoWNIsQB7RraecPlk8fcvXePptYwKxRXGEqH8pq6aukpPH77CSenJ1R1w8OHD3j+xeeFTZ6m1nul8FZa9Lu+J8dAnuTCrkLgZHmK1WUqNCvWlyuMBucstXeMKLK5ZoOdXW4oNoJRlBFUlhbsVCJjigwh08dMPybGIUCR0vP+qUyT3OH/+46U/b9vlnH2ny039nCoPipJ63IUo66/Mpvzl4xGjZE+ZeZZc9R31NunbMeGPl+RlejqbnthApecSclQBshRTHVzlA6lXElnggZ8XdGFkdXDHRfNBVq1uEqR8kgce7ydk/peCphWYV3DbvVIknQKJfe09RGZSFKyJjnnSXHNMPRsrxKzRUsp4mdTyiX/4tf+I955/Smndz7HT/zsf42x22J8O3laGAwjSiVCHAixx9gZlWtRzYgzhab6PGHcobPFmBrnK0LcghqAHo1ljA9RxaCKJQw9i+M7pDhMsdIxqLUwAKMlKbAuU1QvYIVpwIq0i0imRUraUZQlpYExXFE1J7T1ZJimRH5AOpcqjPX0/Tm6uoUqCUPHcZXZbTqsraiqBq0cWkshPcaOlLeYbEnFUEqiXSzou0tybnGumqTvjhjGFSGI94yJkBAgvaqWErPqQEojoRtxbgbZyzudOxq9oPQ7gtpBKbjZYjLiy6SxMHQbtPWkPJDiDmvmKHox1TQtujjGXFAmCUscw9CvuVoN9MNIVY+cba/IecvyCI6ObtGvn1DPPLYyjBEePXodayWOmfk7bFfv4u2C5uh5jn1NCCO7zRNySng7ZxgfE3NBKUPlKmAkjCs5p6Zl1tzhnTe+xkPX4KuWzdVjCtO8qizOu0OhBpu53DzFtPcpBEJRVFVDSgO57xG5n0ztjjHGYqzj5S/9DA/f+DpDtybFgfrOHbrNQ2zTYts5JtU8ffIGuhSR4fA1/XaN0kXkX0pP7e9JLKIDlcnEcQ1FCbiHBXbT5wX3zSbT9SOaEe0iOVvGbgsKvJc8Ynu5xvlKgPuk0MpgrbBlxzAIs1M7SjHkrFA2M19CTJqujyzmFXo7CulmVpNLoCRHijCOI7NakyikXKhshVUFazInRw0vPH/CnZMZRjmcV/gaYnB0uw6dEt5nbi0WbEMRqc1KYSuH0dIR65wwqb2v8VpizDCCioU4dKi0Y7k8xdc1OXWybqSAsbOPJeRlCtaJtrjakxomcLykIEbheS+TqD9yP7KzAiXT9x390DPGgB6mzrUs8/1Iwo0R4yzKGopCyCgTjXkYpCCWovh3lAK2bmiaFl8prjaJy/WaMYwwgaJaJZEsU4Y+Cynke9/5Q3xV4aua711eMOZIGaAfe6rkUHecXI42WGcPXcVMRV+t5JnmvMFkqKoKdBRWM6ItX5SAlhYj4zb1k7y0ZjZvicZgmpa/9KUXeOtqxetv/CHnlyusSfjFCWW7ZSwwFA3G8B//+/8vau9573uv0/SFvA2EEnj69AkYxTYnhpKprGWuDbdtzSsvv8Jf/6t/jZN7t0gh8cAmQneKKZbZyZLX33zAOO7YbjJYsFryyPlyQZ0C90/mvHDriPvP3UXttozDwJgz3bbDOEUaFMMqcHW2wcVC7URKJWF498EDngLrRYdbNrz49JRXX/wiLjvs5QgLg5/N2ebCN99+wHPPf46nD9/l648e8/x3vzXFArKOa61F3kRNJLUkObbzokW/J92oLPlgDoFh25FCxBRh93fdjs133uZz917gR159CRC2uqyheSoEVjhrpLMQkUksOR0MOoeQ+PZ3vsN7Tx7LdKCBJCSv4pVo42fNV37ix/kfnfzPee2b3+c73/wDXF3dIA3Ivg5d1vuuyqkYc4hk1NQFoQrt3NPOHE8vztmuImPfk1PBNh7F8JHYbSnSTZ6KYCvOOcoQD10nxmhCEVkhvT+nDEw+IlpQCNI4klO8xomKmJsqPZH6ylQ+V2Z/de8/I6MJJRNixE55+k20UquJeDV1gbD/9SdgpZ8JYP8oluqflGbzjSN+9I8Pp6E+5Gc3thsVms965JsT/qeD78sHvrc/+Pu//+zePg5Avxncf9QZfMSg/phv/Ilvf4wn80cdk3/y4/pPd/uX7VqfPHnCb/72bwrrb0wM247NakW3XqO8QXuD8YZlW2OUwlsBQr1xLJUVcH1aJHLODGNkDCNjimQlLVzKZKyWKnNSE99KSVt6cFY0WY0iGyg4yImoMkNOgCEzBVEoQo6T47e8s+rGPG2NwViFnjSchYVwo+BQrnXe9samxgrj3gCl5KkNDWmZSpMj/YHJKMnxHlDUExpftLTU6z1jUutrGQxjMAW09M0fzEeVEs1eMcRjYo2IzrwsZNM5q31h8FrLTd1YlURfcwK7jKFpavqhIQRhpH/ve9/n3r0Nt27foq5rafGebonWmrZtSaMwsJ0dxFDUmEPV2VpLiklkGab5PsXEcnGEM3C1vmK2mBNzYL3rOL96SnN6h9WDQNYVEUfXR3w95+xixZMnAqifn58ToyzmVVVNgKeYU5UkJobXUiJitpJToihxYE9JCgtSsQ+klN5X2NprI1+bg3KDKS6BgrS956mAkQ4GqTcB71wSKYsWZk7pWuPvwGAXAF4KSvHw3bQP6Nj/O01jtkxjTwDk/fkcjqne/+dhrBlN5T3WiJZ4KZkQRk5Ob0HODMPIOI4MQ491Lc7VjGNgvd1wdXnF+fk5VeVZr1aHgoy19gNm7mo/6A7j74aGoJJ2XLjRkokE21pNualSWCWkR4W0Z6I4aAlKUSIRSyKjyToDetJalEObSapEH8x3RrSRtxAFQz+SorCb86SFGabui3ZiJUksWya5JWFRKQ2+MsKQV1kYjGEQplMpVApmdmLQB1A54XOkVRaVIsSEL5oxJdbdjiFGausISToXMmXC+iJdB7FJGKMIOQMjKNGl6bue7SaynGmWrUW3A13MqNxA0owBOpVoao8rsFtdUsaAjiJRRYFWWS6GnpA3tJeXMGzZjYpsLcXXzGYzdL+lsYrjxZw8jhRXJhmkEZ0yrigyFmURzRQ1yf9MY6KkxMIZXjle0NLy5ftH/Hq84HvvnMnzV0pA7iKSSNqaieAmhrZaJbwz+JgZQmI3BLrg0QY2faakgTCO7Ppx0hKe5IK0JpnM6smKcejJRRJZ6aIIFLLIdt0gTzDNk/I+ZtCTzBBTIpBF6kVPn1PTO2aMlXFdprpSmTRcJ3rRXjypADHEKeke2O0SpXITgGdEWmaqzE7TlshGqYRR0mZukMJgU0/dHQ5iGg5gdl033L9X0dQe76a1pNI4r8DIHHKycHhTMDqjLBy1Fb029GOAFEkxULWOkBOx79BamNfCLIpiglt78Q0pUmS0VUXSBoMUDTKZPkb2JrApFwEUixjAjUm8U0zRpAjbzUhz5Egp08deElXtDt0DlTHog6+Dmgox15a6+3z0OrF7liX5bAx+HfAeVsKcqb0VH4gYufu55/jSCHfHkVUOUlBRilOV+SvG8veNkAekmKfZrLfC2ETmuJIyRadJ3zlz3eY9Ja2AxpH7gbDekXKcktkKUMQUcLmQlZ7uh8yD2kjXTsmZfrgiY0BJB56JHq0c3rUwgZ1KVdO9Mbzy6p9ntljhqwbrpg6n6ZwVk+SClgI/xnO1uqSd3UIrjXcOozWRfpqjFNbMqOvI4ihS1VsoWyhOfmc1TTsjjDtKCVNsk9hffaGQ046ktMi3TA/SFvEeKEm6t4wWESetDNaIhIgxDqwCIiolSg6TvrQlZ4fSk44QIi+4C+cyZ+kipnFFkZXES0PqmKm5+D1TcPWcPAZiCICmZIt2Ea0d1mRyXDGERGaY9GAXQBThFWUxzpFSwtoo730JYCzaOUoRuQuTsjwbFFq3FBVIaSPdEFnJ9ZVW5nvlKGlks+vZL62peJyrcT6SiqJtj2HcgfYYa6lcjZ0tMLZCurx6BhQpFZSyeFfj7S2UcsQEuV+TqYgoYg4Mu3PWVxdUlXj8aDJV25LiACGS6IluyxgiYVwzdqIC3vVrxm5DGHp82wKKcegJYSDmLcPuUuIsbZm3S7pxJeQXOxUYSxSGeVbkvMNZpLhoijw/d0qOIzFsCUmKV0POaF1AR7r1k8k/Q6NVQ4oFZYRx2tqa2HdYr4SVWiwpDjK2tEIXzRg1zmS0mmSXhkwYxHPFOkNKhWaxpPK1gKUqoa0ihUApEe/3RcNIKVEkZkzF8ekx1ngePrzk5FhhtEWbSNoFmqaCpEhBpBUwTsg0uRASeOdYLhzHRy2nyxneWZyBqtZUteYijLRzi3eWpvY0jcPV5jDjlVIIOaA0OGUE3AOUdpMP1CTJBSibKdaLvJMB5SyGRNEfgMI+sFlnsc6yX8H2hESthOGr9rnWp2CelklSEuQdjaOYoqcyMb5jIiqDSQk1xc/77jGyyEvuJSlKnGLkqaArYCBwM7YEUtdjfYU1njyx9YdRxkdReiJmyTWkmNiMPX4cME6DssKypxBCYJiklup5SykQxh6jNav1moxIdqhpvdx3Nc9nM/z8Ps5LjjWmnt3T93Da4XTFZoycnpxgrGPZ1Dw1muA02U6Fuijg5u9/8xvUTcUmjLxzec5mHGgneQ9r3QHx0mQ2uw3D2BNTpBt7Xvv13yD0A7YuzOZzVEo0dYuzDhAS0+3n7k9r5MiYRnabHcvjU07v3WJmNevLKza7jjHL2h/GwDAGhnFgDAPWWpTZy7QarLbEnNmEyOPLc373977Fi/fuEpaFi/eecFIS63VPUAY9X3BrXnhiFdXtO9z/sR/j3V/5NYmVlKz/Rgt4K93IEe88Oe47JuV8VMkM3cjqas3rr7/GjGmtzZkUEk8fP8ZcXnLSOtnv1EGsdMF5y2zeslkL0Wwvdri/r0orqqbh/udeor51jEWhJk8fNTHTrTWYoNitVjx6421uHR3zs3/x5/nyj/847XIBDwVL2Ptk7b3tlJq6RdQ1UUD+mPK/lIkhsRsiKUjhQWlFzIKjTInnh7xwk7dKlLV3jwmwJ4NN17bHQ5SS94wbXekKRYwiH4dzBx8uIf2V6f3c54L6w89lkpMNKb7/+so+Cuc6Xt4TUflk0vdnAtg/Sr7hjwOY+1gJCz4ClFYf8qn3/eyZb/0Ap/3sVw7//oS5Wz3zgf3xbwJOn4w07wfqs+D6p7mQ9wf2f2a2z3Aynxbw/qMA4ze/+2cVcP6zAvz/aZ/H5eUF3/72N/HOUisx1zEUSYq1wijwRnOrbaisx1uLNxaVwY8ZW4QEYMTrTAKqFMUYx+kDUGvU5PqNJLpdmZJPZwWcVFqCKGPRSUyjhhQgaaxi0oxlMofMqHQNfButcdZSV9VkxCkoiio3jEiZAPYbJilMS+teAzqXSQtOi3FNztcmrLLGqBug5wSiKyUtV1qAg5ttYWoqf+/1fskQgjDFtZaFZhxHrL5uiZcAv4gGnhKTnX1Muy8KlD0AUSb9vwn8LBMreT6fkXPD0A88fPiIMUYycHJ8LAHhZMyitaaqKqK2aD2Zro4jMUTS1B66NyXZ34+9WVHbtuQ0stttuXN6B+003bbn/Oqc+WbFblyj3QxXLYnZsOsT7737kNdefw3vKzbbLdZY6rrGV6I5vQdk4h5g53q2VUCcJFtQSHIYIjFOHQ1KH+7RPujNB5M9uW+HVlimJGL6ecxx0ohMN0B5WfbzxGBPU0vunkXBAcybEgQtANj++Dldn+v+WPug5H2yMHLS1z8rXIPsMDHyFc5YvDV45ykUYgz0Pbx4/z4KxerqilW/Iow9pVR459huOq6urlhv1ozjSF3XbDcbCVwnVkg+MEVvrofX6/r7AHYEdM1qMsspU8A0Aezs7eNKkaB2/z5MTFijFH0IArJnSfz2BSXxbhLGHVMQWCYtiHEMYApGy7vWdb1IN914R0sUvU9bV9i8l6WRe5hSYhzBWIWvHFoLEKRKEfAoS/Oo07DQElAzBlQIuBwx1rMJAyUkPBqcZjP29CHglWUMYgKWUyYnjZ3BOAqYqdQUuKoRkx0pWsIwsrkKVMVD7VB1YreDulSYpMlIi3lBWDv96kLucxZgL8WESYW+Hxn6iD1/io49uWiRlcmJ+6d3CeMGbzXtvGb7ZENNJKssJtE5kJMmYEno6xDoEOIoSJmZMTzXNtyuLC+eLFlUHaSC0hljnADSOSFtu3ugTcEELFbOEVJht+vpQ2I9RGKBVZ8Jux0xDMIEK2J4Zc3EJjeFzdmaMAxiFKmEcZ2yvOvaXofdewAcOMg6SbcK1+/7BOiKhvH1ULfWkKa281I0B4+FXKZ7Iqy2UpBxO44MPfSdQ6tMXVfCoJzA2lSkjbmgREc3JZS59gHRRjNrrZhGGymMMbXOztsZx3OPUXFiJYOuLc6JPI0pkcWiphh5/7RRzOsKhabEIIWrGJgftQyjFOCaRgCffAAksrD1lIaspGDnLEGJt0o1q9hsBsZpTjR5z2DSpCLdTEEpTJI1xxRNHCKlOGLJ9GGkGHnuSoEuBV1A53RYL6cp7zC/QHnfMynPVv2uoXiu/ZXUYY6Wlzxz96hhVmmGrufVL9zn5XfXLC6ueDuNMjdTuF1V/Dfnx/wzXRiKGGEbbYjjgK8MVTBS8GfyomDfHj6BO4e1XqGzQmeociLlcQKPK7Q29ONG5gOlyQp0zhjr0EbkVHKGXX9FKQbRtNZieOw9zjqUiQyjeC6oydfh1S//HM+/0jP0a7rtJVLoDJSsSSpji5I5U8ncPvZPqOojjBEGrPhtODiYGBqMq5mZE6rK012dYWhQKojGdlUxrFcCYOrJBD3pCfQtoAZS0lC8zNs5oJImxSC67ci8n6IUUb2dMaSI9Q6lxKOkpH1nkp70vh1KjdN67kRTGXl2OQ8Tm36SvNCKlEaM0lJAygltHH3sQSVQWfTuyXhT4UxF0jBQSEmM5LKL5CydcmiHdzUlJxRJCrQlgfZoL+zELEEBMUe0tjgj719I51Jg0DVKGTI1uUhBM4WeXS8gnTOGEApt5Wkaj7GZtp2jfQ+6Al2ji8XWcygGVZIUyYzEP7kUEhlj5qScSaknxSuUOSLmREhilrvebNGqxqhCRNGaE4xKpDgQ4pbOAPipQDOSzYyhu2LstuQ4Us+PpJARJfYZQke/vZT3xdSUaikm1li0rlHGEvMAwYimc1hhrZAyilLE2GHdi4zdY8ZxSx+NPGMtgGUqmdCv0caibY2tFoQwYJXH6kpkGLKm6FEM7eNE+GCSHUBB0HivQYtJoxo7ORc1aUyrTL1Y4qwTzewcZN5WiULCuSk+nGJsMXVvaWdzNBVXVwnbQM6GlEf6MdDWjpwSyUgeopyVuASIgMmWo2VN29T4iZ3rPNSVpq4sK71jsVxQe4/3DucNXllSFpmMrNIkr6JIWSS32Btd6qlIq+V91CajrCeNIkmmrRXi0+Rr9eGb/MJYMQTd4yNqmveMsaib8zOfJBEja62ZCuWgDsSUNK2regJDBRRUlJgOZ1JyFhA1BHLcey4J0aXkjMoSRzpjSFqDUTgnRY5mfoQqhX7YOyhKTB5iIKuCxhzi2r7Iz22wFJ0Zx4GUhRzWdz3WO9rZjKHricPIiy++xHtPHmOqhna2QBvN7vKCGEa8s7zwwn2Sm+PqJcpYdv0lu/MHWOtQyvBkveXzp3c4Xpxwsjhm6CIjO7S3pJDwRrEbRh48eUjd1piqZrg8Yz32VK7Ge482TsDJlBi6jsvNipwim+2KN954nV/7J7+CGga+9GOv8hM/82MU22Pt3kBaYa3l6OhIcvHQs+639JuB+fKYW/fuU3cbrrYdfRD5MmOFhBRiYIwjOSfpzt6/18ZQO8eYxAj0crXha9/8Pj/+oz/B2bzjW3/4HV46v6BuarRr+NxXfoSXmzVnVwte/PyrfOXnfoHf/OVfeR/Amqbjxa5nt+lwjWJXdsQxoIqi3/YYBbttx9XlmkcPH/HKneX0uBXkwsXVFf3QczKTwoKecn3xQLMcHS1JKXJ0eizH3sd8SDxS1RW3791lduuYSlmsrwRkzxJpWiNSL5dPz/je732dL//oT/Dcqy9z++5dqrpCIQz8vbzQgSV3zZY7hDB77DKXIvNaAudrSEIoFP+ITwJGxUMpJ3VNELsu0d1ARifpmmkNO2zTX/ckJW3dwaNL3tvrbsI9QK+m3Pbw/em6Epk4ERL2mfbN42g9mZwC5kbW+XHbDyQR86cti6He95dPey7qY/71GbcPGzMfucPyob/85ALBRxzrcF+eBdc/CJ7fgEE+7gQ/cfvTh3Jl+7Tj7s+Kb8Af1/Zn5Rz/tM/DWc28ttJ2NfaAyLjcvXNK4xyVE3aFRxYvEwp2qiinYRQAQmuy1XRDL4lHZWjmNRsrLPWkDVgtyaKRlsCehDV2ap2ajAFzEt3tUrCDIo2BYRgopdA0jQRE1kIRkAn2oLO0k7VtyzAOYDTWWfp+wHDNZLbOYb0wQPdJfMoCku735byXxWN6Y1NKAp5lYWAYayT55lobfQ/o76vQYQ+AT8kFCOAax3gwu9zLlQgTOV0D3FaCUnGTT+JYz74gIFqBamrx0wpCTsJWkAsghEhV1RhjWCyWLE6OefrkKa+98TrWWu7du8f9+/cnRpvciz37u/IVbdNycXGxd/8TZrsxlEnDPISRxWLBen2Js4bT27cIKXL/+RcI4ZSq8lRNi180uOaIuj1Bq5rf+I3fYeh7qqpmfrTAece+er0bxJAqpkSOWWR70p5ZLtVzYQWlQzV9HK4140sR+RjYBwCKECQgFJZ5oq6b97HTRbdU5H9ijBTSxFZPB1AeeXJAliRqMjA9sNsngF3Ad3Xje+VQBBHtVwlung2U9tJE++DuUARQoklujLBAnbXUzqOyIg1BZF0m9vt2s6GpG5bLJbeWJ3R9J4BRiBwvl9y/c497t+5wdXrJa9/7PiASP8rY6bnrwxykuC5OfKD4rxRaixEYFEpO5Byw3k7gutyjkEa0stN+hR2LFpaZ0Yp+vSPGLHI7JKzx01grBybLvvCRc2bP4J9CZJz1dF1PnLpAYhi5dfo86/WGzXoDZEpMGGeovMN6P3UBC4iljEERqRuP84rKZB6+t2ZUM/qkUOuR5186Yf30MSkFfMm4ujCsO/o+UfmGk9tHrL6z4WJ1SVt57jNj2A70XUTnitPnGs6ebAk5M4yJo5TJC8+gBoZ+y3y5oGkTw27kyXtn3HruiBwitU5YrZi3DrLBlIDPgWWtsE17kKvoL694e9uxCwrlK8btyKx2nHiLsoqoE7fSU3IdUcpQLi+YmUB00nNgLSgDQ0iMfWK33Wv9X89bIJ1A/ah48DTyzasrfvn3HnC2TbStZ5sCy/mSGJIwxbLIYMFUJIuR4CtmrcM6y9l2S1aw6gO7MdJ1Pc5l7txZkorm9XcvBdSwCd9qjhczxoue0kV0TCgdyVFR8OipnT2XiNXlUJDKKeGdxxg7dbYwmZjtARYmo7X9CyjA3N70V+YZeR+FJShzb45AAucNlxdnDLduoY9rmtoTwkDX71BKsVgsKDEypoRSBVdXGAxh6MkTC3g2axlDJ2zyUeHsjJQgh5EcBrAK2zh5fwr45ZJh2BL6SIpQjlqUtiI3oTLOZvpsaExFSoYuZ06rBmOFRWyz42qMVE7jvKEYSZxDSKQQ0Qhrqg9RztmI/JN3AmDui7E5BEIMhDhSfE3XD7Tecdw0zBaGMXQEDdp5sKCdRcVEHkf6bU8aAwd3wIM13L7wcR2D7+dEpW7E5fvk98bcqdh3Hci4VTHwr/61L/Plz9/h7NEaVU65O9/h33yXfP6QDrAp89xyyVd+8sf5/o/PObsKXO1GNv1AYy3l1dus1lt2Y2QbduRQBExLhu1ODDddVUlxJ2S6fssXlw0/ce+EFEeUclJsUYbKLRj6DQUBxHLYMj+6LQaCKULOLGf36XbCfndWCh37Qi+qYK1nDDtUlHkvZJljra04vX2f84uHqMnYNJXMvH2RMUb6YWAIgdNb91HaIYaICWvnZGcY+guG8YI+y5rnbYVVNZVfYvXIGCL90LF+95xbdz+PczOZ00ti2OmpDV3RzmbsdllA95JQeiNGxRgpZOVIoaXrLgBFU8/pukDtDTnt2GwfkkJiPnseqyvAiiFp6DFmJiCyGmjaFzAuY10khg2r9SNmizvMmtsoPH42o+vOCMMFcau5WPccLSxzCzmPjJtEqcQ8WZslyyPLdv2YGDaU8IiuD2hlcL6haWegYdwMci060ViHUhbnPPPZCUN3CVGRUyQMYiRu/THWaIy2rNZXKAyX6xVnl5dYbbl9epe28ugS2Fw+RleeRdNCUxPylnp2h36MhJSoXBTZAxATQNOgkqLkHRSN0Xe4fPot6vYIX83w7pgUe8YyYK1hdvocrR8pSToirHWkUqGqAkRit2K43OLNLarmGDu7xXvv/A5eeek0rT1EkexoZqfU1ZJHZ2+QiqJuHe2sppQdi+UpKUsRyLNk7FcM8QKjDLeOXqRvE3Q94ziwvrpksbAU3aGdwqsGvKN2EzFlLLSLE8IYyGUgxvcoqSGnJXFM9CXSLF8mr98lR5lP68aBkrwihUBbZWo3J2oB7OvS0laDmIenEWsdYRjZXa0JQ49zGtd4ATAtdP0l1mmMbjDaUojEMRK6gVIUL375Fc7f/B2sLVhTcCrTVrAboWjDUePpUMznmlnrmbUL5gvH+dVA10XiECg0OO+pakNdGT53bymkCWWIRZFjYRw3+KZitmzJKdNvI2EMbNOK+UmDw5DDhhITVTMDJUUZbwyzes6gYRgvKWNm1r6ELol9J8wHtwl8VQKwF62wxZBLkq4a58U0loPK9Efs59nd6smYXjo/ijEwRghJiEwZMfbOkTAE9h5XKSfGfmT93rvEoT/sLm3OUfMG4xuM0yz9jBISxXqO7rzC0Z3nuNXUrLdr3nrzu4fvlZxJIUARfy2kkRhnayKaISf0GOlWK3LYofKIIuOU4nRxxHmImLbmf/+//d/w7/0n/wl6eZ+T576ASYFf/Qf/F1bnO46Olvz3/rt/i//z//X/weXqe6SYyePArZfnQiaIHVW2PH7Ucev+i0Tb8nj1Oj/yxVO6bSQpw/FLL/O9B2/wwukJOmYevveQtTGMfY9bthwdzXnrbEPlPLvVjm9/4w/ZrTe8sDhhfHzBP/v+P+a3v/uH5NUF1hW+/KUvUN2a0bStPIOcGVYrvvWtP+DW0RHHsxmhBLq+Zzk/5ktf/HHu37/Dr/+L38TOHnM8X7BNgTu3TolPnrLrRogVixKx1hOtwQ0wxshcWyptiUVzHka++u03uVyt+Ye/8l9SHdf893/xl/gLP/+L/N2//T/h4T//D1hWM5avvMqtOy9jJ1a4kIYiZ5dXnJ09ZvfOe7z97TdZ1C2P1YyzJ5ekMXD14JzGe8615/x+4LmXnqdOPTZbtHUob6XYpLXIlMSI14oklBu0ybx47x6/8Nd/iZ/5mT9PmTTJ0z7fy5rQd/zh177GmBJf/sIXuHfnHuPTCuIkbWYkRolj4OrijH/8j/4BJcL2X//X+fEX7nFbKZS2E4tdlPpdVVOUxF0oKBPucS1Xm3EWTk9n/NzP/Dj/6Je/C0hM5usa1HCgkRzeQMUE2gvxyRiPNkZwAzVxz0ueuCISH+cDG35ihUxKAIVCnyMhJ+qpOL4nVYxxFEKHArWXFE3lRrHg+s8IRMrkrXFNpDAT0G+twzmPLooKI51ZnzCV/EsqEfNJ24eD2n+am7T43JzgbwTpn/pUf5Br+rQLy0fds5s//+Hf13JjrP9Z2f7sjefPtv3Lfv6fdjMF6qKYNTPaZUXjPLWvsIDPol2mY8ba68Ugp8wwDIQxHQxLIZGdp3jRtVPeEbW03hpjYd6i0cSUCTERQmG72TGbtzSVwzlLGgZhjE9ku9lszjgMB222bbfDtzNpk1SKcQzEGCYmrYAhIQnzCK1F03QMIlvhHE4Ju8poI21exggoGOIExE6JvPcioeHsZIwpBouuqsRAbQ+kGo3Ke7NKCeb2La572YqbDPW9HApcg4jOV6LxOi16IYg+rTCyMjqpg9HkzbYqOdFJmifE9wGSzjmatqGqa3zbcHR0RD8MrK5WfPd73+XJ0yd8/pXPc3p6KgY+JR1YjSVljo6PUGpy/LaOi4tzAfQnB/A9ML3ZrDi/vORnf/rH+O63vsN7D87Y7tZUswtO771A5glD0Djf0rQNs/lM2PZaI0vuxHYxbmIMlQNwMoTxMFXmKfhKUzu81pohiExIzkn0xLOeGAGTDNC+20AjrAs9scqFniMO9IWJtR5JJRBCPADowtQXoLyUSBzDxHrbz7cTA6oIwCFB0vtDhZwFRIZr7PIwUe9B9em7+zGeJ51Aq50w75wUIoZhIPYjTVVjK4P2MpZeeOFlUkxcXJzzN//W3+Sf/uqvkGLieHlEUYrzi3OEIZF56XOf45vf+R5i8iXsVSaAfR+r7YsE+mDIsz/tiVWeQSFaxNZZcspoKya9pSRUiHL+TiRtireEGBjGkaEPPD1bE9OkP58LOk33pux5rOqZY066fdaJnBMwaxucEVBnO5mfSicL7E1RjZYCVD8MVN6TxhGNoqXBWkUaEyVB1oWzqx7cmrGqqFXFqanZ9JHYjTSN58HZlhALdVNxfLzgydmKbbcllYg2Ff1uQwr7Yw9sLiJHTUtrNaXveW6ueNLtUFWhri1Bgz864lEYOLsccWqO63qCiRgd+dyy5lEHp63mTq1ZKE0qHT2FoDJFZ+b3WmorBVLtd+gIlVV4p6m8Y9FqihaOiC4QimKIFq2lWGNi5ixEHj3pOV/tuNZWvWadKAVd2LAZCl/+3BGvPD/nd7+74l98a8Q1t3ForBWdzIvzHmyhsZZlW3F7Ybha7whe2K2VrzldVLz4XMXdU09bHbFY1Ay58NbDHd9/J2NtRbtosV66L/4H/7NfYH40p6odzhYuLgP/4d/757z7zhXKqPeRgjRTK24RuScmhk3O150UIgElZtkp7ceYxkxJQy6in64QbfGi3i8vFcaEaY5YHM9ZLKvpFdYcH9+Se9V1WAvNxGKyZJFTcRY7zdvr9QqTAiiDMmI85l1F5S3eZlIYkLqqSNecbXbMW8fM1cywuNozRGH8qqRZXz0h+SOqukJ7y2bT051doBuHsppxSIzsMNQoK6ZU4zigtBMj1zyQU0FrkRzZdiPKLyhI4XCIg7ARUSgjc8b5ek1VORKZPo8sjuY8Wl8QCmgn76jolss8ZqpC0iItM47hxji7+ef13/S0ft6YJmVmKPvPTeW2qSthamVh7hvuzo9ZjBXh649RuSb7ivl8Qe1rXIbZ7Jg2aH7kvcLrRxXLo4axybz7+BH/2r/6F1hWBtWPbHeBi6sNqy5yftXzW7//fboxE/oBXSdO5kuef/ELvLgs3H1JJEqsTwxxRRwKBotRUQD3onF6TqIixDUpjJAM1l5S9ABFkVM1sXeliBs6xXy5xJiWFINoKvc9zs9IpdDHyLy9w7a7lGJJLGz6N6BU5GykG6N2osuuFQVLTldsd++RIlgz46R9mdfe+VWMqqn8Eb52pGKwfk5VL7BO03eJfneG1oWmEU1ppQIUTQgVi6MZ4yDzb9N4NrskJpHaoC2k0jNvj8klEvKKo6MjShzIMeL0DFt5qsaQCQxxAGNxvkUrg0K8QhQj2tZou0CFkdPTJcO45uLsHUpSIjmRLJQTjk+PeP6e5uLqivXmUvw6VKHgxVRYid77/Oh5mReKYtd9D2NELu1ifcWibmjaBkrFMG5Yrx5T1wsG7SnFUtctSheMShSVOb98gq9bxLQ9ima2V9TNLZ6v79LUoIgYZTCq4qXPvULKA2HYksYdM1sRxwtm7gjdLOnjgM6ZMA6ElHG+pjDSVs+Dcuz6gPeO0PfEMeGbGuM0xyfPM/QdTx+/iRoLSg94B1XVsN28x+rikm6zJXQDp7fvs0vvYncPaXaiEx8RQAoypfHUfkEKsA4rbp1+hVnbMOwuGfsdXidu3/5pnp6/w9XuEdW8JcbIbH6Lpj4mFs1ut8E4xayZc1q/QIgrvD0lJ7i4eIjzNe1iibMC6G/GHlVnTG4osRUjXWVQuZBCx/m7vyfnhkJlLf4lZoZRGm880VrM7DliCOR+xawtoGaYcIUPEecrxhgpHhKGodvhKy1FC+s4vnWPYYjkpCY5E4gJnFNYrfCqMN76Iid3PZRCt9mwunidbZfYbhKXF4kvfu42mCSSSKrmaOE5Ws5Zb9acPXnMbK4xuse6GfX8hBwzMSZc5VjULdtuh3U1ymmCEh8YPMznLXVdE0PG6EwIPTGOZCIxD1grY/Ps8QPq5TFazSjZ0Pc91vcUAh+2KVWIoZM1cz8H54kQlBLj5M1UJlb7J8k57Cdro0R+TBsjklsxUmI8AHNFCcmpJIlNlTEooykp0e+2xDCI7OW0uQxXTx/TbR317YaoC7qeoX2DrRrGMDKMHcN2jR6HCTjcryGyXlpfUbct9WxO5RuWyxNKGdltr9hsC6u1mNxTCsPQ8cKd+5SUee2N1/l3/96/x9lmQzSPeetbf8DV06f8+At3cC8+TzKGf/zL/4Sr1QUhjGijmZ/OcfaYsD0jjjuMb/j9P/wt7jz5PkYZdL7ihds/wlvdQ3qdOL59i+Y9TZ8gxMR6GIVgkzPzdsYXX36JF+6c8J//xu/xX/3Ob/BbX/saf/ff/Lf4Z3/wn/O9N9/k4dWK5156iSEtWA2R73z/Nb79H3+Lv/W3/gbjakUpmXMGjnB03Y44RvphpOu3aAXeOvqu4/TWHY6WRzR1xeZK8dZ776KLYl5ZNqXjvYuOW63D1Y5YAqbybHaRPhVOasmNX3vzdTbdDuUUeZP4vW9/l222PLjY8uQ3f4sH655bQ8OFPeJqECkmpz3OVZxtdvzB736NzdN3ePfB28zbmuX9HyFbTbGG6mSB0Za7d27zhefv09QNYb3D5AgpYOPEnk6FHCWHVlgpcseeYT3y27/9W/zqr/5T3vjLf5m//Qu/SEzhACjrKW8oIfLg4QMePnjE00ePmY0jptxgZN8ITGIIU7TH1NGZyTlMxKMCBWbHC37+r/0Vfvq//os8Pj9n/fSCW/fuoowWaTdneOnl57h955hlpYlhZC/wstltJbd4FoYsU1Bkwey9ypD8N6trgFspIUpoJT52eTI8ZdKFVwey+Z7GJNlAmt6/a9lV5J1WWkhRZX995bC/nDMpZrxySFerdPuVIsc2RjHZz0lXxthD/niI/QeSiPm0P392exb4+yNJeXzsT39AcPHGTj96D8+AzJ84Z08MO/UsOL1PBNX7/v1J+/nYa/vALtQU2T+zuHwoYf6j9vss9+bjjvdZNjmvT/ukfphj55O2j5Qm+iMWmH7Qa/isx/3jBNf/JJ/DJ221ddxtF1TO460TMx0lbHVyOeiBGyUMc9FyTQQK2VmKnf5zmiEloi4kESilTFp4RWsSoqMdKWSl0MbhKwF8cpFOQGv0oUpbOw8gIF0urK+kHW4Pnuacp3ZJ+byeWM7CAhMtvZILeQy4STde5AdkUdlrscMepFFYaw6yIsDhT+ussB+mZ2SN5eAFrva66AATiAPEMjHQ1b6p/TrYE5ayBJh6z3Lfr1MTSJQntppx/n1j4yBzMgGJaWJW39xiFMaLNobKWfmzqpgv5ty5c4e2bXn48CEX5xd87uWXcNYy9AO5FNG8yxMTVIuJq7UWa+1Bt7vrOoahI0Zhgf/+176JMjVVfczVeoARtrtIVbfUzYyCwliDdxZrLGMUtuuBxchEbpwq7eTMmIIwvouMpbTXP5+ed5qq8SIZdC0Boyb2tQDseQLRxWQ0pYl9XiCmJIn3/l5P0kOHf08Ae857GZggOrt5cmVXkLMw3ynlEFgdNjUZ55aJfT3JhRwAIfafL+xx5ZyFib93nM8pU/uKWdvS1A1PHz4+3POjkzm3Tm/z6PETvK04Ojrl8ZPHnJwc0e16ul1HN/TM2hlNI5qjkcTex4kb53FtyvP+OWlfsLmWtdufqhS69QSS77XR9wWmanrflCqkHOjjQDdEdmMiFDOJdAjPUcdyCExvjt+bW0nS2VJZw2zWSMeChrrylJLpu55SMpWfWkIPmsDyPLpe2B9GaYw29ENEWy062EajMXhjsUYzqihyNkYzaMNV1oxKM28q5k3FcuZ5/M5bLA0smorW2qlgISNZkamYMbcVM2uY2cLCG8adIkRLGCz9dsNy/nnO0iXd9inbsqUtcJETKkdhuKqa7ZBRIbABFk3BOYX3Ml/cmtfMao2zk2cEBasV1lnq2lF5cyj4KDS1tjRJJDFyLrhGobNlHOHyakpIb8ZXpTBvKn70c6f8lZ++zaL1nC4UY6nY0fLPv/mYbPxUENNUdcWQern/StE4x0UauNp0jDFysemwKvHFF0/50itHWCJ1W/PwsufJZZQCpqs5urVgcdSSh8zRvGZ5Z04za2isZ3akaGYVyuSps2SS9OJ63jyUaZSaWlv33Q9TL0oRXXV5/ybZmAmkZzLZzVOqoiZQ4SBBoxR96Ej5CKWkXV8pfehE0sYQUphY34qYI6kgBm9aWMjeWYyTElUoGuPEi0GrQi6aq8vI8WnGWJHo6pJibtzhXRt2W/AtJYkmM8rivZWaZSk0tSdqhLWYAsk76uJxRhiKmkLIe0KKvLfWOoiDyFnkjNrr6gPGGtIgsh65SFHbWHPoGEqpoIumampyjowxYYvBG8naQs4ob0UfOhVSTIek77qYs189p/8VjcrpA7Hxs6GydLvIX4o2hLEw9JHQD9y96Cg2cZUzj9sjbo0RHwZ8HPCl5ysbxVNfKHNHM7d889tnqPgizdxS28yi1SznmjEU+jFzZzHn7//qH9CNA96IDvTJ8Yzn73ju3z3BmEIII0rLWpmjaKGHyYxXa4T5jMhgKevAVGg9AU1ZE0PCTSrDeioIG6NR2hJTjUqDjOSSSCmjjBgjKwO1VxS0aBiHQAg9Q79FeY/SXuZidjAOkDQpDwzjU2q/JGfRTh2GEZ0zTk9jRTmMG4lDIoYEGkK3FV1s6yYNcUihm3KzGmePQGVSCgxDR1aZtnKTz00l4DyaXHpC2jJrF4ihZIIY0NqLBEuJpDyitZMW/5TJKmIrjVIVDiXsfGUYQ08u0tHRdWssrSTwzuKcYbV9IvYSzKUoTI/WDoohJ0U7u400RmZUGMl5IIZJIsNWOC9SNzmPjMOO7W4nZAszgQbVEavVxSS5V6NxVG6ak8g00z9ykg7HkmUuiKUnBnkXjFtgrcWYSMkwmoSr20mX3RH8lpIUqmRqq0lqhnOGUhTkxNh1Iu+SCo2fE/MlSlu0sZQ8oEkC8utCcZrN6pJmbrBKUcZ4kAcpWSQU1ThQqhnFVJRk6Ldn9P2WVAzKzQj9BU8evEY/rtE5oVNg3p5SN6dY07JbPcIojyJPspGB7bYjuslST4kHhMpTIb3yzPwMpQo5BWLfk5KM/YwilYR3TqRToqwXVX1KKXGKJTMxBrrufPr3yGozTN14AwqFURatI9ZoirWoukG7hjFExrGjZUaOamq0URg3Q427qUtRo/xI5QyKhKIwX1pmi5eJCVKy5OR4+uS7NPURzlbiu+E0OQ40XnF6ssBVS5Ta4ZylJDFSnS3nOFeD8riSKGOEUsihEKPH6RFSJA492jrWoUcrkXqMSQhMMWSGvsOoSIoD1i+wfobW6rAufthWisxbe+JJSZFkCok9yURkwQ6kok+LNuyDzIxI7onu1o3pfpJFzBINyuqspjg43YhJZevHQEiRWmXq2DKMHbkoMcnNgRy1dKOOPXHo3nd9pWSMkXhPIXIaYRzpdh0QGcfAdrshpoBxDdbPiHFLMgbtPLZqWcUZj67eY+i3k0xc4O254t7yiMoWnj49Y9EuufPyl2iOT3j04HX+8l/+q7zx7W9y9t7bzBrF5XZNN24wxjKfe7RV+EoMVm8vWk5Pjzg+OWWz2fFuEY8ni2K72vHN77zD88eO20dzrtZb3nn4iH/0X/wyjx68wfnqkgD8Kz/xMr/5a79PiT2pBH7z61/HzhwPL865d/827z5+RN9GMh0mjqQ+UrTFao0no7IUd+vK0zYNKWa+8Orn2W4HLi9W7LrA3/lf/9tcnD3i7Tff5Jt/8A1KcoQySudl1ZDQzJctePH3GteBzXrHo4cPsb7irYdPGYaEvbujzyPDXoatZCiRpvbcfekl5lUhPX2dxmqOTo54p6mxSmGtzMtZibSnhAzSNamVBmuk47xM+Vua5MqMpUwyomM/iNb8as0+Xs3T2N7n5hmRAUspSO6WinT5Tx12eRq3Vgs3W+1lM/fD/wDGy8/zMPLgtTd5++wp22Hg85//ArX3kBNmArrbWY1zkx+Ur3HW46zFW1BKZBR5FmSXV4a6qjD6WgpKCHvAQdpOT74xU+Q85cGUKdaF6/db38A6b+AbeiJZKYD0jFTUdNw4kQqrvVzk4Vdy4nYyrVUarBNt+0/afiCJmGe3myDbQYv3mT/hswF/H7bPD3zmAz/5owB9N4Llj6NVf2Ce/vDzUoc/r9ks14H5ZzmbZ4/1Sdd4DfwI8nUDTVef7tif4kR+4O2ZlOR9u/848PaHPXZ+kO3TFJg+zTUc2Lyf8Xuf9nz+qNtnuWef9hr+KMf4qK02llv1bJrkJ9g4Tc2A5VqiIyGtP2MpBArBcAjgszVkbxgCBJ2JGmTh2hvRiZ5gLFPhU4FWZgpWIadCVlkWiJInsNsyxoAzlpADu64TNGVyEE9ZdAbNpKm+vw/GWshZTGtiPARwRk06omYyGbleP0RXFYNSRvSSJ2AxTcCes5aiFGEC/mSxmooQe5MxJQtZzHkCaDJFqWuznkkvfb9vOfgzz5PpeeYbJnvPyHXsjVYFOM6HgsBBK1srchIdfB0COtgD+FxVFXfv3cNaw5uvv8nV1RVHywXz+ZxhGNnLxOyZIze1wuUZJmlZ7Xt2u80EEGu++/3vc+v4Fs4vcFWH83O0rnBVSzs/knNRGW3FrFPtgZwbjup5zwCfFvy4L3RMQUJJ6XCP9kD5HiwX/XpQSh/0BvfYzT6Ij6FMjHKpZKSUKXuAvdxop8vXOuw5yb9LjqIBObXiX9+fJOHK9HwPAPuUQIjchJxbLuXgIyDXeGMO09c658aYSX9ZPATquuJoueT46JjdasN2u8EYTdOIkeXbr73HYr7k9u07PHnyhKquGIeRi4srhmFAM7ne11oAlxsBk5ru+3RKItekNNenWW58XsbxzeW3UCZGw7UGtnNirGaVJk2Frm4c2YZEFyEpK5raRYxISfkA7O5jnf193BeT9lJBRiuOj5dcbbdorfDeoYwhRjFRdtYQUsZohTUaazUpF/phEFaKNgIiqUylvJx7VrS1p3EOZwyDCiidp64TTZ9Fomc+GT3PG8/V6jF3nJ0KOpaBiNWI5ruCmZ9RG0NtNJWR7guXFBRDSpbhLOBemFPGLX0X6bVoE4ZxJMVAozTZFzYhsksRrwraOpYOKiNmzsvW0VQGbxXOKDBZiqFGY6u9UdwEDxuF8QaVIyEkuiHjGo0aFGMsrNaDJL9GHeIbVeB0MeNLL53yr/zIPWLMKJX5vJqzLi2/8rtvg1PTHK9w3hHGcACpFYoxJa42HZtuYD30WA0v3DniSy/eI4UeZS2XW8hJE3PGF8XxrTnL4xnjJtAZC7tAwBGsJWQvxUi1l2B6f8+DyAlxuG551d8fL16rU04/34Pr3JgvSqGoKWk7/ELWLQH+sjCTpzEbYqBQsNYwRNGSV6VAlAQ25wRFkXXCOTG0LEU00KcRPiWDmdVlYHHk5UwVbEa4re0070WGoaPyjXTuhCjGbs4QkoCuzlpS5cRDIEZ0XWGLmyTFtPA/SxbjsOk+aG0OSUxWk/HX9O4bbUlEytRVFWLCWCn6FqRIkYsY3BISIWcclTCgSiZk6Y7K5drDQ+675n1VtcMz2idx12rr5eZvb8Y7N+tBWtMPie1mZNyONOue0Y1sYuJdV6O6DXUYMMOORb/hbhioPLiqQdU1u9WKzdWOuQFsxFlFZTWVUyxbw2lzyv/3136fEALGWsZ+wDvNctZyslhK0T+J5JV2ldwxFVBZ5L6UyaRxJxrtppI539SisKeipOEFSpb511qJYYyXArnRLTZq8iTJkRIYZcXs0Fgqr3C6IoUdPSOljKTYk8wCg8iOpbRD5QI5kRjph3Mqt5COwihdfE5nwApkUQzaTKBXyYxhIIQdKltp7deGcVRQdgKM5szJyS1i6iipZxw6sJbss7C6TUVOHeLPIL4N3hly0ZCT6PUrNRWPsgDsdoqfkHXXVoaSHc5prKvAWDarx1NRLTMMO/S0xnsr7PUUR2LoSbbC+ZrMiGjfazJQN0u0kU410dvvxBRbGbS3WDcHggDvqWe3A58nCTdtcc2CYXxM5R2V95ANTaUpJRJzxBkLypFIxBSIqeCdluKBrkRqzdRoI50klfPEorF2jrYzSk64qpCGDlUiddUQ8xxVxIxuGNbkmBjDGoWmccd0yaBVhdIIw9lVOKMp3mC1YnPV0c4XqKIpqWAqkQsoyk3zv9yPoipAM+yeyr31NcbVhAIXT99CG4WrKvKwo10ssG5JwZDTgDU1KY8SNw5bum4kBSHXFO1BSYEgF/E9qe0ciialEV0gRjU9p0xBuqDGMZFTRy4j2h2R4qWYmGdE7qu7nHyTFNvdDm8K2haMM5hsKHkU0qdxYs5rK1KIpDFibaQgEpZaG7SuIK+kEKshE7E6EoPM93VjqWd30cbgXEPdLPid33qLZbvA2YpSRmJMmCzj3MwXKDcTW49syDFRgHbWYmxNTBavC1olQhikizNpjFGUnAhjxivNOIwigeGsxDpaMQ4DwzDgfSIPPfWsiDSfz5Rs2fvZfHCT+DjvY99YJj+Rw4J4kFv8tAD73u/ksJ4adXMC5zo83/sZqYNWtXQ2flDnvY8RnYGgp4LfQNZecr2xIxlZ11MKhKG/sWIUIKO1nQqymTiOJJ3YbQ1Ki3Fx1+8oOVE1Lc3iDrtNYt31jKlgqxmjWrAZIsNmgxoDzdEtHl+ssQmWzrO+vKKqTrhz72WO7r3A1eqCVz73CpunK7r1wKzN1NWbDFHMyGvXkNFk16CywmvD7dNj2lkr8lDWYJTiZQrz7Y63v/MW7U++SOUrmqoixcTvf+MbkLcEpagXC77w6vP8xj/5LXIa0Drx2oP3sL9nOL59zAsv3OPpw6cMORJzxiSFGhL4hq7bsb664EjNMVOcLYWZwquff4Wzsw1NfcT9517i3/gf/0/57h9+i9/+9V/n3TffY7XaYZV0UnpjwWcWyznaW2Ztw7jJ9N3A+fk5RSve2+yoosQMvm2IUw5XiqxLs9px74UXGcxIfvsIh2Z2tKCp/RTbAUry2yGECaMQEglKEVWZ1sdMiIF+t6P4zNXVlqdXhW5IByJV2XdEc52/7OeOvYFu2XP4SpnyvENiCQijez/OFByIdzcQHAAB2N98i69/69tE4Pl7z8v7fyNndM6gcZAzlatwVrxTmsqg1O6D4Pr+i6jJJ+06793/SiBLwT7EN6vsX4kprL3+Qk6SCz8bmym1z/0kvt77b1GmuWK6YFUUMQjAXnzNTeqvUsj6bQzOyrtorPlU+NWnAtg/CQz7gObph/z5Sd/7NPu8PiF+CKDvszu58fePu3Gf4qaqZ/6Ur322E/4g/PxRn/oI8JUPjuebdh+f+mz+eDDcD939s/foj2XsPLN9GqD3swDHn+YaPs3PftCCwh/lM5/m2J/ld3/czHanFPP9MSbwNu/dPvesuSxO9nhDLDBkGGIWU0oj/yVV0IuWnANjjhAT8zgtUgoIUVrNlaYU0VDPckiyUcRSsF4YgRpp1cpZQCOnDcvZnNXVim6zw1ce462weZU+yFlkCs4a3B6YG8NUtVUTa7EQowDJezdvpcSoUSkBSfcsba31+wHGCfQPKYoW6qRNHtOkO1wmNUMt4I7VBmu0GLaEhMr5IMKQUhKQ3hhiihPYzHR/EO1bBCQzk3b0TYmYwtS+lQvWOdGPKwWlFRYtjrOTftpqdQVwkIxp2oYUI6+++nlWqxVf/d2vcnpyyqtf+AK3Tm9ReU8MjpwyagL4c0xsdh3b7Y7VasXxyRFjlOQhhsTs6BbvPTpHo7h9+gLt8pjZySnFWFJRNN6LuVYKjCGglUjC7HWS9yzQOLHSD/dATfq/08LvrEFRSGHEIM/tAEwU6bSgFFK5oZU+gdmiP3cNr+XJMEyC/SwSQTGIsdQEBImBapL/JuD9ZuAhoPok8zIxtfZBTpmAf0kamAK0iaUznec+KNq/frNZg1JMx07Ml0tmiznGGrrdljH0+ErMAy/PLzBa/h5j4Orqghdf+BLvvPUmZ0/PWK823Lpzi77v8N5iNLz91hswAYFlP9bKzSLT+zskbsYfWqsDGL8HOFPam90GcipU1rJsG9SYSTGRcmE9btmGyJA1GS+GYwdWhSHGYXr/JI0WjcBJQ9HK8x9LEeka4Pj0mKEkQj9AylStp6RCGHaEcSBjMDqzPJozm9Vcrrf0fUBrYW/EvuPF5044WbY03kCKnC0dRnlKyjhTSHmgDAkXMgsXmc08rhRqZ5gtZjw/O2LcjuxCYlMyx5VnrjWXypCM4ejkmLDdSDKXR37vjXdEb7c2DKZw0WuerK643G3pU8Y1DZfrjXSdoGiPWvRuzZglkS/W0ikNYyKjuLtssU5N8kiapqnE+G7qutjuBvoQmTciPSLFBnDWUxWoc6Fu4YlWRK/YbbspTDKH4amU4ZUX77KYz7jaDvjsMFVNWyvuHhfqeibz0KHQlfC+QoXEGDKXVx3n6w1Pr7Zsu5GkFPeXMyo9wzMnmZqYBx4/3PLw4VoYRyFy57kT5sdzXvvOQ84f9PDmbpLiypDh6cWWXLRISO4LkVrGb20dxu67g/YOp5KACUtd7X2wBLLRHLonyjQvywiXFUMY6rKPnBMhwfHxCdY5hjDSDz3WlOn90qQcUdoSgoCBKibqWY1zlpwi4zAQh0RRBm0rrJsRYy9gVoiMu5HNdk2kpWhNzIW3H6158d4xJY30/UBVW6yr6IY1w7jh1skRYwkMu5383jlK1ZCzdCioAVQlCVOKiZDEx2K1XZNyYTFryWHEFikqGe3Y5SJsWyUJZ9vM2PYDY0yAoWQIJUix2spcfXG5ZiCjq4rF8phue8YYA7EU4hAnppJG1HuEhS2srw/fSv64uP06sp8GKxjNpotcbRJ5UJz1W+pd5iIWXusygy6ckFlvd5y/+YjZODLqE4opMGpMMFycr4ndiA0Zr53orOZITIFx0MQQySmx6zrCEFlv5gy9J46ZMWpq78BWKF3jWsu4ewpaQDBcwjODvZEug9zLImwx5xNKzyllC0rA25yGKbm3GFNjZjVPnr7BMHQoDHfnDfNZS4qRcQiYKtAPO1SONN5RipLCnw4ooOt7ilaT9GoBDCkFMTWPEWeTrKfKkLUjK8ijxWmDcyOwxTfHYEXv1hrHrluJXJeyhGjQpTB2a0LciaxMfYekRDKpsjBsE9FlTDVn6e+iVMDoIub2ThGigHxGOWEsl46UI9ZWGNeiUhZze63RyhK1YrG4jdEeimbTren7Fc4ZFJn15Rkns3soa1HKUrLB6Iq9CZsymlB6xBjWUjUtcEQYO8ZxYNz1UFpSdjJFusLRcU1KijAGhmGDtY7T0xO8V9SVQWVo6gUxRRg79qbP1lqs8YxjjzId86MjZosThu0ThqEnlxpnZ1g/I12BdUcUW7Ebz1lUNckUskooZ2nUKSE4Qkx4Y2lmx/TdY8K4ZgxPMcaBsqQcSHGgbu9h3AqbIkU75jPPdh3pbaCpNd7NMM6hzQxjWrQXVnTJ55QScU5hdEPKgRwG6uaYVHaElBlCZrN6jPb3sWpEGYdpK8poyWFHCjtiv8WaOaF4VLZUtUV7h65nYDQpjgxpSwjyVntb4bSSWE87TLUkB0DvwFiUqYg5CEiOA1VwDmKfiEHMEl1VaEtFphBSYegV682a2jc0dYvKPTkmqqpCVw5FYcgW6wzGaiHoBIupK4w3xDQwjjtyGPDOMJ+dEkpGYSgkhjDyU3/uv0WOVwzDiu3mku1FYnF6xDgkVmc76nZHKQ0xJOKwoammWMx6mrpGBUdzdJf16iHdxes0NmJMA9ZQtCLmwtF8zvn5FWdnK557/jm2u3N2fU/KiVk1p1jPMOyIaUfUlsZ+UToTPmLz3hFjoO87ijfsDYflDSl0fQ+lCPu1fBD8fmZalpg77//LDDFNc7QS+dAp3xGWsXzJWUdykSwy7ahrri0g63TO0I2R89UGazVZVaQEl6snUG5RtTWoQorvl8MpQMwRTyGTheCRIjZKAS6EILG50jjfMGuWdJfv8L3vvUlGk0rFe48fcHt2D6ojqsbxU7/4V/nd//e/z9Wjxzw1hdWqI3FGFwbmx3dQFP7h/+3/RJ80UXtCbEhDYbPeyTxc4Kd/7Cd5UCo2w0B894rnbx/x9Mk5w3bN87dPOGor/he7S0wf+KerC1Yv/Bxv/+YfcH65ZVZV/Pxf+ov8+r/4XZRR3L73AmfxmHMFfQ6YOGCBeT3n+dt3WR63rN94yHfWT4lakZUh95EqRb72jW/Qbdf89R/9Evdf/SK/vdvx6OFjnLM0zREvv3DMX/mFV/i3/ld/l2/8/hP+3Fd+idPZC7z3zhN+9Zf/AVVdY6zjyeaKhbakmLBKcVRVXLY9q92WcQx0mx2t1lQ2cfv4hFdf+hJ+IjKgJB9wBaqSJZZKEVu1YAxJK6IGry2hH4ljokwdQWNM+AKxKHabLaUPDF3P1WrFg7ff4c6XK/7pr3yVr377Cbvj54lx0jdR+yLPzc5iiUksWrr2TSYQpaNDKaxRwpRnT2jToLnRBb3PkQRfKAoyiaIKpnL4WUV3teH3fue3+ct/7iehfIlu6Gm1RktKT0gJxKNb2OxpSig/7NXLhZJgNwwsophW5zIR06ZiFiVLfpeSFLKtOwRPSl3DsV3fM44js5Zr8tVBy106BLRSaFUOhQgFk8+c3JdhGOl2O1x1hCrXnkZFCTlNSBtCILPuhwiw/0lIQHwmRusnfOxGEeRjPvPp5Ul4htH+Q8H3P9X24Ue5ZsP/UbZPvgppNVaf7WJv3qtn7hufeMQ/+e3T3Mc/aeD4s26f5Rr+JCVdPksh4gfZDIWmCPOsqEyZZDa2YWBMiawMxRgihVnTshs6roZOgImcsUoSLaMsY7cWLWmlCCWzU1kAcCXMAm0n7RilDmzmMh2XkolZo8weDBgAxKhEIy3ZR0dcbdYMQ0/dNigjutWmGHTJOOdIMaGVaDCbSV96L7nhnWU77AQczBGrrCRBCGOlcvUEqgrrNIQgjGJr5VwnoF0rAXV3fY9xVgAS1EE7VsxNJgZknBjP5XpO3cu65Jzleo00l6UJyPTWESft95AilfYHwNcYAcGM0igrLHsUB4ZiTAlrjBjSpQC6cHx0TFPX0s6WEs6L9m+7bPnZX/iLfPW3fotvfvfrnJ6f8srnXoEUOVoucdYRhsD51RljP+Kc497z98A4Hrz1LperNUM/4L2nObnFYjZnOV+IDEAUI7fae55//j5Xlxd0XUffj2ISW/I1TFIiJU2GpCVN1yhIWMliWkmSBVq09JJo6GsBYGNKhCzMsClSEo316R1RWsm+JxaOMC8j+/bRjGha5hylaBCjgPFlkqIoWQD2MrHgJ0ZTOYD4cGDf7AE9iujDTq2vB839ydg35EwhMZ/NWMxbjpcLzp8+xjtLSppVN5DDwOXlOTvnqW2FNpovfOELOGfZbFaixdvvGIcebQtFfZmQMkkpTC16k7futpAL/dDjKjsVD5IkOzzLVmdiBl8Xnfes8pRFsiKQMJOkhdidDkDEWqjrhjFDVDDkwqYf2Y4K1KRXHRMpjMI4K5CKOmj96RsgvzHvL4ZlZZkvl9y7fw9QhJzo+oHUjxQ30tQN/ZjodgO5KOYWWrfkeFahYibNAzFPbBElDKbN+Y5khb1eK8Nm6CWwLYl33nqPp4/PIIyclApvDEmBqaSo9/3XL7FOdOadVwxhRCmLKsIQjyGxHgTEcl4x1secK804ZIYywvKUdx4+ptaRn3h5xu254fi5Y+aNofGKUiJtc0RdOSqvqbRCq4BxMnZ0GvFK4byXjhAK2ojPhNIGikFn6aNJJYlZaz0jhpGcIyonhgRfemnB6+9eyRif5KD2ibAymq984Q4vPlfhTAQsrprT5ExdRYYkxc2c5Z2wWuGNlRqSFhb0EDPdGNj2AzFnRl+x6we2Q8BYhUW6NazSGCyr3Qo/U3hfOHvvKXm5YP2oJyFyDzoVNusNYRzBWvA36TOKnEYp0E4ttxqNnopqk23TgSAhedDEwmYC6tUeWNAoJuqSnpICa2md4fz8ivHlOb5uaLym362mNUP2V3lLnyKZgq01uhjEsNLhrPyu0AJ68r5QmFKhtaXRhi99yWEylDGTdeHiQgBXV1cUFKaCuAtszi7Zbc5ZVJqqnVOcx2JoZ56nZ1ckNTGOYketarSrUdqjtWGMPW1dk1NhXPe0ixklyboUQ2EkYawmjpmryy3P3WvYXe5IQLOsUCS6qNiNmSF1wmBzFTL0NLthI3IXBWJOOCNAaVaZosthXrmmW8lctF8Opp6w6/imPOvOMD3Hwz5kzH3nnce89/SKR+9d8LeWc155sOFl7dnee46XUuTkwuLDCDHj6zm/+9aGb72zYZcND7cd/r0t/XjOw/NzFq0jBEPfR3a7ga7vWI9RtPsnGYXf+Z3XePu7D/neVy753/3Sz7Fdb4hlC9rhqiVWe0oJojFaDCn3GORZW+MZwxatDNrOplGXUMaSc2EcoWnmDCOEVIhlZBy3qGJoqxZvDaZkYj9gjGUxb0ljT1EBpTIGQz8CppuKSoqkRMbNASEFVleP6cOA1mYCZFts5WQ9jB1Oa7zXrDcbYuiZtS3aWbSajK1Thy0RkjDyK1s4O/sWSs8xdoZtPNotmNUzShkYhy3Kz6nsVBxLO1K0aCtgWskZWzKb7SO0NvhmhmuPGHdrIRwoRUoDw7hBaYe2Hu0MxrYMw44QBrypqI6eI8eROPYYtyRrRYoiv+UbKFSk3JNTTwqBOK5pZico11AwgKMb5L1WypFTAC0xwNhDJDCb1bTNXJiTOqOKxRiPNTNcBSFHKBlvHCgYd5diTm8dThugYhxWxLDF2pbaVBQMQ4KxO0fVFUWNlBwxzMgukiYjcTXCFunKtMZirScNW1p/DP6InCPbzVNiEnIAaHztmc1njEZ8GbxWhIuR2EX6qFjevo+tJvZ5KSg7cvvkVWLq6YcVJdekYUUad4SxQw0DioDKQST4zJzV+pKFtjTtHPQRdqYxI4xDIWVD2yi0qdHGoY1itvwClS3E2HO5uqSpCtpVqKIwKqHMLXzdoVVEFdilgDEZ50VacrfqGbon+ErTzFrG7hKlWnkmKbK56ij1SFW3eFMzdO/R1pBjx249UrUNmjgxtDOubnAuyryTFDEPhNJTxp44ZHZjpK4rlKtI2rDbFvABbZYyh5dIzB3OLLFaUeI5SQdy9qLv3BjiILJqRllMZbi8WKGr3aFjIpIZd+8Rs6VqblOGgNGJGCIxFWZNzRgDi+MTlqd30SbRE2hti1Ya653kcAS0yqgBMFfA+MHkbz+X7jUPtcjjxCQdVyUVhlHitT3b99NuJYuZcwyBoJMUK8o1YhRTlBxNA1nuh7PSoRYKKC2dyOUGoH//hedpZw2b3YrtZseweyoNkJVn2R4x9j1hHPbY6WE9KVOHaQgBry3eiHTR1eNHIsdHIUfxDlJFSWdK1bBbPRYPMd8CA1lpVqsL1Gqg8RDnhn7lAceLn7vF7dMTnnv+JbSt+OpXv8nOFYZhyxiuWF1Ynv/SF+j+8DusNx1KGX7jt77O1TigTOHs2HPi7uGtp50tGLJlUVn+00eXbIfI21Vg+C//KU8fnDH0PaUU/rNf/i+IqfD5z7/CF19+gW987Q957uXn+Mm/+Of52Z//C/zI//P/R91WzJuG50/v8LO/9Iu8/Z/9QzZDD66wXHjWXebFl17mJ3/qJ1mrwh/8yi/z+mtvM4wDu67ntW99jz//F36Kumr4v/8f/z+8u71kMVswDh2Vv83f+G//dd55412uLq4Yh4HLyxUPHzwBVQh54NaipXhH3488eHJGW7fEErh3ccF6/ZTzXSe5hYZQoOtHLtdrdque1SYx9pfMjjekIaCzdMIrpdl0PU8uV5SJFKhyoULxkm8m0LzgK8/dV19Euac8enzJG28/wtUzju/MGPod3/vud/l3/g//Dn/nL7X4WgpslAypUDJYa/CNxEk5qykmuSYgaaVxvkJ7J0mPNhPYLMGKnox+QWRmK2vxykIRKwLhEGhsXR0IR9op0IXV+ooYAlppdnu5PHU9pvfbnvhXlCKGkTgO5CL+N3nquA5JpE7dJPOK3gPj076yMFCePH7M5sU1d07uAJNggFYUPcmbZXUdsnEzfpvyee0Yh4Gu6zC3kCLBAUgWaUvrpHBuFPjKH6QGP277oUjE/DC2Hx5gvE9DPn5/n0kt5Y8NJH5m1H3sOV0f9fp0PuRMPg43v7n/T2I8wwfA9Q/Byz943Jsf+DMGRH/c9sNmgf9Z+fyzn/mTLA58Wtb/H2kzsq5kJUzymCNJQ1ai3Jg12KZiPXQMMaAmGQuUwfsK5xwhCei5t+DU1lG0tOvrvfSMXIWA8FVFyQJsA6QUCTFjEJBZT+xea62YHGWYNy19GBlDIITAolkcgGW1r6Ki0BNNscBBIiYjFdo96xnAOS/A+FSJvqk7vZdU2Vep1eQ0rpXGGnN4HlrraaUUoDBP9OWShTForRGNt7x38X6/cYgS+vDh/siSNd0jK/qTe5mY/XdijBNrUx8q58Bk/CeAaAxivlm0AJUxarR1AmPkhJuM94qBk9Mj2VdKPHjvXWIIPK5qmrplMZ9zcnqLzWbDdrPjnXcfMCTYbHsomtl8gdaaum0x3hOna/CTbrt1jrEfGPqecRDWu9Jg9N64ddL61Qa7v28pCVMh7bXQhfGipkI+k5xC2d/3lIg5HkwJrxXxb8y9CqnG50TKwlgXeRgxRUwxkLJI4OxNS/es81wSe/qAQqr60kmw13Dfg3ccCin7cXBYL4q0fmu1ZzCKWecLzz9HXXkuzp9Scub5+/fIqbDZvIZSEMZA7Wtu377N51/5HGEMdLuOcRiZLxYs5nOquub2rVt8//vf5/z8nJQSVeW5c/cupSS2mw2b7Y6ry9VkXHMNMpa9Fv40bq6B9WfnuUlnvux13JW0hXpLiUiAlRVjiGy7gX5M9DERkpq0L8FqyNZQYrpO6tAoNT1fQeyF/HFg/yusMnRdx9nTM+ZtRdd1hDBCSWjlGIaRfhgYQkQrR9NWNM7iVaHEkcYpUAZrLLWfURnDwkBjCs4UqgS1s8RiKCVRm4y+3RKiI+vEEBSkgkfjKkuyGe0M2UFShaAgIDJaWiXmPkJTaGpoWzhqFngrAW+mUM1mNN4xrw1tbWgqRasUTheMTqQcqI3oamutRGKgVNdskgR6YviWvJdXUgcZo5iE3c8k70DJFJXYdh2lFJx1ovGoHBpz0JxlMtgVsDpw73TJ0dyRyxpN5o23n9AezWmrWuY8c+M1kSkXpTTKWBk7k1RXTGEKri1GS5cSReO8p+sV206KgskptmEkrDIXDy6xnST4KIWunMxpo+hhFkR2AjUJcpUiCbnaa62LfNX1i/l+L4Hr9vTrWfd9o30/1xSEZZwh6cy8qfBWk2NgM+41eZWwoArkIUgBSkMpmpjCNJYn7eHsCUkKVhSFtwWrIqvzNRePL3np1dvsNjsUDl17PvfCKcbY6RIUKSoUPVWjMXqG0pZxjChj8cYR8kjV1CJlVTLaieyGRgrnGQ1J/EYmBFwYSYaDvNa6U4yjFCOt12QUu/UGtGK28AzjKDrzWgoI1hci0rY9xgj1+w224iSxJUC5Zt+F9b7tEL5Pc6tWB3Dkg5+7JvaUfb6iFbauMZUnonhz2dCfj8yiogkDdrVBh4gtBVs0Z7XjogysyCQNymaZm4ImDYnzbWBIhZiFDRuQJNkY6a22VqN0oYsjq34gF4P1FSRIWTP2Har200kqFKM8fyVdEqVYQhD9ZKUUMQaMcVLgiCN96AnZy7VqhXNyndo4tCrXXT85ktJACFtUCaLlj7THZ8JBk0/8aDqiL9JJNg7E0EOS4pFzFVp7wrDFV0u0rghhyxB7tNJUvqHETCgBa2QdGMMaTRLTMq1Q2qBYoie9f0NFDBtGJZ4dFAG25F0UsC3ngVJEns/qGqUDvmkEhDSakg1KRRRiKJ6KrBl7AM/6dioagi4ioVOZhlQi2TC1xivAUBJ02ysBYXVEqURR0EdDHiI2SVdBDD0xiFlpUzcMYYsqmlzAaSjFyxqlMkYpMbTTQEnEsKMk8VIxSroP0SK3QlbkBLkMGFVjbTP53FhyDsQ8kksW0NxUpBTIBbyvACNEk5KE4KEVauoEyTlj3d50uWBUwYUIYUdBkzOE2ElRoioYFVmfX9F1I8Yo6trT9xdUymJcI6QVP2eMW0pKqKIZw0qeXQ4w9oQ+cvriV9hcPWJ79YRSImHY0m2cGByrRLJW3nlVYb3CVRI/a+Px9TGqJFIaKSVRVQvieC5zivFoW0PpKCmQFFDE6LxQo2MiqUzwO4yt0bqgSDTNDOePSVnRjyMhZ7QawGQyI1kbNEZicmfE/DwPMu9ZRy4ao+wUgGisqRiNB5XkHXASk2vjsdZhfUVQXogXcSQUWYvGOJJyj6sqTnxFCImQMto2YvCbg0glGAtlYBh6lNKylpSEdbWswWWSDstqmkMyQwiIXH6GEiagz4KRwnLZk0tKJqsCyjD3zeQL8uFbTuIfoo0hTQ45ZfKASlPncVIaRTrMtx+63WBl5jxJZFpDDjKuSypTSF2kEF4kYzMa4kRcslqLnGRVkUomxfGw9vX9IIVzrUhhIsCUIsxclRjHxDiGyU5G8sGyX99zlmLa1PFcuNF9d+PEU4qMY49GuoOzsmBrTMlcnL1Hv7vCqMIf/vZvcn5xTkwO6y3dEFCj4eHb77Ides7PH/DSiy9AdsSYiTlgU0LvYwoSQ38FOOl6G6aCuPXcPbnHj/7U5/j6r/8yy4nQM44DZ4/PGYbhkG/IuDEcHy159ZWX+d2vfpUvLha83C65Ux/zt/87f5O3NhtqY+g3Hd02ABJPzuczfvQrr/LVr36T86dnvPX629y7d4u3V1suup4hRmKB8ycPqaufR5uKX/61f4I9mlHVDQpFiJqrh4/43NEx1b179Ebx7sOHXKzXrNdrNsOIpzlIkJWSuXNU8bgPPFq/y1e//V+RFSg7+WRl8WUiJ/HsampM2ZHztX+bSSJLWZQipsTqyRlLWyBH6lL46dsv44zD2pG6qlgul5S8ZT6fcf/ObRYvv8r68QPOreZyfcVv/9Zv8nd+6b+B1laKq2YCoJ0RebNiRK6UfCBpuOkzIvk1UEJGAJMpHy0FcZoS+VpZG6SD3d4A6Pcxqd6TBaYEKJPRhoklPr3rwn76wKtXCuSYGLrE2BbBKJAuTlkTkbXIiP+ZAcYQudZ2kRywJMXqakO37ZAo7Gb8K2QoYyacRGmJdSeGvMpTvKdhGEf6XXfAMfbzgkoiJ+iriqoR+RhrzLVs6sdsP3SA/YcFpH32/dxEIz4Lev7s9/l4kPozbB+/G/WJn3gW0C5FfeQ92beK7DHxDyYB6lNe0/TyUK5fjAOg9uwnb/QBqAnAQb3/sj7kEj/q6fzpjZ1Pt69Ps88fVDf+2c9/mmv4uM//kQyE/xieww+yv0+8Jq3IRhNKJlEIpcjfrUaaAgUALkbT9x2xZJEucRIY+KrCWkfsOqnWTouMNpqkp6R9epn27GG5FrkeM1UwS5YERhYefZCqMMbIumMzvqrwlRcpiYkNcVPCpUwAbd6HgTeS9JIl8K20gOYC3hm0Fu13rTUxXr9Ve3Bf/rEHTyedNX0tS5NSElZeKRNDdrrWSUPcWistZUoCzv39v7kP0SQFkJYy7ythOZXCejccPq/Utc73vhhQioDNWcl/wvJ6vwHoMPSoUsguUlknYM8kvxFD5Hh5xDgGxnHk8dWTA4OwaVru3L7D7dt3SCg2Xc+jJ2eErHC+pqkbZrOWlLPIQ2hNSBGdC9ZavPcYY1ivVvR9TwiJnECVLCx1roEsrWTBLaowjAllJNRIkzbrNZmxHK55v+Ui13sNsO+TjQn5Q1Em8DGXiSk/FTv2302Tqep+rByOk/fSPOV6/B7OZT+mr1td5SnenLLLwY/ETA6jejLNbJuKxXyG0VraVBW0bUspouNcJiNRYyy+8ty5c4e333qbrusYx0Bd1ahjRdu2LJdL3njzDUoWhlddVxwdL1mtVoQY2e12XK3Wh6IE7ztbuahP6jLaJ14o6Qhx1uD89D5GiDHRDyNdPzCERCyKgoNJoEkrsFpLi+oEvpX9ntX1Mz78aA+wG0MIgfV6PWn8BVJKB5mgMEZCjFP3hj8UvXIMdF2HVgXvNZU3NHWFUlB7Q20K5ECjoLZWmBololHMfM2QHJuxxwyGHAVU915TzSzOW5wVCQaVNEXvKBScLrx0u2LbKyoPi9Yy8xWNBTFHyzTzlsWsZlZZamfQNkMolBzlv6LRqRCjaEnKTCcFsIwE22nMoNON+KKQR+m8SDETYsJ6YQoqCikFNpsBULSNxznHOEAMUNQzyXdRlDRy66hl0TpSN6BN4q0HlzxvPO3R4nqQX39F3hOtYTL53DOCSs5gjTBXpmKL5P6eTRfZ7IKwa+qaLga23cj66Ro3KGKMYvxbe4YYKVHuRslJWk3Zz4dyK3KZTKgOoddeFuqjA6hnV9VrfwdhvjHJ0SRVqJzFKDl+3/f8/9n7s19Zsiy9E/vt0cx8OsMdY8rMyrmysgYWBZHsJiF2NyiSIgE9NKQnAQIkQK/Sk14aetCDAP0P6idB0NRQC5QAgi0JbFQ3B3GqYk2srBwiY77zmdzdpj3pYW33cyIyIisyM6qKFNoSN+Oec93Nzc32sNa3vvV9bbM6kih00YQwolypcmuaXBIxa9GFRlGyI6VCmMUXot1oQj+wv95xc7UjzhvyHMgGXNvwpUcbtDZ1XBTGPuPchFs4fOsYZ4WyYBsx4g79Dt+uiHGWrjFTMNrWfaZepzaSRCpwXoy5Vb1fRkNMUKJ0RTSNJRVQRUBMARGTAE1anqX3miHMpJKZY8DEhDrsz1rX9fYug/HT8oxa8Dj8+ljwqw/3Yw/qToyMDCatNL5t6LqWRWe5XDpulg2bfcAPO9L1FUFnvNVoZ7k8abApcmIKxhraraHpHEYXHpwuub4eyWXGWFWLCSKNIxbmGVv12b3RtJ0FLL5ZomNhDpEwDoL76KYKLswo5WtxUqQWRHscYUbPM9poYijHAsw4ZbwXw1jrZRxqbY85wiGJjUlkQIwOKOVrQpxQOpMiqCxSfyUMzEUxh5k5zCjlhL2uTO3KE/mP4qQYklMgzCOuaTHGkuexFgkDuQTGac/CW3KZUFlBtli3Qakk60DW5NwzFwHvjt4ItfepIEUAVVJlxRtQGdssUGSMMoQYBZwnUSpbWvbiut4UQ857AIwypDIj0nYHPxPxAZKVojCPvZiTWikUF6WZYqHMQXzblCHNE0ZpvPM0vgE9QaqgoYasPFqL0aVWIjmhsBJjpJmiFV55MJqia0u9qoXtksh5Fh1749DOkeZISL1IVyiFUQaKEikswDpNiYqiLQWN1QospBjIKVIUWNsRU5C5oDXGLUBb0BOl7CW+0KaC14jE1TDSLRts64lhwrhRiC1YlPKM4zVkLetbGUFZVEmoPEPR+MU5btih1QsOXYhzvyPNAaVnfNNh3QJlnHTq2IJSHms7vF8J4zKLnIdznmkyVfpYgGqjJU4rWQx8jbPkpGutwdJ2Toxuc6SkiPeeZnlKzMA4CZkjFZlzWuP0RmIrBVZL7pEy8p21pygrBWJ1xw/H2trPJEVI5xzWL7DOY40llspuzgEpH0IuM4VI0y5wxrLtB0IOaJ0o7KuEIRQjoHUMM9F4vPZAQqkOVTIK2XdTNihjsEbINc47qNKFZE0pt0XLkBRkiTNQkJUU5dQn9/g7R87l+F1DkPlDzmLIm0QKNB/X7c+HC+W6XiujUXPNy3JGHQgBFblTRTox4BBvCnBurSF/wgCx7wdySSyXlhTzsYB7IHqqPOPUzHKpmAZDqudTRWKrVFnqaEPWkttqazFGfFRQEOPEsL+BlARQVBa0pcSZ3c1zUgpY63jnB99nu9tL0cQ45qjodyOX25dc3Fywmybm+T4pKzBW5mxKqHzQp890LZTiUMbhva0EKUPbbXjw8MuMuTDX/NwYzbAffqLgbIziZL3i9ceP+J0w8pXzE9Yh07/c8Zu/9mvM3/8hw/aGcdfz9OkFKYnR5GrR8Y1feouLF69wRnFzs+P+wwfYzQnKvapFEMXNzSXb6y3GXPH9d37IG1/+Euir+pAVw9U163tnnJ6d0lvFXBJjlnntrWW4krwvRJH+bG1BW8Wr6xf8wR//juxkpgKwSWLFqd8zzyPZFKzSqDhjShLfilogsMaSU+b62TM2j89QKcA80QzQGcP64X2+/OYjnALUyGtvduzKhrzx2CxZST8NfPj+uxh3dNrhIDmqrRGDaGU+7q2lYB4jKc0Yk1kuPPfOzsgJFssOVfPOA4u+ZNl7ZK9QR1wDDviFPvoPUPPVjGjay0tr917dO8sn518RIt9uO7DZuCq7KyQuXeeXqnKrunZo6pzu5Fd1VpfCNE7EED8eZglbhUSpAPsnMdDDdUkOMM8z4zjc/q6UKp8r816K+R5qMe0Lk4j5tOOzQLgDWPSLAnSf6/2fSDTKx376GT7r0072GR/3Kd/4My/uM//liFkfrvouGn3nzD8BGnzGCaGyLO6G/z/JtPnY28vh/z55UvXx//6UD/2kyM7xG38CbPjJ41OAeH4+EPZTr+vnOM8XJWXyRYz/n+f9nwUwfZH34u7xsxYAfp7jTwLNklaM3rCLswAYFSApWpOytLRqa7i+2aGdhdp2ZIxFYT4+fkvdMACltBAoaxIeU+IwV8Mc6fcDm/WGeRApmMa3orlZZTxyFvMZq6vpo9E1KdT4psEYw83NDV3X3dGyVoRpRiHAvGsbnHU1sQNtrBiV1o1uypMEfc5Vg0eOoHnO+XheMXLLx6Q/RgnmN5sN19fXt6aMxtQ9KUniXIoAfHfA8btj6qivrtVRC1hrxetvvMHZeoM1mt//N9+XJKMGVgdm/UFm5gjSw9HMdJ5nMWd1nqwi+76n3+2xxnC63uCcIw5ZaiHG0LkFFy8+4vr6mhAj5w/uk3KmH2fee/KMy5uejz56yjQF2rbl7HTDcrHCGGGh21JZBeXALOcIsJdSeP78OW3bcnBon2eR3zl8p5xF3sfW9vMpiFb7wXCWY+Hi1pCJcidYUdJhcIvHFFK6NcsEKlNK2PHzPAPlaBAr8jG1VTSXjxUxbg1xbpOCwzgWTfcqDVN3irsgvFIH3TpqAWdCa816s+Eb3/wmL18+5+nTJxitefzoMc+ePuGjJ08lCfSeKQQe3n+IUYYf/vCH9NtdHUvyvZ1znJ+fk3Nm3/csVyvOTk5EvqXGQWGaGfqRfT+wG4aDt03dQOuF3pnDn7be3G3tdUrLHyOAaWMcsxIzt2EY2e575piF+Vk0xktyoEqhxMO9vfUqECY7tSgl15LumAkrpdBO2M6LrpXOmCyMYhDwPakkQLKVQtXldkfbwn6wPL3Y0hnN6bpFBbh6cYFdOnpnaY3GG8PJwtG5VoqCcRJAJCUW1nCvXRPxUGYWjaEpI48frGi8Z9lYVo2lNw3/8Ps7TM4sNfztv/QVhn6LVeCdZpwrM+dQDExgXCRMA+NWAP3dOB7XT6s0cz8SciIirGwBvCThc60njSNN57HeHGU3YogYZD4PYcA7DwmmaWJmpt8VMTxVoBl5dZ253AV0tyCPe46ZatUPv79sOV8u2JeC9YEn1x+yupdYnd92YVAO6USNlYR2z5QSxsg4scYQS6FtLFplSp4pKOaouNxdcd3vaNuWR790Blqx3fb024m0fSHXojXGe0zbkICsgSjs0WOSoDUKT4iZlBG5mnLLo8nl0NF0m5BzyBVqwZXKjL9bPTgWMrMAV7v9RJgThobNsiXFwhQihUJrDdpqYpWTku5gUyVkxM+hMWs655mHgeuLG966/xV+8L0PsSbz1W++xjsf9Dw+8TS+pWkW3Fd70d5XiaQCz54P3Hst07ULiJ4PfnzNo6+eUZxDkyljongjYHeaICm61ansXdXfoGsa9lOgpMSi6dhdBkhZzACXhXHMnJ8uaBpHRhFD4PU3NqSUGWLG+kaMTokoNaPNmjzLGm2dYbePZCvyYM474iwdRod97FgTOjyEwzOoY6rkcgzjDwW4n4y7D8+w3D7IlGhM4rV7BkPig1ODKyPLlzds84gNEWs8atVx/ZUTflVt+PbC0K0bxhvLj+eR9mHDr3/7jJfXO168kIIepdA6JXWjCgBpqzg7W6EznJ/ewzYeozrQPansgEQKAes8xjm8l8Kp1R6lNLHMuNawu94zDhMhZfpppLPSQt11p6SiaZoOayyowjjOKC/PJMWCKQWtO7RWxBKY5hlrFgKEWotVlj5cVFDaEE3LvHsJaKxpcd2GmKS7YByvSfM17foBIV6R4jWNd5huzRQmpnmmMQ5bFJSZnAM5GpLZMI43FAbaFs6aFQldTSdHYenmUVrVVcK7U2y2hNQzpRu0aiSKLIlSArFEUA6thNk/Di9p3AJVDDEK476wB7VClYY4B4rq0crJ3pBapjninMU3HSVFUujrXmxQReNdpJSJFDNz0MT5ktaeY7BkIoulYdmdCGCfZhq/IqeZECJz1jS+RalQZdIyKgs5hZxRJeGbFdoYMuI3ZErBOluLZAFdDPN8Q9INxnhgYru7oGlOaZsNWgf2/UusX+PcgpImULOscUqhtBS5SxaGszUNuqzI+YJCQpuGoqHrXqNNmd68JMx7rJ2AQJknGq2I05bcQbNosOYEnCHlyNT3lAghXKOyjJXVyTlhHohWoRvDarHm1fMfkOdrvBVpNasXxDgwjTdMU8/5g8egNc5oWrcilYL1JxjnSXEAbcmphRJAX+LX91BpJsc94/CK+w++Rj9uSWFGl4ChsBu3KOVpu3ts2lNSuqlG9IUSd2jXoYtFpxanBpQbMa7BNhva7h7D8IJp7JnniTwPWNuI348xtO0ZY9pBRgqEaaBtRTSoZJhmcN0JXedF5i9knG0xRUEJwjgni46+1midiGnk5HSNc3vS+BGTHVFJWM0h9FJyzxMhKHYlYZtAGDIle0qxOJPQHpH4Mpa4v6lFOpHbLLkwxVEKABjGuGLVRjFczzAPA2Eeq7fNpx85ZazxeOuZwiTxdvW/MlodiTw/Ae591lGBb139tIxryPuenG/jUqMUuhSRhqykx5QTcwgknSHO9X4ezlmI80hQidwsyCFUEF9XE2fLvW5Ltx5QTvGDdxfstJJGvpCY91vJ3YI8E5zGKM16teJks+LHP34bimbYbRn2A94V9mrCmgXOwTYndO14zSkxaoNynhgDau5ZPD7l+fYFN9sr+qEnl8yPvv8D1qcPRc7n3pnIONYcSKP5xtff4qOXe7I2vPWlR5gyk8bIR+9/yB987xWm83yvcbi2YX22ge//+M6+J/dxs1pwcrJhtdrgjOGt1YoX77zP7/3xB3zrm99mfrpFq0BKgd/+oz9gmGZWnWeF4uEc+Z/+D/97bINmTIbzs/t847u/zN+f/19cP32BcxPbfs9v/Rf/Bav1Q9Znj1mf3mOetlxePOX9t/+Iv/7vfYUX/ZYffu8pN88vWW1O+c1vfI0HDx/QLJf8H//zv8er6yvmGNHAD997SW5bprhnf9WjAhjnKEiRfzsE3vujHzLsnnH19AnffeMBdvuEhwx8db3gRedYmJZOW5gmnn3wQx6efpN1HLh6/pT//X/293mkI3/7b/9H/OW//OtcP3mf++e/w2/+d2ZOnyT+3v/zH/D0aqKZE20l1LYlSwdeGClDzzSKXFPrffVjA2sUphhS1vzgB08Y3cS9heav/IVfIXdrhrnnO7/+ddpVh6ndi2LsXqBorPHiI2K9yDFyALh1nZvuKIdqnKE14iGSipDSjER3nzoHS4H3nj/n5PQ+OdWYV4mMKapIZ3Yp6JKk4NAYtANcQTvxu1MFmk7jm0OHfhKJmFwoMZNKxKCPJrNy/eVOpGwoBcZxoB96wQamQ/4t0dux076qADhtKvXqpx8/N8D+b5cO9U8CxT8JWctrPh3K/nzHF/WNP/3WffLKPgFe/xT2+i9wJV/w+X7Gz/23aQj9//HxpyHP8m/D/J8VbDWM3pK0lnboCmgWDTlOlLngug60witpTzXOkmplOqYiGuwh1BZKI+wRo476XilVQ9OqvmG0ZRwn2tZXpriwCDTiVE0Bqw0xBOlyTiLrgVHorIkx0rYtfd+LREnbslqtUBxaJA8sZAFcFOCbhpwzIUbmObJaLrm5uSbGjPe3rOXD0fc9TdPUje0AoMuGFaMABvDxdTDEcAwwm6YBOAILtsqmhCCsHaUksE+V5ZmyaMe/eP6M7dUVRmv6vj8WDw7sdGOkqj7PM9M0sVwuj59/+NyUEyEGjNM03lGScFBC1UK8vLgixMjJ2RkpZuKcKVmjdUPbbphSZJxnhn5mGLe4xZpmKdqX7WKB8waKaOKVklkulyglxYlF02KNYZgmxmkSbfycK9AlcymEcCw4rFYrUkr0fc8YJsZ5RpdyxLmk7pkrm+k2xEjlNuQ4FkEAxW2R5PBM57mv40/OE+/o4ItO6QHsvx0DOd8WNorkz/WQfTBXnetSMinmjz2nQmVsV+a60or1aoW1lsVigVVggJPNmrZt2aw3fPj+e1xeXWOdZbVcc7m7OQKIOWeePXvG6dkZOSeGYWBbWzG99yyXS1bLFdfXV3SN5/xsAznx/vvvMc2Rpu3YD6OAvPpPNpf5pCSWqv+1pupmFoSZMif6YZbWXOM5OV/w6uKaPN0CAjnlyohKjNPEgcUqeZyUpIquxTkFoO/cS80cAhmN8wWUYRpmjNLYpqHfjyxakRRwxgFwvmjxTcOUM6/2E6ZoriYxcYxT5JE759XNnhBmLPB4s0QbcN6yWrYsFg6dwKiCzZGFzqwWvgLsM5uuwzUZZxJWF9EvzhFToFOedH1JngamUphRhDkK4yYXYtUVVUZ0YkuO5GKYRmHl51LAgUoZ4zzGeTDCyjbZoLXFdC3eapwVakhUmsa1WCPrp3OWVjXUOgyu8eSQWK87vPMsli1aJZ7sDNdDLWjkKBQUo2V8lEIYt8xjEY1alXjRX/NWPqexXiZErSTdRl0ig5VSpACbzZLL3YDqJ0qcOVt3tG2VvgFUzswxEkui6zTf/gvfYLZwPfSkqORFUVOsJhmNLUUM0nI5zru6OAg7zBzA9HxbJDuMYW7/UkqpnQC3hUkx1NZYdZgbt8nM4RzWW77zlXucLjVKRWzbMO4nnDXV5wPmDOMksgeuZKZekbPD+YbFQmHQFBKrzuAeLBnGFzx6vQHdgF+hm4g7XxGdIkw7VoslaRjETNy1vPlQ0y4aIpaoCm898nQ+UJJmTtDnxPB8YLVZsF5vUCqTUAJExohVhWQDBkPRhpwKqxVoWlKGcQ48uq9RQcEY0TqzcI5IRyKRtABOOSVyUpTg8G4iM4PKKBLj1HO6PiWmxO76iqwEoMq5kKMkboVbQB04MuZkrb018+ZjT+Djxyd/O+TIycmCv/6NX+WffO8d+m/eo7/oONvuSVFRnKE4YaS+OtUwRVKY2b6c0Qmu9z1t6Xh9seGt8w3l/iljCPTTxH4YpJtK6ypjoEhTJmSFLw5tRPKjKNEa1bagkhUzTgNGL4lpIsTCNA28urpmc3aCbRo2jRffiRRF+iAX8jxx5HirQoojOQYmhA1ubI2TUqKkCGWm8Qum4QZMh3ECCpfcSndWCuQJ5mTFVLREpjBglaWkTM4K6x8S+4jWhqwhzMK2tU6LbIV2kAdyUJTscW1LSCPUZ3uzv8YtHtLaFqszMAjztTRSFC0SO0Y9C5tRbUAVcpYuPpRit39GKharDU5bvN8Q4iUqt2izFCPL0FLSTApPuHj+hC9/61dAWzG21JCCfG7RYKzGmvUtyOAa+mGLMQuUNhgzsmo0U/+CMLasNg9xVqGVR3STxIzXGGH6N9YSi8S2qgbJ1izJOhLngTj1GGtFaqSCaSUhUhNoSknEeIOxDZSZFCZiiqzX5+RSCGlHTB3OrtHGkvLMtB+xjRRPKTDnQNId3ni0SozTiFYvcY0wlVMc6FzHfn8BSrFYrFBdYZgHIboYiz9d89Vf+4Z0GcwGfdqS44xtPE23YewvKdMWlCbrRD9ucCWgckSVjF4ounkA15EXS4Y+YcpECj3GBs66Duekq1EZxXLpaZevyXofZ3JMArKagy/TCqUbrNFkq5ic5vmrF1irsdpgjQMDi9V9KRTmgVQiCg8Uspo4ffBLFLOkTD2aG6Y0sGqW0r06jjRdwLkGcFibsGVH4UD4GDD6ghCjGO0mYX86q2i9mDi2ztIt7mF1QqmM6Qw3u5do16EKhHFP6xTzNBKNGOZaClOcUFaxvnefcmkwJlYCjqLYgjdKugGdRrEhYsDq6pPoQMUqg6Rplxu0U5QUyVFi6IVrmWfxV1k1O7wNlOzE+mG6Yn/5hDj1n7qG1sVXgC6tmY0SEDiLXGdjG3JMVSbx0+mRn37KjLGGrmvpt5N0lNRenpgTps73Uk93YKTrqqkdcwBfUK38u1t7nFeYrBh3Pa4kimvJxqJz4XH3Ead2QIfE9ErxxgA/bhyDEZa3OuwXh7glS3yVM4Rp4sHDcy5f7YghQAmEObM4PyHHwjxtsbp6lJVMmAZePn1XYpEipqq7V5fs91us1SxXC/b7nsWyJTGyH65otoVFZ6AaqhareHhyj20oTBRc5+FmprFwOb/knffe5y//pX+fF8+vuNpu2c6XlQb88fssBq3QNB0lgN9ueRYyv7+d+YN33sXUwt5+2DHNe1SBb37l63z3W1/jSdzy3j/8p3zj61/mW1/7JVbrBffeeI1/de+cRduhp5m5bblIA/vhkoYzmvZUckilwWW+9aUv4714A1lj2U5bdoPnxctXPPnn/4J/76tf5+r5JX0/olThy+ctoQR2IXDzwnJ2tmJzdsrJ5oR7Z2d86Y2H2GaLebWnvVGU8V1+dPWccY686Rp+6d5r7F9c0F9e8M6PPuR/8//5x/wn/+v/Jd9Y7Si7K842lunJxI9+8DZLD996tOAf/19+l5hP2WEgjfxvv/YN/vP0I/avvc7/4H/+v2BIP0CVTN9f8fTZh3zr6xsWp99mjJHlesHrX3qLZvxDdH8thIp5oL95QhsnvvWg4/V7j0kUXn90nxOnSbpBQG4oVjzTEhHlCn7lWaUFyhm0NRhjal4r3SZjnLj4YMv9R2/y3W8lbLPij54+Q18G1EGL/SfmmuJ66nn/yTNOH7/kV1MgTKmSFTOqKLTOdEuPbxzjkDHJ0KhOCghOEULEaY9W5mPEt0jBAg4hzVmtj4D3AU9VFcQ/iEiF+rkcCHdFYZzBOIv1rnYOJbq2PSoY/LTjC2Ow/zQ5iC8U4LuzTqrjjx/jk8Cdn46L00/wrYG71/zFXN3nOO4ynD+Lvf5xZh7cBeU/5ybxE5/653h8Ym/7mLTMT3vb5xg3d1/zJ73+p/37F8U6/6K6N76o8/087/+0z/7T0Kj/RY+MYkaTrLSCR1VIiJO1VkaMN6wWXTGla2u5fC9r7BEEzTkLQwiDVobGO+7wDyRpyxljDpIb4L0wQcqhE+Q4ZYVlKPIYB1a4yIkaYzFOALgQAi3SIjVNkxiraS267QqMtcSYMFodzUZKPa+yco+NdtKaXIEp+LgJaa7A9wFuSSlJK3eMXF9f38rEJNEnFLavfF9rLSmljz3/A+h7uIfOiWYld34/DCNhmqt2ez62fB2AaecESLTWHk1ZP6nTnovIq+SYyTli9aHlueXm5orLq0tKUZzff8D5+RkZi7684vL6hutdT0iRDFjnBbyzrgKzB/mH2t6lNcvlilJEI3EYB9RaQP55DozjhLaO+aAJz2179+Ee931PSomQIzGnyraU9lQKR338CsVWJUgJlEs5tNunW4C93GWnHxjqM8SDttxB+71W+0upLAL1sSLLHZIupTrC3GXJlzrubyvzt/PWaDHeNVphjcZ5x73zM5qmpW0anBXjg5IUOUamacRYx67fwTTTdRBC5Pr6Bqs1KUSUKex3O5rGc3Z2xnq9ZrlcMo4jV1dXpFJ4cH7G6WbJZtWRo+g/r1crXNOSCjhr77T51e+q1CfaFz9+HMdvzug6j1T1UejHmWGKTFNgyiPL5VpyAK1RRtrzVZ3XRQmOUlLhIA+VczqynQ4FmiPjISP6qkqTcmY3DLz33gdM44QC4jxLcWiMSBNzofGahVXoFMg58XDV0XhH4xXOAMVz77RhPylSalhaxcnCE5OAJl1r8E7j2ozIMBoa7SkxVa1Sjc0FjaEURYiJ3bgnRmkQ11qR50iaxeFHK5jGmaBEhxwlbJRUWdGqiPzDpHMF7gzognGgjEFbi/NG9LOzIqEpTuOSofMabQ2xWKxvRNoBMFqztA3EQ6FUM+QtVgnYF0LEWs1Hr/a8uhnQKpOOgupwkKDIFJTVuNYDmXFKFDRN2xzXcFUB6yNeraSbZbNcsh0GnDW0jQOVaZwwVkrR1VjRypNOiRITzb0l4WJPGUVionwsmyxoXUjVYFhVvcp6S+u1ZKwzot18kJH42BngyLlRouU6pyS/OwzSw38Pf1f6uMfpkrm4uOTN+/fwrhVZEusIKUjxt7jKhqxWqdqD6gkTaDxu49EoKUaVhLMiqeMXDShHUrA5b0EbMVuTCh3aiQZuRlNMRJkFF1c7rrc9335wAjoRSyJlhbYdhKpLr8CQ2U+Ibv0c2e2u6M5O6UdAGbrTFpJlGKTL5epmz5u/9AZPProgxsTJ/RXLFbVrTbEwhsvLGxarFUZD1okYpNAasyR1bdsSi6z36IP5N9VAOt0+jFq4K3cfUl1vjoWRQ0fQpz3H+qwOz3Q/DOz6PVNwuJRRrcOcr1m/+Rj/8oaVhoW1qK7lOmdGFYkUVFbEIPM7pcyUMyYEnBGzYAd01oiGtarapQVGHbAonDZVcgJyVpRs0XpFDllA0xC52d2gdaFtPL5pOD+/j9EZYxspOKSZed6Cks9RxkMcEZPUQk6RlMaajFlUNlhvyQgArLUlzIF5HijM6DDjbENOe3KcyTFDEek5ZTTKSNF9LhPaiAY7RaHt7ZCPMaG1sOpCCAQ94YgibaIVMQ01qRaJt8Y7yKP4oRRp59dGZE60dhjTkfKIrTrKaA9Ki4Y0SKG+PWeMY5XOgVRumMYBYwreG4pxOCcdjJoF1miED2rE8FkpKQDXPSspQwm9AIRKYWxTSSAiNeO0IZlz0L0UfVQkJ5inXZVTKUxhROVDvDHj/ELilCKseGtEE14khGIlhfSyfimEga989TpIGL1G6SzmeTFBCbUgaNHKSVFG3xpc+EbM6WOeANFoj2ki4I4LnHMtucQ6D8XvZNEtUEpjtGLXB7Q6xfslFBkToEQCoWjSNKC0kSLwHJjnKEU/BSUn4nhFt1rSrRymmaT20J1XvfoBYzPGtHRtIw8uRtrFEtcssMYTYqL0W7T2GGVRtoGkKUXY1s74atIiPkENhaKTPAOtQEksblxTDXIzOe0o+WDSu6EUjTWWZMRwtuvWKB2luJQj/e45RjuMWeCaBaTCMN4AAWUKc06UKHJHClXlBLXo16uMbxfkPCFKkoaYGxbdiQBJKuGaJUpFjBJZkhz2dJvHUvyd9uQwsT5Z8fzpK3JMNF5kxOYpUpKcs2uFhJOy6Ck73x6lHqFQjCemTJxmYpiBiWmvwRSsFfJETBatPMoqbLeUfOmn4ViVECQ5SQXH1UG+ElJMx/j7s4qdP3EoMDV/E6IFR015pTS5ZDQGlCKlXPdjYbxL3Fo4aEtIthFFylMpMWxGUzI4kzlbJBY5o0Il4LSKh19b8uR5YJzyMRa4/b4ZYiJrS0qGlKTwJW2Rh8J8IU8TOQnrPRUwWon+tD6sBVWurySurm+IKWKjEGJikXUxDAPTNGFUorVLsio1LtaMYyJFRcqF6WYgpYyzBmUNTePoTGbReYZesxsHTs5P2F5vSSHJPdGKx2+8QdGa7/3xH3Ox2/IH71zx4Rx5Z8rcX3SYaPAamqbh13/9u3z/+2/z0fPnNKbwN//Kr1Ie3ENpzdOXFzwIin7cotLAl7/8iDcefJffffv7VdozonIAlWh8w6Jdslo4lt7JuhMLMWbKVc/Fe+/y6mLHfphR7oqQ5XpVKYRB9MVNUWgDw36imD39fuTl0xc8f/8jrJe1r6FwsloQmo5UZoYZ3nn6IRcvbwjzTEiRicR7P3yHN799Tted8uLVludj5F/98QcMMfONv/Ut9u6UH7+aGPaBR0Fh8pYzZVlv1jw4WWF0RiPdGkV7Hj46Z7vbMvSRjYFnf/yHvDb0tFl8DxrT88HljnEUA3e9mWi8ZX71hPf/YGJ894Kvfv0U39hjIdYbJ/umErKDs4exdisBo2ushDI8e/WCi6sLxnHPsjFsOsP1mEmxQLrFD2SsJl5c73i0UuQoOS9KVYmsTJcV/+If/H187Hl82nHSGv4nf/OvslWZ6zDxqt/z3gcviVU6VUHtHufAoSHnJOC61lXyRnI2UVDMQq5QVaLx0HV+XLdkTTlISGprUAoa5z4z57x7fGEM9p8GrH1RwKOcTP7vk/D0LZD+KS//xN+P7/tCwPXPuWjfvY6feh/ugus//5V9iuLRn8lxFzw//v0TX+PzgOtwBxz5KWPnk2zFz3O+n/XfPs/xi0iy/Gme7xeRiPlZ7u3P+1l3j58V0M9F3LuzMkiDY6la4UgwYaxUWWPioE8oqIo6JpuH4O8gX6HQorNVGcCiKS2gdL1KQBjdoot9AGt0NVoDimiXmWpIk8uhNVs0qY3WxBRxVpKMlEQTd9F22FoZRmsSct3aVEPQJDPqYLxqrZPgEn1HF/zW7PTAID6AwgfAO+fMNE3CcE9JdIZLBRHvALV3Qe/D+w9/v5V9Ea1aVds4QwjEUkSptNyC60fA/440zOGa7z5/+Z75COZjJLhRTrFcLoVpYWyVy1f4tsM2Lcp5Epp+nMmIbMuiW+CsF+1XLcUT0USXxM1YTdO0DP3ANAmTedcP1dVc2EBN01IFfMUcpTJMD3/6vr+jb1oBuzouyKJdV+7+r+QD5lXPlaRdmrqJF3XLVI+RGAOFg8HsbXBSym2Qc5AuOdzDu/+9fV6344ByCCbKxwocAAf5H4dm2TY03qON6H8vuu5YVFkuFsSqozoOA9Y5Ui7MYSYj32EcB7x1R9bwPM8452jbFqUU6/X6eA992+K8o2k83jtCmDHmNskpHNjrgPpJ+bW74/STh1LCsDgwjoqCohS7cWKaE3PMjCGgbRDNZi3FudsT1HOquqeVIjIxhzl29EGIch1V17rUulsqmTRPXLx6JXOTTIiivVxCwVlDYzWLRrNuLIpE1tCcd7SNw1nRtddGs3CKVesxCk5aS+c1ISUBJK3GWvBeYa0U5qai6V8KyHJ64ohZSjyqKFLO3FSJBxlJhWkKhJBQWmRCEoWYxPjSVBA9l8NiYeRZ5YRBWJKqAvNFKZTRNN5jrGEKAmRnNKYarllrMViM88d1WCGdE1knDqz04GTNpF6jNpZXN3tu9oMkjJ8YDb5pJLGnCGCcOQbV+hPZ+hGSrgul0hI8KxKt16wWHucUrXOVISOaugCn6wUnq47dEMjKMm1n5t0sRd2DNNSdApBWWQzdlIAuh3GilOjx24PsVo3d6lCXYlhNtO9Gh+WQ4NyJF2//pmvxDpEtyVmSSKosVJIibsihSmMl6bCyuu6PBucUQ4ykqksvVllSDNKm7kPWUTBoBYulxxz2wKr9rqyDIu3uYlwnCdAUJqyHGDIpV0POJJ0NSkVyFKb5HDW+7ttxnik5Me0DBU1ei9FtDJFpnAjThMqFcd8TY2Z9tiSGQFZSQdFFWobFjLwQVRIGcl0cFdC1HqVyLahpUhHjr5wP5q63z+azj+qjcliTDq8/vkl97LUUGKtE1XZsKTExpcyoNdOyZTUVTowYHI7e0odEqvMFyVnJSXwcxlQgTbRGAMeSEhrwVgrbOUMsBWUNOsucluVNWJ4C8jaQI/M0Mk4zuz6InrpvaFxD01lCv8NYBxRCDKQ8o1SDGPdWYKiywXKq+1seoBhUdsLgTBM5R8iFMI3SWZdnyjxRmiU5T+Q4k0IChHigtXTC5BBIOR87mmQfE4A912KyMUWK9Ekkb5R1sp8A5KmeU8axtRrSRFIzaIfSC7SSDr1Kj0ARUcWitBXd6xqLlZzIudA2a2ItlkvdugKrWtb8UjLOWHzTYq3B+pYYZe/WyshyZyzUGPbYpH4ollEkNq3ZrkJhmjVFizwhWjogQx4xRdbklIQ9LgbpAa9WR58W0GAVqhiMtmQjz1Mk5yJKVa39pEArrFZY15FLL7r1SnSXc4oYIx2C2lRD4iTrhHWaXDxTrkWrArkEShbzYqM1xjbkkKrEncil3cojFpRuKNmilMWYiFWKINVUKQrHhPUiAZmTmCEav6hrY4bYY8wpxi8xjSLHGW03pPKSEgYBh6yY22o0JQZcI4QOoxtCzEzjDW17inUNFFfJIVVaSlmyOswgWXuF1OEQy4hICLXApAxojy6GmCb5TtqTYsR6MYE1xtK0CyijdBKkiCpB7oGWYm0u0lmptcJocywyH2QLMqnmCYlCFI+gPJHrHhZTYbVshcSRD74DYhxLnsmxB+2wWJIeUQR822JtSyoTWguLPxf5gwLrFLYRI2yCAOwpJpFkKgmUJoZZcoQQ0SYxzxHfWYyTLgloUMZL4aF1ooOv/2SYqhz3uYMxtqoyF+lYZP78x2FXreu0utWlPoYPtUB6yIlE5k3VDTsfK68lF5G+yCKDA0bGSc6YkmlMIs+JuZ6zWNArUC9lz87qE7Rv6u+z5A4xRxmLGuksBEiJFOY6n4B5QjWHrpbDvUgUrSiVkesaj5XlgHbZ0bUt11dbwjyRcyvj2GrphC6SZ8UguUcYZjHb1iIDuGhaXAnS2dCIhMrp2QnDfiAditQoTs/vEWPkRz/8AVMMfO/yiqfDzMsM58u3UCUTMlhjeO3xI9555wN2Q8/LywscsDo/YZgC0xi5vHrJ9tkTVAp86fWHfOkrb3G5v+DV1Y5xtmhdaqFD0XrH6bJFlSy+SCESY2F8dcPL9z7k1XZgalckk1Baiq8mJeak0Vk6Vq3RzHOk9CM3YWba9zzTDpTi9MEpr715n6lt0WNLmjJ9mHg1XDFbi3IdrVXcaxzzsCfzOsWd8/ym5yYmrp5d0HSe6H+N3i0Z/J447Ol28IPtBY1TrGPh4v136O/3GAXTpMk4Foslq85j0kRTAk9+8PusT0ZMk9A6sVkZlguRfbS5MAxXpOiZ55HLZ0/Yvrzml756gtXqGL94a+laz3LRslovWa1WtI2TOP8gG1SLUlobbrZbbvY7pnnEGs1q0TAWMQMuKh0lWQGMNcwpHnOuUjJK167xnDGx8Ef/4p/Rdpq1V7ii+I2vvs51SFzPE6/6nhPdcL70NFZmrLz3IOUpP1tjjt17B+y31Hj0yDsoNcbLh/l6i4fkmicfpHet1X8+JqefdXwRwONdYPbTgtzP+oTP87qf++p+oa/1JwHtP+MH/ASQ/ad8fMoH3H1GnxdI/xM/5s+QFf3fHH9+x88K6KeSmWIUZqoyaCvgljk4UQMUdWSH35VuACqTTPSgY2VrqwrkaC3JyeFaDsD0gbGeSxRmcGWziSmVFtwkRwmUtZYkKQsf/nDuGCPzLHrrB73vm5sb2QSswTrHFGZ847FawItpmhCisoKiKwDiboFobuUCSil4L2aAFntU5g0hyHVWmZa+7+U12qCBUE2nDlrfB/kT6nXeZbSDXJOu9xYrusGlblC5ft+DpMxBf11AY7mvBx30gwzNXd3xUkHNxWLBNIoO473ze6xWC+4/eMhu13O965mePuWDj55zebMjJFg0mmW7YLlccLrZsO9HyBJwO2tpfQs5Ya3Be880Tex2PdpoNptTri4vOaBeRYnju28bFAL47OcdIYSalFu2262MG2coRhNiQKWEKgJixpiOAfqRd1438kOQTJE2VnFEv32OArRX3nu57RKQZ33bpSBD8nYMwC2oLr87sFbKneT/joxEDR4OY91ay6pxvPn665ys11zf3DCOEsiFYJjGgW//8rd58eIFV1fX9H2PdQ5jHSVE9sPAYrHAec9yseRscyqyQZUVvtvt2O/3tG17BN1/+Ze/zdMPP0DFCVtOmGJCaXj56hWX11tAk3Op8gLVePQAut4pCB0AyrtHQQK5ohSFTIqR4WbHdtdXdpNG24Z9P1TQ4BaE1Udm1K0OaC6FEg9zoZq5aosiHteDw3NKleVuNQJ6Y7AoGgPL5YKFjixbT9s6rNXcW7XYkjGq0DaamGYUhqI0AUOedmwaw6rz3FtbGqvQvqFU4LJzBQnrNAnDH+wnLl7dcK/TPHq04ZVJNDmgEfDwMib6GJlTJGW47HckhKHvlcYtGuZJNGS9VrLGZshFCh5Nt6DtluQ5Hc1UQ5oolSFptJgCTWFmDpnWGtRSE1WhlEgyCVe16eVQlBwxTgDvUhKr1VLuv1JoZ2ibBX3fsx/2ZF0OeBOUgtHw6PyMMIyMux5lNM5GzjYep2Hci1lq9Q9Ga423YhCXK4t5miZQiZOlZdkuCUVzslrhjSdnRV9mOpX4ze98hawN/4//8ve4ejLy7IdXXD7dYReWMghgpA2i12gMJmdhDTsjIG5GEl4NvvEo5ZBKnYxlqxIJhSoazGE/4zjfpYNKnkdSmVKEvitfTTOHiZQjmcwcC1/+8ldoO80cIgbQTYexLbmIaaBxUhRRWqFyYrVcsb/YkueJaRQtbW/NsbsDMulQtdKgs8K3hnlODGNEE/GLVvYsldGuxRR4894pj09XxP1Lck1B8lzYvpx4/MYGpSZiP7C9tvjzDu+l42RhzwnKkdOeec5cXracnXtc03BuHQ8enjIMI6f3OoyxrNYL+nBNUpYQMvOUON+s0EZagWMRQy6rFKApWtNpMLZlDJmQZrY3AyElaq+AdLap2/XlbqfQncrHsRBy2NFui3vq+JrDeNcF5iGw7wO7ZNiGxNOLHXkI7J9d8B92J3jviY3lpjNM40TjGmGYl4A2nji9JDSGGBCw2lopA5XCoYaQcybGRKDImhEzmUyo8i4piW+IMgIC7oYr9vsdzq8qE9eBshiTKEg8kMl17zIo5SgpMoeXKH9CzpocxY/GmwWRXkxFiaSgmMYrAcpjZhqvyayIKRJCz7i7oNvcI2XNNA9EXtFog1UGkMKz85YYZ0KYaa2VJBjNgTilTUFrT0kJxj22WRJShBzQBaaww/uTagY5kVNEY1C24LwCHOQ90zwQQuD++VtiQJwtVrfMcRLDzpQIYcIYS4xA0VjnWDarCvQ3OL9EEdHZ1vXcMKUosiI5k2IAClkpjBVCgCKh2iVNFuB8CltatyLmLOawKbKxLdZW+cAghqNOF1IaGYaZ1fIe3ose+zAm5jmhVSLnSIozoVi89Vi/ovFyD1NuMCphVGAab5jHV2jTYN2SbrlgmmcUHq0bnPdM0xVae5z1KKPJSd9q/udI051AcIQwEuaBYqTYaIzG61bGlfG1zhVRes2cdiIN0LQ8uP81nj17m3HaQQy18zKjTaGxhVK18K3OmCaxNEuMEaC2aEXcfoTOGe0drlsBJ+QcmcIWZoXBQvW90c6wvndKnAMpgHINq+WKi+330XohgHtKhLhFG4NVHoWFGMl5IuUg2tZmidEBoy3edYzTlXQW6I7GnaP1goIUuXPsMdGJgWURCZem8dKpkTWlGJadZrudmcdLxv4lWq8lBnUrrF2QYiQ0M4SEilLgWi860Fmu0yjQhaICmYjXEcWCtt2Qc2Gcrsh0WKspWTPlkd31e7T+FJUGCltiXPPml7/KPO3ZXj8XI9dmhXOGprXYxYq2u4eLMM8CsE5qTwnCulYlkudrKAljofUtg95hTIsxDSkHuvYcpUFbS7fcUMoCpTs+61B1/UkpEucocbdUlY8FnfrCzw+y1wJUybn6KpSj9nSkYJyTwn2pYDdFCuZaCtRCmKkVsgizAWsrGUlZMiMlB+KsuNopFg6sgUbDMil++P2PGMoZqOqro1WVI7zdOKSzqjDOEylGtJeihFKKcLMllSR7jdbcurZKfJ+VR+WE1k46ks5WnN7b0FnNwlvONgvmkPn+jz7k+mbgtfMzXjs55erlDXs7UDDczGM1laUWriGmDEHR6UY6h4qiW2348htrTKt4+fQlUxH5sFw7IK+uLpm215wvF3z/8pKrcSKmjM+ebq14fr3l1eUVbxkFMfP644e89to5v/VHv8vXl6d89zvf5t5X3uK33/4xf/wH3+PxG2/x8P4jdh8+5W/8xl/g33z4Ie++3HITPZbAFPdoNfPa6QnXNxNKwRwL23Hk+nLLs93Iy13PNI58x51x77SCyePEYDTbyy1EMUdXncIaRRalGwYiVhnunS35zluPOGNmFxKX+5FhP/Ha43N+/Tu/QdctMM6wWMKDB49pN6/Tv9zxJAZSUWSVmbRh136Zp1f/im+/PrJ+I/DR7yT+d++84u+sHrB++0P+3n/6n2L/7ndovKLolqjOCXrk7LU3WQ5bhstnPPnxv2L91gPy2ZKzlefNN9/gl776Bjf7PU+ev+T3fv9dwt5htcdax+UO6dihiKltyXgP9+6t+SX/Fq99+w06v+C11x9hrQHnoIgBsCoSvjpv8dahjWfOmcX5OSduwI8Tcwy0tdivtKJpPI/un3J6dsZytcQaKfiWogg50aeZZy+ecG/dUZTmZgy8+847PDg/44375/ylr7/OyX/7L5IfP2Z1bwNFsIqQAgrxUEql0FhXu7/EE09XE+IDhFOonYwxHzEPU83NUxLvPm101bWXYvPngSW/EID9z1oa4qcdn4hz//RB5s95/HlIffy7dC0/6zm/6DH3ZzmG/6w/C764IsWf93O6e05VgS2jBIDKuaBSEWfvKmWQa9v3gRKkKjCArkCZgsZ5xmlGOQHnhnFEW1uz5Mp0hwoUgLilL8k53gG4pc1b58LKOFxj5fOVMAVLyTWRk889Pz+n34uhjNaa9XrNdrtlmmYW3czm5IR5nigm1wKBgO8HDWhpVz8EXgKoH1jPhw0ixCAme8ZgG08qmaZpBehUtexMEYmSDJRCTLGCrI5PSpEc7j1ATMJCdJWNXEquAV6hlraBgrHmaNKnjYYkoKPI8ujK2j6AR/nWPFUV5jzx8vlzSIW9Uvyji98ixkRGwD3btuxeXTLGQrNcsu7WLBaepbe0zmKt5nyzpu8HST6nSJgmGu8Zx8I8X7HZrGmalhgjw35kuViJMYvVdE1L0zYM1fiHIrI42+2Wruvw3rPdboUpW5mqCYipSu3cYZhmBNxOJVdWjyQAKeVqnihsk5QEAM61ap5zAX1nnNV7Lfpwd6Qkyi3QLD/mmrAeGE2HToKD7nusnRelAsYi39N0DWfn59xfL+m6hjlObHc3taVX2jXbtsGawvX1Fa8uXtUkO0hbn9K0XUdnDTlGtIL1ZkUKAe8Mznt8IwZL/TgwTSPjPPKjH/2QhW949eol773zx5yc3+fh4y8Ri+H55RXaOUkW6jSkKHQV3EFBKhFnGj6eRRWRISgahSGpqv0/B/bjVAMuYTFSTV+NEQ3vnKIAmBRyZYkqERatDImAb1pSjMxRJF1iCmhjUGh0kQDMeymgeW/xVtN6J/IKptAtPAsTxeRWFZJJXLLDO43Vii3QeI3XBW8yZ07x4PQ+3lpMyZQw0XmPrZ032TrazlFCQlUGd3queev8Pq+dWN48bdilFuaARfaEF9srTNF4Y1m2jtZvoDIfyxRZnqyYrl+RMRTrmVPAeot3Hq0s/T5J8pwUWlmWzYLrS5GL0EbR73tKEekKZR2ma1joBda4muzNpCzJq8FgtSPEnnHfk2sRYz9MOGfJSMJ7flZ4dTmw3UZU0EBNgtFo7fj2l79C1zQSTFsNxnC+XrHwDooYAqujp03dn4R2j1aaRdtyb9nRnKwBxcvtntBfMA2BtFhgvCePHV869fC1My5fPeK//D//Q1yVAiBJB41ylUWjC85qVBHGZtM4CuMRbM1ZktOYhOnnlMEbTescU4R9hKIPxZvb9VILP7POYykKJpSA3gUa32C0kYKsUYThOSXdx5glTWPo+z3KKWxjaVVHSnOdV9KRsd8PnJwZMW3MFqwUJ5QyKKUFoAuBlDVJyz41h0xKClLhvR98j9e/+kvYxVL2BgpRC0O0pImARZmWoR+Z5sjjtxaAYu6hJM3Z+QJTNTanErHWcnWxZ7HxnHgnoHMyXO4uURQebR7w5MUF61VD27XoxgMOo0XexraZPEYiM0pD5y1Tih/rnNLeMA7iQRDrNibd/h/3QLqNq/QRZD8mHJ8G5tR/K3XsffKfxmliHid0Lgxjz3ATmNcL+ItfYvy9HVnB3iQ+ILJ0jt00V+3QjM+BX3lwD+8cJs60viHMs3TRKQ1aU2IEMlaBLpYSIcdaEC8RlcTcPCeIU+Dtd/+Q080p98/XOJ3Y7fekoJlUJM0apUbACsjXNig65hlSKpA9liVhviROPfM8UrrCsO9JWTpQlosVeZD1f5gnGr9mzCKvYoxHOysFouTQ7hHn5x6fFfM8MUw9oUw4ZWidQyEFq1yBa2UtK3/GNO3Z31wRw8xysWLc3RBSQGvHYnUPq68IWfZj31gcCa08OUf2/VN894BxvEaRaduW/XCN9StK6on9FaDwxgur2GR245ZxusFaj7GK7W4HrEA56dzRLVO64eZiJmbYbB6ScqzMb4hxYLcTI3vnHOv1Cbl40AVtPa45IaQdJRa0stimI0UlxvSq0DSOpbtHCAMlJRrbVhPYREpBpL/8npR2UBIUwzgVYRzXNcNbjbEdRjlUccS0A9OhTYNWhuvtR5ysv1TXAAEblWlpHFirmYLG6EbMTEtinkf2wwuM7fC+I3PoyBSvo5C3pHRNKiK7tWw7Sg60zqC1R5WWm91HLJcPWXb3iXHLy1cNjbnEugbfnpCnV2AW5KKZh0wxM+fdfYo2hJJp12+y65/hlcarBeP0HFU8CkfTnBCmazQLum5N163wbUdZKkxJ5JTY9VtOT99kGnbsri6YhoHN5iHGrkAVdvMrum5D609kPueBkvf0kyai8VazWb/GdvscAGszKcGiW5JKIqaZHEbiDNYsWLgzBp4ABoxD0TCnHmxmsVpjy5IhJMad6H2HNKKiIkwTzmjpYIuAskzTFqWgc/cIsWCbBdZ0KFpAJHlygWI6Fo0WSRVa2vYe8/SE3TCB9vjuK6S9YRovUCpxenZG173Fru/JacSoyMnZ10ilJbmA8xOlaKxrhAwSs/gmuJbGS16g3IbHXc/Ub4nTDuM92ow432KcAeYa00Y+7VBK+kr6eWKedO3GSmglOc04jZ+yEH+OQwmBJcTIOE8c/AdybSET00aJ9b1yKDUTYmGOIrtSsgaTpSgepdMkBiF5nKxW3FwNUKqcKQnrG1xJGFMIFp4OimikC1kbTTxUbI8FA4Uxku9N44harDHNAm8bnHXk88h8+YI0T+TqM2adwrcr2qZjvepYNoZSpGPifLNBl4I1gsM8eXLNfpi4vLpmGCaev3RcXm65vN4yx0RWmvefXRGniRgjT55fyLqKhtqxFL4XibXr8fpKCDrzON/eYw3Pn3/E2emS9aqhUZpvfOsrvPvBU3709vv89vd/n26z4f79NV9+84SmTFiXeLww/IUHp3ztK7/Ob3/wY/5v/99/SZkjf/cv/jLrX/km7758yXsfXPKV194krxv+g//ob5Ci4l/81j/jd3/v/03KgRJnUt8Tri7EbN5ocI7h6Sv6m2vm3cBYNG/+3b/D+oMnvPfu+3zv5Y+4ngOFQrtoWJ10PFifs93uiDmji2XhLb/+9a/zaL3E9jMvFag3H/Hoq1/mLb/gdNUxhsijhxseP3zMvcWSp0+ec/3iKdfPLllph0qZx/dPeP2046Pf+zegB/qYCUPm+UczN8CTtMduIyd7z493S7yzNeToKbsPMVmx2+94+uoZr8oNP36xpYSBYbziq48f8Z1Hb7FeOIxz/NW/8G1O1x1d69HW8OJVz5v3lpRSmErConj9vGPjHuP8Gzx6+JiXOfGVt96Q4nXjyVETi2AdKhZKSPzyd77DX330Jl//1V/h9V/5OjFnLl5d8L3f/V1++1/8Lv0QKLnQasVvfuOEe4uWh+sV1jqy8jTa0xgoGP7RH/6Q1x/c4/56xWnX8cbXvszFywv+6MkzfnR5xRuPHnJ++Yr1swsWjy/R3/1VnHUkW5hNREfw65blvGA1i2m48gYORsRaQPcpzuynnqlklFZEFdFovJaOaiwYryAFvLN/dgz2LwJA+7mAuAo43P35YyzYz3jN5yo9/KLX9onj873/s1/zRYKUv6hkzyff93m0zz/tfXd/97NeyxcpR/JZ5/vTAsL/LItRX/RnfdHP6fMUAD7rWamPve+gHy7AuzWm6iRncZQupWLKB/1tBJRUkhi0ja/O8QpnBEC4KwOhlSOmKO3OKZOnWAO8qtGXwCsDSkBTX1ntR7mELFrnucZKOeUqM1O/v4X1csU0TVWruqHrWgFnc5bWJiPfTbSAD/NGljTRHROw3jmHSNtYTDWxoxS8sXDQZc+J9XLJOA6EeaakRJijmFzVdq0wp+NaeVfaRbTalWjnHpi8BWk3VhW8z0qCLqNvrzHMqFKwaLJSzDHgm+bYtpXLwURRqtsmKcyywyon+oalEIaRuWo46lz46je+xcXlDcM4Y7Qh9iMle1KUZ2Stq3r5hnGc2GzW7Hd75mmuzLMZ5+Q5CNte1+cNpWT6QTZkWyVmvHXc3Nwc77dSYpqLqizng34bdcwdR2e+Iy8jidsBQE8lCuulPpt0AH1KlYI59IsXKvujAvLlIDyjqpTEbRFE/shzOchEKKVxugJCVmOMQhup0C8WC9rW03iP14Z5nOizmMumKFITISZQgZgLF1fXpJTFJFMZHj2+R7dYs93v2O52LJdLTs/OODs9497ZOdcXV+y2O6zTLFYdTdNw//4Z11fXPP3oCXGx4KofMcyslisePXzEq1cXjPuRZbuE+BLthbl+GH+HO3t33zw09lZC83G90FpaYUU7X2QTVJG1wWiN1hBKnWcKMdcxUtASzdQswLkqoKGoIrICRT5RGY0pBnMwYS31c5FxEUPBKY3Kosfbtoa20yyaDms01ii8dygyjTV4LQn+pmkwlefb+Yal82KUmZLMsVlLi3aR8aazoiRFCLAfAi9f9XR+ybDLfPjuFtpCGhK6ZJQqPH8xEKOseY3RbK96RH66YLXiOgyUZJhSYpoGmrYhzIlSRhlfSsqPJScUhWHuCXOSznYtxQjnNGOStnSz7fGqYdbDkWGmrKtmhZmSRLd3nCZiiFC1470Vzw2isGr2c2QMVTz94ECNMJK/+eV7NK2VuRILKoNTCs0tO4XDmnXnUHU4hRBY+AarpOBlleFk04nklJNWa+ukuPvmaw/4W3+t5eXl72C94qaf+MG7F8Ri8a6aKBWRiMg5Vla/kyKcOtRsK6MLdRx/UgQQinsBSsy1wFTF/4o6Fs3Kce6X47ojggUiiSPSQSJPFsLMMEJR0uHUh74WVTXD2GONwxhL0ZZmuWTc9Rigaw1zkrZoo/LR3NtWWQitTR0LhpQDcwzcv38OzjCESEgRWwI0SDErZ4xp2F6PFK3o2hbnLDFbIgKObIcPOX/wkFSidBeEUqUohC5VSOSica0USF9d7jg5XeJMIsSZvg/gFXlKaGPwjSOnGeuk+MNhXY+Hu6wIsTIRi8JRtbMLt+321M2srjlHqY27K1H5iaFVX8vxPbfSifJincCEhBlH3jg9YeVlbd5Oe2LTknNiMpmrhSaPowDjdS3SBnxMqJCYy4yIZMg+FrWsByoexpailMQQIkMI0LbErJjGnpAgZaBkzjYbum6FxjKN16isRA86TmA8scjappUYC8tOlyT+IVPGa0gjWmes8wLI6hGVJvKc2CdpKc+AVhllO3QcgSSMSNNickD7llQ8Tz54n/unmxrnZRq/JoWM9R5jnZiUNhuR08gR3EyYM6XU1nMdcMZivAcgxqtq5imxhtYNxnms9cJmiwqj11gzkPNMDLBYrKCaWTvnGfZ7rC9yniwxhDMNxogOvXThiCyBVpZcuw0a7/CAM5p5HlA0aDxaid8GVa5wHncUJZKFSitinrG2Aw5mzAnUWBmzoJJhzglVpKtKa03fb0ViJUVSmiAoMcGkYHWicR3DvMcYR9ssMBTppCGQy0AuA1CB12Ro2nuEOOFcizFOYsQyk7MnRgEijbaVRBEBi7UdcwiUPOOUGOQ1pkEVSLNiShmVRiEalOpdohu09mjdYe2OOO8pJYHOdIsFu5tXhNCjDVi/IM75KOcQ88w4XAOQVSLrgm8WKA0h7CAbtGnwZoFzVeYmBzCZVCZC1Ph2hdMdWEUqkxQcVKHxDZv1axQ6pjiQc8LbFShLLhaKGN96d4bPQnjp91ucL1gdiPNMP9zguxXJSBHBmzVJXaN9I10gaLRaYG1X48WAUS2mVaJWmDK67HDdKSnOlDjLe1mTwkzMmdV6Jazo5gylPFkf5NsKSs0UAkotyDmglKK1UtguJYACbRTOn1VmuUjH+bYh5RUgkjLZtDinwXUYjbwuXEvsbi2lLChhJMWJFGYa36FYoLXF6Ba0zJnGGUqMQpRpW7TpQFnCGHFuQPHpAHup+ZuYVtf4OSZcLZynFD692PknHEez0hSqv5Ss06XGGDEfTFuocaXE6AokDsmqFqyEwGUUKGUlN6IyfaseutZw2UeWNuNq3BmiQlmFMlIEPVJs716jkX1JJY0hk/fXTGiSdZw/fMjcNAwx0qeAUkb2uVxI88y4z+Spdk+Uws12FHKMsHQY55lpHBmGgVQK1/0IRboSUi4UItv9Xrx9SkYZBQdJsixx2vU41bGm2StDCBMYi6170MnJCdYaQkjsthNDmcnaMIwzhcKUMiZEhv3ETiv6Wvi/2I380fvPeXK95cnVjn4aMUrxL3/wkZDMVEYDHz55xjgPPHz4FhnHD955m/3+RvJho2G55Mlux7IRnCAEwEVYetZdy/liw4tXO549ecKzy5cM9QEIKULW0O3VJffON3z5tVMWraOxhgfnZ1KkpNC4hshAKoowzVxHGScv0wv6qx0fmQ7tCurhmnm5YVty7dzwuE7zZH/BdnyF30fspLnRSzqu2M+JyxzIrWJOCoyY1Q7DREhJxmspdM2CzWxxGpT3WLNkTB3v3GTcdoaScG1GseNgHFJy4rff/lC6fU2D/tEeuw+0zYJF51moyGlI6LlnGPfY0DGP0vlbMCjTkKzBqJl+/4I//oPf4emz93BNizaOe4sT/v2//N9ijIV+GNlevGSOL/jRB1eE5SO+WRSmZFKYCWlGkdBW8eDBKUZp3n51QT/tSGPGOcNqk/mSBkJk9+Ip2/1I942v4nyLDgU9JWY3YrWjhEAeex4+us9soZ8GhlCIpWCQ+CaFUdaPY+5ePdEoWO1ojMcbSzSHePynH3/qEjGfl6H8MwN38qZPnuTTTvwnv+ZP+qzPARL+4kDmZwG8v+BpP+vTvmDA/k/zs/40gO4/C01x+PzX/u8yI/8XOX6x5yD648Axu5XtT1jqWiGbP/m42WstIHw55smSeHor+mnHTzJ3wPXaiq8xoneapVJ7MKZS1FzXSMKmES3NxEFfUxiEBxBIoY5agYcJrpUYjlEKKUamcaRx7timpFQ17quMy4OJnuBEt8aYUIO+lCpwXb9REqPXnAToVQXZKLQhKZjjQcrFoI1FK02ISTQZobLZP/48lFKi54kEhli5+yB4cC4StB2uIsWM1VYSyAr41hsCSmGUJZeEcUbuecooBJAySoxK+zGRda5gvDoyzjs3QVGMvaJRFp0FOC76kGxKgNS2DbvtVqRZEEbzYrHAGCUGhAdWf9WPTSXT+Oa4DqvKLjt0ClhrJZkgV0ahGAqJdIsA4TI86zXnVOVdUtVAh5gk2LnVqk+VOS0AuyrlIANfwfZSz3uwyLuNvo/gOvW1pT4bgVzqmJMAXUA3g/KKk/Ua7yzWiKmUqix5pRSLbsE4T1jn0cYSc+H6ZluNZJ0wOLSm7VpiSgzDgDGG1XLFcrEU6SRUZf0b2laM5pwRM11rLc46dv1Au/A8uH+CtR5nZrzNNDaiD9/jiJof5vthxh56O+o/HKj96s6/1kKDGGEhY/BQJJGFocYq8pyNETOqdLiXWeapOhaZ5Gd0ldZIdc3gAJLethGWWgyRlytM/VOKEdZnEcd7rTQl6boWSbfGgbikI8QxSTUvZzTCHNFUY7JcmEcgF6aQuB4CLy56ztaeJihiP7E475h3AZ0TSicurmcBvpXGGc00iWmnNSJ7NI4z7cJQlIxvnwy5alemVNDW4H3VQaYwjzNaWQ7NL6JNagTEyzCOkRsDSgUxiNMOVypj/rA2NUYkErTMD+ctTSumdToajPXMuTDngwr5QVi0YHThS6+tq8SDyDRpLN5IV0BFtDmQwri7lt1OImEFpXjsMOralqZpsc6TchGGnbKcNA0n6zV/5Vdf8Wp7w3vPr/BeE+Y695Toq+tatBMt5sSdj5ZhqtTHCrog2pHilXzwsjh8TVULenX+HzpX7pzvjiBVXV4LMRZiSqQcZdzVNVnMr3P1MJH1JOVM4x3qYAxsCmSL6EqLrJexuuq+KtFzpcorKVlLF4sl0RrGKdEPkfOFGKBmVE1ONLn0lGyhWLR20mVmDdob8pDEg0Llahxd8I1DWZlHMSQwBm0dBkVJhcWiJcdRpEPmhHEtKWQxXm0VOSdZCaspl9GqGupqFEU6VYzCZIXLBWcEBM5HWd3PRmwOK1H5tF9+/K8fe4HICWRUTJgwc9aK3nguiWGe2PoF94eZmcKNAwYp7nOoEWmFUdLRJNu4wmgLWsZ0yvlYpNZ1fQxB9I+nKTCOM2GemSbpnFp2DavVGmva4zVa5+uzjxTdUhDjYkWhqEJOWbRVM8xxRhsDSmS+jLKIF6AUfFJJTFNP06xA62ommlFFTMq0dhTTorUhY4gpMQ09g1d432CMB90wjHtQkhxP0ZBUjY+IhClBKRK/GEN1ZMDaRrrj5huM7dDVaJXqQyPLg6Yo6dCx1kmBOUl3jDIK8Vi5NSJXStjdSlmUVLvkrtWCmKrrUC4F6y3WyD5syAIeZo34GBiUmo+68iI1IlI8uhbcrRaTca0V0xzEmwHRnM1Kk/KIq+aaBSnWawy1fZOcZkJIaJXRTvabnGonqPaQD4buWWQQ0yT68FkRMXh1wsEq7rCf5jySkgasxDdKHU3gobJth4EUZ0zToZSVOBwNzouhehk5rsDqMC8UiYLRhmIjOStyMTStZxxaSpooaSabjpQjVpsqO6iIcZJ1tkSyMzi3pqRMUQOi5b7C2A6UR2vPnHay3+ZAilDSCmUajLF4J6buCod1nuXqhF0fiHEm54Aza0oxAvIW8RsweoUxEzHsmKadAFYpEOPIPI40i0UtjNe9t2lRdgFYcgZjlrWYX2SMK42xnpwykYRVDVp3xPGGFEes91jbMfY7KDPNYsk7P3rKa2+8xeZkQ99LZwXVdFU0xaGQ0GicbYmpPlcEKDV2iXMtZZ6IccB5yHhKkU6rXERv36gaA2fIOVTj30rkUKYy2Ee61TlKJ8ChtEdTiGicX0LWpL6XQohqyckQy56SJqqL6qcfFUynajbnGI+xVr4j6/ezgOwKRB4mzIRprnF79dWoMflxL6jEIUptCE75zsJMJWPUvEpZidvr4l1DR/ZTEnA917wpKXTNT8pBAvITF6iNAhyZFqtlnhyY/s4YTLcU4lQ/HLeslDIpzZQUGWsnZsqFTE8Ikn9QY4k0y/xRWjPNgVQqqaN+93meK2kHIWTBLeFHwZwkZodEVpLPGN9gETnKzck5sUzsh5lB0nUxtK8s94zEGMMYUAhRLqTMq91AHzLmuZis2sbinOH333/O0hk2q1Z8VMqEUkk8LqzixfUlY5hZ2E4kfrxj7hPKWTCQUkQ5Q7Nekop0gz559pyb7RVTntGNleJjkY50ayx9v6MxG047z+lqgbOaVszHJG7XmhKMjEsycw4YrUUic7tD4Xn4+ASz2tBaDUbx4PGbnN9raTtPn0ZejZ6YCouoWWw2rF/dMKXMtUrYkoiTGBsLoaBnmmcKIpPSNh2tMhikIGw6T5gDL3eBkgo5RIx3DFNPTgGVE94pAjNKW7R15KdbvnL6JrFtiSFipp59H4i8T7/f4/cT/f4aaYpXTNGyODmn5Mg89/SvZrY3V7SLJV23ZLk+xTfisWFLIVrLsJ14cblled2TSkGXxDwPTHGWvdIoVquWOSRuxoH9fotJnpAzuVdcXdxgy1I6y3YTJs0YPCpm0jjy0eVTdJzoNJwtGn79m19lyIHd0LPr97zst4xzxqQESfaR45pRhFwYq9SnM5bGiubTF8Zg/0VkIT6LofzvCgD4acdnMWq/6O/053V/fhbZlk8aU8IXdz9+HnmTT/vMn3YdP+t3/Fmv4bOu5acx6L8IGZZf9B78PNfwWZ/9s5zjZxlL0kJnjsCjolQzuMg0CyvPVF1zUyVfCgXjtJg/HQHucjSYU0qRsgDLB1agt5YYE8478I4YMr4a7xyATKc1aGGMN51HFdjudpRSaBcdzsvvcroFwnMQ5pE2GqtEN369WrNaLLm4uODFMLLZrDk9OSGVzJgCaAlqUgrVAFISTFWTmsPYijFKC32MNdGUpC3FSEHMsuZpxhlLto5duGGzPhXtSqUrw7AyLsi3oE/9DK1lk4zx7nNSHIwzDxIoIED6IUg66OQrrWi7jmESHWyjDV3X4Yyl7RoxEZk0+/3MHMSkabM5Yz+8wLqGReOw3vHhux/y4Pwe5+cb2RiVZupHpnlmjIGmbRnCSJglCM4pYq2heIsYcfZ0XYOuGtnOiVllmCdSSjRti1KizRZn0Y9vmgbn3JHVL1l5Fnd5Jay5eQ4CaJVUE+4KuJdUQfZYwfRCSnNNLqtG+gGMPyBqB5+mqrkuoPkhjy+18HI7hw5gW6lgvMEfuzlySthqGmm0wTvHyeaERddSUqSURGst7aIFJYWizWbDs+cv6ZZLlDZcXV9ztd0JON549tc3vPPOOzjfoJVmtVgwTRPX19fEObD1Hmctv/Kdb7PZLAh54O23f8zz508pRfHaa49YrTaUlHjt8X1+6Uuv88/+5b/mL/+lv8qTZy/4vd//N3jj7q4UFYDWx2RcIQmK0ndQRg44vKLkLLBAkddJ3lqE1ZAS2YiJH3XcCuChSepgaSkJmzBoRY7qMJcOEiMZ0aOUz61yNhi0cVin0VbhmyUlZ4a9MNC308A4jcQ54DO0rsWjRMrBQqhFOaMUXhuKVrRGdMOdbyRBNgmtpL61qgBR1pqRwpOXW9azoiwbztaWrlkQdlcYElYX9lMixYgpBacc3rUkHdGmkLUmBEWnDY0XBpwKBu8MQQVCDGirOT9ZYH1DRjPuJ2kHnwOJQrNYkOa5JmaFMRouno0stWbVOtzZAqsdhoBtDMv1gpgT3svcQBWUCnSLFUobfMko05K0I8BRuknGQUYTeethi9YBlMJZR+dbutbVedscRoSAqnVdM1r8FpRStK2llJmUhOFlnQDL2ji0FWM4bQ0qa0qSNe8//lu/wf/9H/wT3v2gZ7Uy3FxM7PtM4y2rkwXOW6Ch5Eicx6pjro5zVGSXxND3UHgcUyRkRIO9gnWH46DJegfPq+CklrleJNGPKYnUmIOXVzvunbc0TcOia3jy9BLnHN42ZCKr9RJhc0IKkZAC3aKFXOoa3GKsJYdInGZcI+3/0uERUE701n3rMVYzJWGtDXFgO4y89uChtNNoQ1GemBKn9xyvXkauL2dWmxVh2tMtG07PVnT5Ac+uryqQa7A2YVcN1jhiKFxe9rSdyA8457n/cEM/BkgK5ywnG89YNMZltM6UJF1Lac447bHe1W6wiq9qAf51rcE4FDpL986xvFzv992Cq6zT6hZXuYPB38FZbn9W6q5vsjzPlCkxYlJAh54xSmHGN5Yf28DDODCGwnX2WKMOyvWknIlG4zovcgAh0mhD5NAdlQlxRjtDTEU67byiRFBZUULm1asLfGO52e7IKXPv9B6RsRrZKpr2IanulTInWoodpasiBYgjJY749oxSIIw7Ng9fZx4DKQZU0YzDc5T2KNNiGQlhi2YJuiEbTQ4vyFPE2BWm2aBsi3aFfrdj3m95/fEDtpfXWN3SNh0hZcaQ2I17ikr49oTry0vRxlWZ3c0Vr72+onGaXDTDDHGKLBYK4zxKbcSEUkEpM/N0A+kcUxLaeLTywE6MtrUDFCENLLsTSp4Zpz1oQ4zXoEacLThzzjBtUdEJqGYmNCsKipwDGYW1EqeSCswBpy2xJGIecGZJ31/jfYdzS7RtURbCvKfkgnUnhBxYrxcULMOwJSiDmCdqCpZU9ngjUktx3uH0gqJnlFJ4s2QYXzJW8gXKovUEtBScaCinQUw0lSZlRxkDrgsk5YgF9sMTXn/4G6QYiPNA2xquxqGCbJaYpHskHXpCdA8syHkm54GiDUyFcS5ibGcRs88kDHSUrF0qT+Q4EgBHYL1+jZQL+/4SrXecnLxJnHvivGMcpGcj17msUOBbcpgERCoNNzcTzhl8q/F+i3f3Ma5DG8eie8BUesL+BWm8JjMw9hlnwFqDcx3TfEF1YGCcB8bpgjjuKDkz6ozhHjlsyczgM1rdJxdNKplcBubZStdkzKAsdtkx94ppilCu2Nx/jNULATJzwqmGYfgB2nicW6PViGJJ0RPYwsI+RsU9gwmyR7sNqllhvCPFEe83/B/+r/+A//5/fMpf/IvnzC8/otl8WVjj2qKyJmaJi4oRg8S2fUCOIzlMxDihGsUUZ6Z5JM4zqD1KQ8kjKfV411LyDrTHlBVgaPSahCYmRZivRWpSWYrRuMWKPDakFMgl4owhlJnEBqU6lJ5wzRJTGjAKa2Z2u/HjQPknDqXVrXFhyqQ5Eig4bUkpH0GyO++4/c9tRf3jJy3iiTBOIzcXl9huIUVyUUARTy0lIGoKiaHfSeHPGkrtBisqS6DpZNQYZbHKUdLBUFYuTBvZW0wBiyJVY8bY71FaiFlFvGrvbvIopbG+wXcLcphZbO6hnAGryd7QtOfYOVCub6BkphApVe40GI6eT8eqfSVoHH7Uxok56/G21U2Rgi5QYq6FewWpEKeBJL8QWbKSKEfjmIhpPI3xGG1w1rHanPHuB28zz+JdkVF445jmam6fIzHO7CbLPmmmaY8pM3mYKGrAaYNrMzqKB8I0TeQwsjKWh+s1v/aNr3KZEsU0LNs13XrB5fMrVmbBsvE0i4bleYfCUHIhhYl+u0d3HS+ut/zR22+zPL3izbMFj1ZnuP3A5eVAprBadGw2KyZG+v2Wj3bXfJgjtvE8bJa07QrTLHHNDcY2NF1Lt2xpz2S85Cgxk3ilWR4+fh2t1jxYdbzxH/xNXHyFCy9pbOK9/jcwL9/my7bnb37jdZ6+8wGpFG5KxsfAdHlJNKXuIUlytgxpjlzGHs2MmzpUu8A1ju31DdY2qJIgB9bmFFekwOatZkiajQGrLdZ6vPZcvrjhyfARcZ5YNCsuhh2d/zGrrsO1HTe7Fyjl6NolDx8+4ktf+y5XL54QpoHzR68RjRibztPIu+/+IT/6wYdsuo6FabCzRp9ntJaOqBxH5mLZb6/o00zbtiLVFCd0gROvOducMe5FN//Z1SWX77/k137lK3TNArMynAOhJFQJDNuX/OA/+z/RrU9462zBt17/Kn/j179N76Ur63q345/+8Hv869/9EUssOsjYpygOvmc5FsZpwBAxBlqjwd3quH8WdQI+J8D+pyHf8e8quA6ffe3/Ln+nu8fPItvy02RWftH78fO8/6fJ0Py8n/FFjv/Pc2//rOfbz3rPft5r+FnO8XnGkrQiGmFlp1rtr0C8MdIKpo3INhijj8mvUuC9g6qfOk8BV42iDud11h5NThvvGYdrOZcx+CNbIx+vzbUVbCiFcZrw1mGtJadEnGamYRBJDa3x3pNzZp4mlFZYJYFHSglrDE3b8uDBA54+fcrF5QX7fsfDhw+PGusUSb7nWZziD7I4cHtNB1PSoyQCIn1gncVquS4qGOOd5/z8XtVF1uQMIZVjcQKkXbKUQtd1VVpF9N0PuvAHVn6IobK3iwTjlON918aKeWwQE9Vtmjg9PcFVreAQMv0ws9tv0Rq6dcM4Zfr9RMkDwwCL7gStxIhz0bQoq3h5dc2zl6+OMh7+YEhUEm01Q1uv1yy6BReXl+QU6zN2LBYLtKmmWVod2eVKi/xBSplXr14RQyDnwqJdHMeXyCTUsakSKSPsp5KkzVkVQpgEqE2zMEdrp0Gu7ZmlQMyhxq5Vdz2lCrQjwe7hER7A8yLPOR95qrWLgDuFrkLlgBVgJhdhZDtn0FoMNh8+fMjJyZqXL56zaC1hTiwWK/76X/tr/NZ/9V/x67/269x/8IB//I//KTkn7t+7T9N2bHc72naNNZYYIyXDL3/zDd5++236YeBkc8L17pqb62suXrxkmib+5t/47+Kt4/LykhcXzygZXnvtDXbbHe+9/z5f+tJXmMeJ/X7gh2+/zTiMXF1d89677/K97/0Rzi/EnFeJeZOYTB0KCocU6bZjowrnVAkDKejEGI5MRa2qSVVlIsZ5Epd5La8/JjFG1YeaUIjcSSmKlAJhShgnkhooi1KpguxK9AiBm+1eWLZGM00jugL3Rhuct4xjT6ldI95ochnwSrFwlrOTDVfX+1r409KyXOBs4WmtJsfIV+6fsOocxiosSoLDysZVKdLvE30bCcsW51rm2TBlGaglZS52gTzL92qsFIY0ihiFcWmU4upVT9Yi63Rzs8U3mpQLMWbmlMnv9lzsZ676kd1+wjYNuUAsmTHMdNbz+qM1TWf58GpLDBY1TcJcbIS1bJ3HO0frPSFOzNOI0Yr1omPTWdrWETPsx4huDK9uAlpbkhJzwMNkMQreOF9xcXHJ81c3XF0OJG2Jo2XcR2K6BgSvFtNbybRj9T1AKWLIUhhLkoA2BUxRkDIqRkxKlKnglEhoBQ1pgH4uzFmz9A2rNjPrfEciK0t3iBFplVTnMKp2vJQoz60g2vPGEFQhV2Fskam/Hdsgrdi5/pQpwvinSploTUjCEjfaYkzDW289ZLlsiDFzfTWwvn9KmnrxfNCG1M8oEecHYB4nGicSG1prchrQzh/b8sU0W5jSOVVN+CL3U2uNaTZoZXi0WXN/tRbDthB49uELrm56vvGb32XcTcy5kLSheM1+HylDxtiZRkeUV5gsxYhsHUNwqHlGkWlXHcM+YByEaeBHf/CKxesn5GlGa02zaIVNqWdKzswZ1g9P2fY7YsmYVMDc0VBHCtAhJKy2GAxzmkWrPHELQHwcneHQ9XJchD95HIbnJ//tzutTgpgLRReWJy05KFRWWKV591HHt4YbooO2ael1YKGE2R1jxCknY9mC16CyrBehFnDXthXvkpSJSeZkiFI0aZYdb772BvO8Y9X6ahi4FRPGlAhzYDdNLBvLqlljjQeVeP7yPby/j/cd2IJzD8l5xnnLw0ffYM4juXhS1syxsOhO6ftEzhMGWPo1SkFG2NTKeYxpQRkwW+awZamWmHiJCq9w8TH3HtynKC2SamVmvcjE2RGiJkw7lNZ44/C2YDYWrxqUEpm+RgcywmjXKqHzDpIlKLnGNGmUtagcsKbgLGQiSp1WJvpISjM325dY62gWG9x0TdPer+SGQs7XdJ2jaU9oF+f0+w+xdlk7QyJrv2QchCGOKhhXGIYea5dYuyACq/VGPAfmHm1aFnqB8ScUZBuapwtiuBFwXDXkMkm3Aok570n9njk2aOfIWhHSDa6Iqf1YTe7P1+e1C2kgzZ6FNxivUTqx22+5f/4G87xlHi9oTx+zvXmXbrHiZPUYrTS7m/eFIFAy+1GzWGwwpkEkwwpGK2yV+cjB0u92ZBrQhnkPffyIk/U9Fv4EZ9bQWVReUlImTDPazqQg0Y22Bcsp4+4SpQMLE4jKE3VCN5qua7D9wBx7puma3c0e/AmNmbDKoZTn8uoDvDvBug7UEtSXQO2Z5kSMjTB26XGm0K42NGZDjIp+umQ/Jbxb4ttHKHVBCjtCvyVjpDOijIz7S9I0SteXUZTJ8N7VP2G9vIczjrLw3FzsaJozmq7D+QZClQSxgTnPDFdbmq4wToVhCGzOLaackFNmNpGTxQOurl7hbEfX3sP4hmm2qHmLLjPoyG77hPX6Pr55zH53yf/qP/kfQxyYLl9gG0MfAk0JkmvojkW3Fim2OFFoWK42LBYdYeq5unxKCh7XWNqFQ6uGkGb6fsRoy6I5I9Og4gzWkrRmnC6w3hDmmXkcMcaLGbprMe6E4eolQa2le/FQCO4z8BLIBCJenzCMT+mHnnn20kl2x3T+k0fOmf1uh54mpmGkTDPJGeYYYT9ydJdGije3SPVnU9pTydxsb0R/PSQCPdEIYzXngyeW9HHEXKRTNoskY0mH2BIh3hv5k7UiqUIIUVjs5XYPX3dQbCEaiV+KkpLZYeMwxpFyrPGqQqmGlDTLxRlnD18TE+4ouYVWitPTM7Y3F+RyiUB80rV2yKFjPpCCDvQTRQoFisS5+UhSkYpxjAcika4yoIqkhM0u3Zoa7bwUHoCkNcSENtTOSMNmuSLGRKmaZl3Xcu9kQz8MxFxonBXpw1kxKOkgTNbhXMa7kVQMMWvp5Kj51hQLZR5QiNSOdp7UWK5s5vefvuB83fA7//U/oXMNNme2VyMPT6FtNVZnVA5EFGAwjUVFh7eGL6/u89W3HhOIOC1SfSUXTNMyhwAk6Rj7pXtYa2vulijzxBThZpy5Gl8QdyKrk5NIqQ67icXScb5ccLJcYJcLhqvIxfBfo4xjeX6CyYaryz2l3/LW/XN2L9/m0Qrub055kArfaRZ83W/ICv75/IrUeoKKlKJw2rNSjilNpJxwKRNyR58yu90O3V/QrhasF62w3qcoxRsPfZy5GmbGkDEp1657TSkZ5Q0lZHLIXMXEdhhp5oHr+QZ6g4maEHvCq1d87913UFbRaINThvyH32exbKW7qBZXzk8e4bxBWUOynrOzDdlvGYfI7/7WP8JOcBX2XDPzPPYM+573PniBN5ZxCGyve5xdQLGsXYd/rPjXP/qI676H9YL/WfgfEUPAaen+3fUzdjGzf7VjGmfapmPXD2ijcd7zFx68wV/8O1/lwVe+yutf/SbFZEipSogqGm9pWy/32hn6ELm8uZau/z8B9vpTl4j5vMcnGao/D/v501ivd3/+s7yWn3a+/+b4+PFvqzzKzzsG/7w++9/W40/3uxQB2ouB2lqrrbQlpiwJYlIZ54WlqOqia70l1Vb3g2yG0qYSYlXVvdTSnhwi2lRN3wLO2do2LO2Iznlilf8AYaSHGCWE0wcmj6bUVqMSZgFSirSc5ZyJiHRNLoVpnrDWcXJ6wjRNxBS4uLzEtmJQanTV31PC1hJzTGGWH8wwDzIxhyPlXN2zhfUbk3CMCsKKnqaZaJK0Axe5R8KwFDDhwIr/ZIFI1yDtAPoetMnlOwsYqiiQJfhM4i+LtoqubVBWE3NkDpFpKmgsISZinOknATeMFnmYlLK0PyNyJykknPaUrMlFEp4ChCQGW5lMCAFtFPv9jsuLC+Z5xjrHcrFgvdnQ9z3HVswirOj9riem2q5ujDAAtaZxouceQjje2wNb3zqNc5ZxqrJeRYwwc4lVs/ogC5OPf4R9mquWu4zldFdfvYKGpd4z0WW/fd+hzVfokLcSFwcGu6KgdKogs8ZYxaJr+drXv07Ome9+97t8/etf49XL54z7PT/8wR+z3d5QVObBg/vM08Szp0+52d0Qwsw8TQK2ofjmN77OftcDil87P+cHP/ihBJIhst/t6MeB1jRowDvHzc2W9955h3Hak1XmK1/+Cl23YJoCSmmGYWC92UAO5DhQKMwxVPd2kZxIJaEURy8AchVKqgzSn1agO/xOdCKTmJsaxeHOKaWY43xkqCulhM1eP7eUWOeeAOySpILCoIqBYimIgWZWBaVk/GRE6iiVTEFY0toZjBWATBmL1jWJTIGlE218YwpzmFi2Vhi/iHGZyYVV4+m8oWBYLDzOKqxSOBTTPKFdnQfzzJSr7IoqoDWvXl7SKCjFMUyRfpxIJEKG7RD5o4+27MYobNdSyUQFZiBkRG5D13tZNHNMJK2IORFzJOUCw1x1mWV9vYkzJ0VkEJ7vR0KvJYBVYGdkDk8BmDF6z/2zBVFpdC6oaWa18fSzFC+SV7z/akc8SGwd2OumVE36zD//1+/y5GLLdpgZQ8IbxWaz4Hq3Z7i4AFVIh64HdSC53SbcsRTCmDBVZSWphLUKVWbiFJh2I+Z8QTQWcmEMI223pJ9mxijMzNZZFo0wuHKdxyINIV02pCRarggTTqSGEJD9wKwvphaPSp3it5JQ+TC862UfZBukq0MA36QUEWE3qRhorVxvTuDblhwDqShhm9YCo9YigaSKnCdnVeW+RIIl5YAp0LSeYjRjkCKD0bJuN96hsBRdxOzVZjHAilIOMCpxtmn5/7H35u+2FPW9/7uqeljDHs88cUBBRYKAA6iJGuMQJc6igopzSO7NvbkxX/MH+DsBNWa40ZiIEyA4oEYxj3qNAceAA8oseDicc+DMe1hDD1X1/aG6+6y99hp69eq1Vq+9P6/n4eHs1d01dfW7qj5V9amyY9pT253BwlYFqczhcHMLFfihqXvCEsYFTRCAQcOySzi+EqBZW4ZSCk55EVox2LAAps2kRKggLBtMcCjOjL9fZnyYh1LD0wq265gJAMu4QUOk4VKZNpgzY9A3Kw9tqMiNTqKr+oxLosSw3qo7cZ3UiFbIa6PR8erahDOWdxO/cS/BGYcMfHAFMMdBaNt4zJbQTCEMgqiSmvbbEsa9EpQJmZvGHZwp2FrDUtoMNKP2TAgOSzJopqCYhmBmFaVrO6bdCTWk1JBBIzmLpeS6YLZZierLwKze1QwOD6F1A0HTTCIraDBmQfAyAB+WMLu/uBWCqQossQxf1tH0m9FqziYYs8CEBS1FUpAq1AgDH6shYNmzWNi+AGgboV4xZxhYJTBeQbXiYnllBY2VGpQyu+CkkvBCBU86WGk0ISzHuPngHIxJBMEqWGjeIWM2tPShVQhuMyjlgbE5QHN4zSYE3wpuRavDpQuveQq248IWJdiijMA/jMrsLlhCQMkQXughVBxBqMC8AIxV4XkrZvKV2/CENO6HIsM/s0KU7Ojw86AGxgUajToaTQ9KATMzDL5v9ENp0z+17UrSD5BhCG4pNJvLgNawbBfa5hCugrCUmawozUaTrQFcJhE5dgZjMN+JrEGCQUkOpgDBNJreCpreKhp+E47jYnZmF2x3Dly4CPylyAuGmViDlGAwfWmtQzMJbXE0/GXj2sB14JYcyNophGEDmldgC8v4pJcK9eAEQhUiPlzZshh8FYJxc9irhoVa4xT8sAEghBAajjMLu8TAmQumq/AVUMYsuGf8epcqOyG9YwiDBsKQo+psiXam2NBKIAxqWG4sgfESGCtBKxuBqkNzBW1bQKUMh5chpZnMCWUDzDseLbIQ4NpB0DwGwQS4NYMgFFhdOYnFxQU4VhVBWMJMRZn6pQEbFsrlKhr+aTSDE7AaDPPVs8AtBdtxYLE5eKvHEEpl+mhVAagATtm4vlFaIQjjfooPzgPMze7Dk088iFBaCPUstMdQLc2AKQ9+vQHpr4DxLaYfqQAlbdjCNsbfyE1Mo+6BCxu2BXAmEXo1rNRNf8F2ZoymqyY8vwGvWYNwBFzHAlSAIGhC6jIELxl/4lKZs4xiVz2lKvwwhNQlBL4P6S8h4D5KZXMosdbGZ7MlZk2zxzi4o+B5HjhbgFtaRKCXAV4GWOvuxdYOHcAER7PZhPY8hIE0uzAFM8Zc6YNbDiLfj0C0sKEVpSWYDqPzJcyCKiUl/OgAT/PdMQASCtE5PmFofHFrs8hABSGUrSE0AKkgBINkLFkgIwSDa1twYSHU5qDMaIBlvmkeGteSYHCZgDn+9My4y3Qrk949tA6hVWjGbkyjvnwMNjPhSW7BbzgIwyaUDmFceJkeQuv4gMWLdRD35SIf7PzMOUewrDOGeMbAuBWtSTHPGbedLOp9ANwSZsFLqIyRnqvojBAL2xfmcfTkafjSuGfkWmLX4izkXAlKK5QcB6VyFY89fgSrK8uYnZuH65YwP1/G7GwJQaAQu0KF1rAdG4EX7ZCGBhMcjhCwhHGRU7ZdMBs4eOgRhHUP/qlTZvwCDos7gNIIpVlIAw5ISJRcx7iMYwohAjjcxCm1hlQMTPmwLBH1KRh81YTW0RlkjIM7NrgjMVtyUVUOZKgBzc3YV5lV1WXHgfJ86DBEyRFYadbhP/YApNRY4mXMrJ4G4xyBcPDo44ewWjuNXTMzcDgDbzTwezsXsHS6jlNeAGEJ/OznD2F+sQq7bEEKbnbA6tC4jWQWNFZhW8bFqa8kRMPDkr9sDtbmDJZThgsXNrfhCBcWGljVTUitIBQD1zYa9bp5PxUbS8tL8ENpXJKF3Izfg6h+acD3JRzLgadC+DowE2wc4MKB1iECbwmeDKMFjgxBEOAAd6AF4JZLOHz8CQgmcKzWREMFkMzDwrbtCJkwix8QwqrMotkwrnCYZaGxWkMoPHAnBIPE4d/chy3btsMRDLUTR/Gf992Dc3ftgG2ZswjmqgtYqdchLAHbtlGr1TC3ZQaqUYOqrWDpyJNYrZ0ESrbZmcuBX991NypSo3b8GOZtjqbNcOHT92Pfnm04tbTcWZ8woIF9WFcbvUizYnVQP+5p0pPGvcYoV2JPyohbJANu3unIK7wsblryjruokw+DMLL4IiMkZ8YHbLJy3RIIAmNUPjP+jXzERrPy8fPxfm+dpNN0NhAb8mBm8C3bjrYqxgPmqNPBjJsalSyjjQ7DYWdczCCa5Y+3k6vIXUU81jYrrxGFo6CkhrBtuOWyOd3aY2g0GqjaZrAYh8MjQzsiA6FxXWEMtvFhpGfKP/bdfsYorLU2DaVWkFJDcCSuBYyrAn3GH3gUbmxYZi1lFpnQzYr5aBW/6bTp6OwU4w7DHP0CgDFwLmBH/eYwlAgChcBnsIQFGQJBoKGDECXHbB3jzIq2WNvGzQc0tIw6MJGrEC3MQYZmz795z1oZv8NBEGB1ddXUEcsyBmce+zaOV4Abw1QQGFcwnJvVEomrIdtKVqPGq/TjiQUujGuB2KgY72Zg0OYEcph1pvHq9dhwFrt9iQ3sxqepWpMuro0RDlqf8bscG3eiyhy/6zOumswBi2bCSKFUclGuVLAwv4Bdu3eg0Whg67Yt2LlzBxyb4/TJEyiXS1hePoWjx58EtEa9VoOG8bHItDHg2JaFhbl5VMplNGrGvU+lUjLlGvlA5VxgYWEBXAIqNC5XVpaXceL4cTAOLG5bBOcCQlhwHRfVShW2bQMWwJkNSAuV6rL5PqIDsxg7YwRl0cHCYMwMFJLPef2EeqsmJ8ZJrQElYQlz8FS8AsYsLNKJ79jY+KWj3QZ25DJBK7My3uLRDoLYfhb/I/qvZUgERP4qGQMswWFbAp6SsIWZ9BMcsLTGlooDkawiVig7xgClGIPvAbZWKDsCFccc1GgLE34ozffQDD24zAEDgx9IBErDVwqeNAcbnliqY75s/Lwu1QPUPIkACiHj8JTGoWUPJ+sBvEBCSg3NAcatyOe5OcBLQ5oJAcYhJQCLwRIaQsRtC0sGzUwINAMGyW0obqMZaviBOX+AMQaLaVjKHIxo/GMrVGZKUCpytxOGWPYCyKY0PqUdgZO1JmTcjmkVjUWj1Vmhxn2PHsfxWhNNaXZ5zJUEZubKqHsBTq/GBzIljYjxm8zOHFSptIYMzU4ExpjxnxlINBs+EGjUlmqwKja0ZV6+DEIox9wTSA3OLXO+gG1BacALzMCdR+8ZiIxIsfE9MiZLpRAqBaGjXUG69YQBrDPmRlKajL3jQ1KjIbLxrxpNsAAaMpRQEoBlwbYteKEPFR1KJuLZBvMhwSipNoZ1rc05Cxpma3h05oBiZiW++UzP6I3WOjpvQ0HLILIQmm9IWQqlkg3bdqCgwYWNUtmUvJTRDhFu3KJzEbWtgfEbK4Mwyr8ANIOUxkCCSA80jw5BZSyZPNORYZIDYMpMdNnRCkQw0/bFhmmpown0aAWfOSzWuIOK27+k0daxAaXthQCRGJyZeE7eTsukSIt0R0Vuys6O3MVxpSKDp/F/fqxiAfDNSnTGjXsWZspIhvF5K1E5RLvYYqdCXGnjHzju30izo4dbPDKKKKODXIBxs9PCHFBpHnEdFxKmj6OUhJQBbLsS1b0ASgbQxtoFLSJjGWxoi8WOS6B8DttuIAyNcS1QxlWasBhs4SCU4ZldSVE7GMgAll2GU5pDEAaQzXhXXcmcbsMdKDQQRi7neKQBymz6QBAan8NCSNiuBcEZpA7MakRmdh1xriJXDEYTjaYwhCGglRUZTxgUzEG+lmWbAwZhND72vQ6mjNsnZkEpc4Ag5y6kMi4auBAIw9CcQRO3R0qDC9d8I7GmQph3wYwRTqnoYEEN40tdOAAC6KTem63sUBpMRQfb28L4npfG6K6kBwZmDGLaSuZ/zFkiTWhmgcEGYwKOY0d6wWFZZXDuGmO6BoKgBq+5As5scGGDaQ4V1hEGlklXtCtMSQYVhsZfv4p9TZvzMSybQweRO0cdwg+aYAiN+6BIH5kSYEwCXADMRSBPRqusjd9vzs35EJawwVCGowNjbI9W2bpuGYrPosmMAaxcmkOoPHDLguDCuJ7x6uAihBASTLvg2uy8DLQCxDIsp4R4Z6aSPgIZQCkLgDDuf7Tpb5tFPRYCXoNtl6IJEAGbVxFqDWgJwTicUhm+NAd+QklIWQeEgIBjFrVE71QIG1a0QAjCTDowFbuEZAjDOuo1H6XSVgTNZcjQQxiaw1krpTKU9M0kKDcTq2bBBsCZcRMIbjz/K6kBFpq6z412y9Azh9YCsF1jiJYyjBYSGXeBQitoZQ605FoCwjZtpgwhHGF2CSLaOayBIGRQvgfpLYOXAOg6oBwobhtXU3DAuG12YbAAoRe5HuQ2OF+G1vEByh2I+meBHxitVyryxW6M4ZpxcNs1rlE4B+Nm926skUpJcG3Kw7iaMX0xSwgobc7/AOPg0Q4qzcziDBmEUMz0CxFG5RMtnoFW4NwyO7nic2gUh4BZfe0nq9fNiCN2RwYNaM2glFllr1uzqFRbvhWU9KBUAA0FGdah/SbAOJhdQlg1Zy9obSb7k/FNFCjnxgUoixpIM5eugbZyZjyajInP2WJxu2jaJdO+sZbGjEX51cbdmo781UfjXR0d5G5bNgQzZzCZg14ZSiUHbqWMkmMmgWzbQaXkYr5axZb5Crxot5w550DBtm2oIB7XajBLwIbRGXOeGYcHieXlFTRXV8AajWRigEWLcWRozgSJc21bVjTRrcz5UtFuWQ0znAyDAMKK+ojR4jsr8h3POTe+3ZWGZTFY3JzJwLkA19EiHMe4waktr6BZr8O2TT/TWz2FwA/hbtkJ32/CclwwNoul08cRBh5cVoXLNJbrdViCI7QkPBUiZAIPP3YEO/2tcGfLaFqA6waYkQEEA5SwoWQNc3YJnAGBDuELU3e4ELBcB36o4HLXuPVhDExxQMe7/Y1rPjPmNBM1UksIYeqv1Bo242ZyJj7jzmKYnalGZ+D40FKiGQTRxmHTVgVCo9kwOyeDMAAkYJctiKbA8dPHIWwHzYYyixG5xMxMFYDZ6R1CGRuAHURtmDCLehwGlzvg3MaxBx+GPLkEYTGsLp3Ak7VV7Aq2AVKa81+0jTD0IUIO4ZkZ7IA3MX9qERW3itpJDydmbczs2g7u2Kgtn8ah+x7ALFfQzTpcHWLG4ZipLGDX4gxOz5c76xNSGNi7+ZNu95ec54rvbmnI0+g4ijDTxNf6737l1m0FfpY0Z/EbP8gOgEF8mfeqO3mTh3/xNGGOasdD1nC6paFTvRsV/eIZ1v+9jjoxwrYgtUxWtjJERk8uULJss50vOvE5rtOBb/w2Mmb8JJpDR83KQD8MYPkcrmVOjG74AcrlKqQMEYQhvMCHZTsQtm2MRI7xx62F6bhIL4BbLkFKHyryZW6MtTwx3LfmT8vI1QBktFLAHG+omFlpaLsuao2mSXuU/nirYlw2tm2Dc77m8M0Yxkzn0fgTk1GckcGDRYZT4aJUchLjb/xcHF68Kj4MY2MRX7OiV2uFUJrtjzxyHeOz6KR6acq3WiqhXCoDCmYliA7MtuBAIfAVtLbQbAaQoQbTFhzHxtzsfDSRwBCGGiXXjXzYSdPwMxbtEoj9l5vthtACSgWAMKsUpdKwbCeahLHh+yFOnDgFrXV0kKyAZZkZdcbMQaaWbXzqI1l3qNGMfMYrHRtczLuQngJg8tlseognNAS3okkKCURpPONfHdG/VeJT2aw4Ukmn2NhmdeLbO6o0iA/S1GCQgBnMRveYyRUVTboIaB1iccsCdu3chW3bthlXKEzj+NGjuF8By8snEPpe5N5H4+c/vwtcWdixfQdm5+axb/deHDh4GDPVGWzftg2LC1tx6MiDOH78OJpNH0eOHMb2bTsxM1MFFwKz84t4yrn7cfzJYzh98jSWlpawsrICy3Kwa9dOnH/B+Xj44YegQoVKpYI9e/Zg7549OHL4EEpuCQtzOxBIDqdUgrAEuGVWjPKoL6+VMSabSZ7okECtTeevbYVS/O2FOowmh8zERRAEEIzDsqxoIo0luz7MRFIYDSqMYUlLDQhtBgrRpJLNGZpSGsOMUlAIEmMdi4x38UpjxmCMZjDbf20GaKZhcUCIEJbFMONY2L91xhxapRU8X8FmCk7FhhYcS6shbMVRFgou5yjbFmTgQQLwA4Vm3YeyGOaYWanR1AKaMdRCiZNNH1it4cnTy1htcvihwvFVidNNjSZjqNgCKFlY1go1wdAIAU9J475KG1ckkml4CCM/9aauWbYFF260dVeByRBWxQGUcXfiWBWEUGC2WQVrcQe+o6GiFUmaA81z0JtRAAD7MUlEQVSgmRiQtQSOnqxFZyIArgWcPLQEGfiwGIdju1hp+sadhpndMh9JdNBpKBkOnFiFsixIpQAdgokSwjCAgoW6NocnRqNso3WsZV1xZPxkzPhdjye6jh1vAIFCyQLqqwHskoZlaVgWh2XPIJBV+JJDag1hmdXRxiAZGZOiA2E5Q2KsZTjjA952bNSbTZQ8AcvlsHR0QGI0KSPXjH+To3wjQ1+LkV2b9WScweheNHlbciwsL9VQLVdhWTZEZIyWgQetFVxHwPe18eOpYYxnTBnXaQC80EfZccxuDKXgQRnDiG0bg7pScBw32g0VwPd8zM26aDY8s0tDWGj6vjk8mTMoCFhSQiIyjCoNFTTg+wqIdnjoUMGybXjQaHpN+CurcBf3YWFmBlprLNc9LK3WIDUHEMJTTWyf2Ytm0zMGKqUhtYe4uTVu7Fmyiys+6JpzDivqN9R9D1Z0sGsolVkJGU9sagWz5//MQD0xliYTnCrR6PiFsOQJtL7EM94LYCaMHcExVyoBWqPEbYCZdtmuVLG0bRbaq4NJJG1PPKHBtYaIV4XqyFWQVtEkpoAKNZhrQWsGGQINJiFtG8x2wMslcyAdbEBYcCyBqltGEATwgwaMu7MygkACOgCHQrU8D85KaPp1SOmBWwJ+yGBHKxk1AghWhubG1ZoQHE3egK1KUGoWUCHq9dPGXRYAxiSkXgX0TOQXncEWYaS7AQKvCW5Fhiom4AgBPwixdPowag0LUs+i5Gpovw5L2AA0bL0El1Xg+zUEWsKzZjDjGAOzUsbHsqUZSuVFcOGAKQsNfwWAZ9x/sDLAzIrz+AXOzW2DthxoaHhhHW55NjpMPYQXBOClMrilIIRjDmYX5swOqRSkboKFNpQlIGUDSgZmkrdcBbPLEMwCZw4WnGo0iREa/8ScgwuZ6JDvrwIIzBk4TgXgFsrlWSgZQkrfHPDOHEAxKL8OJhiUrEErBvCKOduGcWMoVB5CpmBxB7ZThe1WoHUDXAuU3arpeyiO08tPQOvj4HoVYdNCpQIw7UJrDr92DFptgVPeCsuZgdJAvaYgrBJKloTfbCCQPixnBm55HpwzhKeOQaMWGf2MFjnunDGWhE043DXGbGZDOFUE1qqxtTNmFpgoC1AOIFxYlouqtQAVKnBuQ9gzkP5xzC2ei0pQQ7OxBMZsBNLsbBHChlcP4DjRYg8dQjALbsmGlAqh9FGvHYLN5kycGlCBhCckHMbMGAENzFS2wAuWAaZRrc5i1uWwy9sAIQBrFfVlBSFsM9HBAwhRwfxcGYJJcHio1Y4BcgukakCjCeGGENy4JNCwoJkNr7EMywIsm0OqGmyb49TJJSwvncCJ44fh2IAMA7OiWtpYtSqwXQe248ItzUL6HKvLTYQhYFddaMv44ZaSw2sCs3Omzx4q4yZSawlmWdA6hB8sQ4cCCiEsbsOtzsCyGBpyBdxyUXbmoZt1hNzsvAllCMgSlDZutpQKUSmXEPoNQDUhWIhK2YIXnAQXZQg2gyCcRRhKMMcBYwJ+oxZNaq9CQ4PrOmRgJzuDO6Ei9yxGUM3OVR0yKMHNTkG3jAAMjAtYQph+itCQQROh58HmAiHjYJYLp1yGZduYKbmo11exsrxsDLoyBNPKTIApcxYTonG09JqANt+rDjkgTZ9IBT5ktNkIDQbYgBIaoVRJG8GgIVhoFikI05XxA9NQaXXGPqWZWjMZC0iEfh0qaIIxDXemhBO/Ow6lONyqjYU9VYSrS9CwYAkXQdR2xYuIbNuG7/lg4sxY2axO18lkPCJf8EzHC9U04nOFwM0YQ0YHynIhoDhL/LJzzqF8c2io5kBT+njy2Ek0mx6cUgWV8gyEsLF8sgamQri2DaE5avWTqK3WAAYEYYgAZpGWDkL4nge3FE2UaAYEASzbjeqLsQEEvpkIkkyb/r7SKM9vgWtV4C//DrZwjPtN6RtXa6GFeGGaiNwwBl4TUprV+D4C4yqOcVi2RrNpDn+NVngBkkEynhzw6TougprR6oBrIFDwuAfBBCxmw/YdSBGCMQnuMgTcw9b5RXhLIUJmYcf5l+Dgk8cwW51B2Z5FRXuw7j2KBcFQ4goP11bgnWzinAUL2+cs/HbFw9GlOvRqCbaWWHUA3vRRqdeM6yTLQuP0EuYDjQq3UHLKWJydR71eg2IMwnVRrliYK1VRch04rg1X2OBMgFtmnAMWolR2jGu5mgdpVzFTYtCR+9zqzAwqro9QhpBQcLmNfTt3YKW+itXaCpqNFSw3GmgGDVilEnbuMWPKI7/9HWrLK3DnZmGJeFe3hFJNMK0xM1cBZ2VoCWjfR6BltAgHYDLA3LwDphm0ryFmS2gwF0xbcJmLk795AEeCe6AtBiUYdu/aj6c87ffwuxOHceDI4zjRXML2cglMBuacBgYs4xRcx8XqsWU88rvHYb/iRXjG9nlUUMbRAw/i5BNP4rR3CtyvQzc8LLgObA7YJRfO1rmu+tTXwN7NINdrO/aoVyTnEf6o09wrvm7G00HTNIjhu9u9g+wI6GXsTJOWTvkeZ7lnjbPb5MQgcQ5CHjsaBnmvo6LfpMMgdaYTxqe0vWbiJgyjlVBA0thv27YNq6urycpgpYwhVCsJpQHHdhEqc1gk4m9Fa7N6IxoQN5tNhKFZ2ewFIXbsnEMQHSBqfHibVe7moNUz98dwzlEqlQDExn9jrG6dxAqjVc+aM3i+b/IWGRUXtizi8KHDqJTLmJ+fh+u6qNVqSd6bzSZmZ2cTo3sQBMm1eDV7tVqF7/vJfwDguq4xwkbb2KSMV1mbQ1KNz24G3w+SQ0/j9Mbhxod1OrYDzs3qmyAMsdxYges4KJXKcJ0SlG98wFrchrAs46vR9xAEEkoaY6NllWDZDiyLo+Q4kaGYw3ZszMw6qNVqKFVKZqW7DBEos3qEMQUBjYbvQQmRnPAtoxUt0IAtLJTL5bhCAoyhWq0aNzzRRIJSCtVqNZlAaPoNKKnNVl0lo0NMNXzfR+AH4IJHPgo1AAXP95JV6GEYotlsGN/r2hh3zfs/M2A2SVFJktZNajKAqTOrElvR+oyRRgiRhJmcPwCNaqWMP37FH+HJY0dRLpVQrVTRbDZx8OBBnDh+GpXKY1hZOQkdhggCDyoMYdscT3/q0yCEQNkt4+ynnIuDBw9hZdlshfM8c+js/OwcLNHAysoKDh44iGa9gdnqDM7Ztw/33HMPFmZNPS2Xy9Baw7FtlMtVlMuzeOKJY9i//xzs3rMLu3btwi9/9Svs3rULjXod993/IJ44dhSveOUrUaqUsVpfheO6ZnAdr8tkpnBM2xa9zuRvlpRjXIatOwMYQ7QKEeZQMa2iVWQt65qjZX5KKjAJ2NFpVGHgQ0oN3/MhGGBxY9yDBByGaBWjMbzJUKNs28nJ8xVHoMS5WYHuCpSdWQgZYKZcwkyphErJglI+mGVWcqLewLb5Gdg2h2Vx2NYWLC8vmTMLLAuMKQShBwiziqPRtNAIfFQswHVsCLeEphtgK9OYKzsoV114Cti7bSuajQD+4VNY9CRsv4EKC+AqYMaaQa3WgBWG4ExjYdbF0vIqSo6DcqlkVhVHbkw0FLxmHQslBVcIWMKGtkpYrdXhSbP6Z7ZcweJsCVtmOCwLWKhY8PxmZO3kENqsfHFtG4xrSDSxWg/g2BxMcqw2Acf1sW/7VmyddTHjKvznPQdhdh4A8VZrQAOCQdkch1c8WK5n/LxqgQeOLGF2yy4cP7aKhw6cBDfrciLjaLSyODJMW5ELJA3AjwwFUod4fLWGUzUbVnT2xe9O1dBoGgO6ZWs8+sQxnNYawrKTCZYwWh3OOY98pUdnLERG3Xjxvam/Go7rwHHMLiXj8CXa3q80AinhWmbLj9JmYvHMOQ2xYVWYlYORIcgV3Ph0VQpSNrGwOA/LshCEDLVGCIkQFcesVmoECjbn4NocmcyYmTwNpQITFmbmZsCkBhfG1QuLjlbW0AhCicAL4VpmJTSYOejUsmwEWpoVgtqcZcIUwIU2q/99Ccs1vpuVCsFUE17IzCGewrhNWVldMWdulCrQaOD0yWNY5dz4669UoOeqOHHkGJiW2LFzC1ZWluBwB64lEM+1iWhSI4h8oQrmJH0EYVmwNSAD3/jttGyw6N03vcB864GEVoDQInJJFetKpN84s3OjVUPQqkGtrLW4R+/UrMgPQo3G6mlz+B1nWAqXYZ96DOAhWMWBFjbcStms4C25sEplhJqDwYKwLAjLQaAA4ViQCgil2SoeagtNX6LhhdC+gu9JhFIC8xVjlFBNWMIx52roAI4toJRAGEr4fhOnTj2BysxWlEpVKEj4jRq0CCLDtwWXAyqMVsbKECI+AFdpSGmh6VvRim0Op1SF8hRCHSIMAoR+HaWZOeOKDwyW7aI0W4FXb0KpJhqNBhpNBbdSRs1bxWrjNHQQwC47KKkaoEI0axXMbd2D0DuN0FuCbXFwR2Jhdg5W5J5ParOqnXOGyvwiVAA06jWA+bCdKtySMOXMOFy7DCU0GkEIwR04VgmwBZiy4Xt1NJs1zC3sg++dgpINKITw6g5KbhXm/OIAjJdQqmxDKAPjXk9pKLlqJpOUQqA9aM8sLAAs2A6HZVUiF3lh1J45pl7pEDKsgVslOPacmRi2NDxPmgPshenDSalw4slHAShUKuZQcsA1OyOEgieb0MIxhzQywC2dBeFYkTsgbg65FGY7vgqNS4W5mXlI7UKqrSiV5hGExwClwHUIp2zcDBhDdBMQCpZrR9+GmVQUfMb4J4eE9FfgVPaZyU0J2A6HVgvwA8/syHLKkPDhiFnThsLHlq1nobb6BJQKIIQDxsrgloTSDdQbDVj2AoTtoOxwlLVGs2kDooSSU0Z1dhuCgKO++iSYEBCWi1JpF0LZgGWb3S6NlVNQ4QpKla1g3MbpkzUgXIaIdhIo5cMBg+QhJAO4bEJyhlJl3hwSzYHQ3QYFCagQFsrYvmURS7XDMGdvLCBongKYgmLGVU2lug++8oyBEiWUS/sgdQOMSwjhwmYudMk2bmjA0aifBFgTs7PbUC4vwrFMn9xvNOCFdXjBEspiDlxzSC/AqUYDi/N74FY0uAVoeRorqzVUq1sghA2tTmN1FeCoQnAXMmwitBl4WAFnCkIowJpFwz8FaA8QPmorARxhdqX50gcss6ONcxtaCPheEyXXNjvepNFb7TeN1lqz8OtNcGcWghsf0PXagyhVthsDdVg3u6iEA0/Wo1X4MxCWSvryHaWUa2hE5+lER1ErqQAmwbWCbdngWkFpCSUDhCow7uE4h1suw3JczEarrZWUqDcaWPI8BF4TKtp5BhXCHE5i2unAM4cGx/vgtDJ9CS44uNYIGQe4Dc6N+5hwJQSrcrNgJ3JDqqN1+Zwh2l1kGgYOG0AQffOtjU0LWhsXMfHOYHcHeOkUtC8AzKIKC/XZKhq1pWiHFoflOHFDA9/3k13WYCyarG09rDue2F9rr+EaZpIh6q+waIe3OaPCGNoTg7VtRz7rzeTD8aWTUKGGVVuBd/ooHrfsaGGQMu7LGODYZkyolBlvLZQZqq6FklvGzoUFrDQ8+H6AUEpozqEDc1aVYDA+zMs2ao0mVKiwZX7WnH2kQqgSw6p7NmbvfgBbts1gbscCvEYAlzFsX5gFA3Dy1GnUZRMQEkKYaXQZcDQVYNmAUzKL+FSoYAsB13UQ6BDcNS5hGedoNmqwGYeEWdzgySZKLDogWyv4gQepWOSexQEQohHWsHfXdiwu7IK//ZnYd855OHnsOFZPHoVrcSxuK8EXwDKA7du3oHb8t4A/i4pTxtl7Z/DOP/i96CBziYbn4bGlU6hW5iBDs+K9sTCLlZOrOOUHYDLE0VoNEEDgh/CbIUpVBw5nQHSiV7VUgZQSju3AdYyLzy2zs5gpl42rOMFg2cLsYBIcQdPGbLWM+DQwy3Fw+ImjmLUdzDMXXrACrgNsqc5jpjqHHVYZJw89AVuHcBygWVuCU5kFtIYjbFTKM6j5DWjZNC4ppRm7sICBMbNYMlQSSluozFQwW52Ft1THiWMnjZteHmK5dtr4j1cCmlnY6szgwMGDCAMPu0QVkBwNZibgmG1c9yxYczh0YhVHeAN6xkLj1/+Nn97/SwS+RO3IaSgEqGiNOcvCtrlZBL5EoBSYlhCy2VWfBjrkdJAVzb3IuoJ2FCtvJ52WUbuI6XTfIKu681hNnSfdVv/nFW4n8t6dkXcYg6wSH8Xq/UnVHR4dGBr7H0/iafmPc47Tp0+vWTXOOUfJNgc/hdL4bpVaJqvRjHEshLB5YrAzq7Ij9yzRAWOA6f4EYWhWbzNtfNFF9zuOkzzPoo5MTKlUig4pPfNbGMZ7+MzzPDJoM20OF929ezdqq6s4efKkWYlsWYkm27aNMAyTwzdjwzdrSUscX/y3EAKVchVCWOZgLa8BxszEhRCWOcCUmQNAY9/qiX9qmM5RuVSNjOoKtVoNUsYr3hVmZ2aj9xD5upVmpakfhGYQycxqMmHZsG1zGB/n0RZ/BvhBE7Oz5nAcKQPYThmrTy6hVHHBXY7TJ1ZQqZYxW6qYVWXNJphAsuKegUV+CM/Ujbm5OXieh2azidXV1TUTEQCSv8MwjE1iCAOZrHYERzLRorSK/IZy+EETQWBWrgdBaA63DUM0vUZi2I1XT5/5G2v+nxjYtT5TF4Ezh/fGf7c+Fxnt4vfCuZl0mpmdwZ5dO3HW3j3R+9FYWamhXmtGHXcLy8s1hCHD77/wxfj5f/8MUAxOycJTztmH1eU6At/HEjuFldUa5ubmcN6556JULuOnP7sb1RmNMDSnw8/PzcFrSNjCQqlUBqAxOzODpueBg2GmWgUHw7bFLdiyZQu0ZnjGMy7AI4/8DisrKzjvvHPhOA6efPJJ+J6PUCps2boNjx86hKPHjiOQCla0gp0xFvl9jA46VRrQHIrrNe85/t7MQOhMfRXx5AsXUOGZlVEaZpV668QHADO5Is22d1cIQGvj57HqwLY5anUPWkpwMNiuCy9U0eCFw4r8Epp19goWBCzOTF6UgsNtuJzDjvwD25rDEmb3hyUE7JKLEuNgUOBKw5EhFlwbgjPYHHAsC03IZPcKXBcrXg0ly4VjO7AsB+VdW+DoEDY3A4G5XdtQcWzosoVzZy08xw8RhNHWZigIy8HpLRyBNLtbqq4Fq7QNjXoDjXoTpWoVzaYPzw/gSwlRdgAtYUOiYjNs3zKH5TJQ94PImKVQD+uYwSKU4giWl7DALPhWCLMeWCBkAFgYHfw6j4ZsomRJMITwJYPv2Th+bAWq4QFzJXjKSrT9zGQLYAmg7DCUHQtQTeiQwZMKdd9MGikl0Wh4keuVZFQLFg0Wo14uBBQ4AmhmJs5sx8WJ0zWcrPnGjUgYgokQljauYCragozcI8S+0hkzLlrAANvm5nAzbdxRWMJGMwgRr7pnACzO4AoLNuNgyrhoMO61ognMUAKWg3jArbWO3A5FeYjqd7yi37iEiAbv2vg3F1yiXvfQ5MDWxTnYrg3fr0er+xm4Y8PWAlIbw6wAg2M55lyQRh0OL5nDcaPzLUx7aAzxzDIH1IbSuP/RLPbZemZ7PuOAxY0xW4USzOHwPC/SfWNkZoyb1b/cgrKa8JsA0xIWB2Zn54ESx5NHTkMGTezaA3DhYvvWRTAY/8Vl7kAGEmEgwbVZYaQjf/22ZYGrM1qgtFmxHx9mbgmBlWYTFdeJDmez0AhqZsu7iv2/8riL0KLG0U6CM5b35H+xGablhIwz13FmglSBAzPzqJx/IRzfgxYOoAWYBEJmmee0ig7iteBpjQYYpA94gYwOkDPfkQqBQHnmjBnNEEqzWMAPQvi+hC81gkAikBJbG3OQUoNr42ZCqxBWyYYfqujgXTNpMjdThWYhZNCA61Sg7DB6v2bVLyL3GkqbFfXclpHLBg2lJWwBeNqHhg8uFBxXI2zWoXUIaI7A98GZC6/RxMrSKczMzkZaHLnkQgjLrpqDgrWPhjwJy5qD7TSglAfbsaG9FXDVgGMrcGZF5+Y40OBgwodmVuISQEcuZBQCKOWhsexhfmEGwjIHrYYqhOMsgDHAshhsx2h/IH0AxkUJZBNMaVi8BMey4PAStK7D+KEvmX6kDCM3GwGYMnVZy+icHMGgo0kIMAklObwwjNzshdDKh8VmzDkeABy7BHAHYRggDL3orI4KtPahdQjOBGzHwezcAqAlBLcRSN+4pdIKYRCaRSDMAljkiilk4GENinNobsFmLiS4cdViKejQN+WoXSgmwFkDZasKFTaglIRt74Xl7kzaXdeaQbN5Gkr7ZtWsMtOVYdAEVACuASECKGkOW2RcQqMOx3XBGUfoNyBEBdwybuc4AoS+D8Y4OLcBZiGUIRzhQjANZoXgXGJ56SgsEU1wWOXIT79pG4RgENYsmFDgHPC9ZUgvgHIcQADN+ikwFiIInwC0hoAPywrBuGkXNSRkGEDxAFzYsJwyZFCC1sycYaFKEI4Lpo0BXinzPTr2PIAQjGlY9gykNIZZzjiOnfgdZmZ3wLLKYLDgByHArWhHigewSGeZcXViWRaktKL6GEJqH5qZPodlMTiODyCMtJzDsubg1Y8DzIYQHFpxVMoLkb//enSmkIMw1FDauCGSvoNQNuH7IeoNDzu2LJn3hmhySJXNWTLa9GvC0AOP3JnE45hQBuZ74wK+F7lmgtklzAIXgpvJXzCGkls2hu/ALFRyuAXGReReqAlLCHCmwdj6xSVnBDTa9pb8G9GCCoXA9wFeM5PnloDl2GDCgWu5ABRk6KO+chrNaMFLrM5KaSgZJuFppcAjVyZSKeM+K+rXcZtFrokABbM6fXZ+FvUVBRX6EJYFqUL4gQ8wMxm9xmjOjIt1oczOpKbnRWOAuI1oX70eNQVgAITZtaEVpLQBrmG5IVZ1E/B9cGXqsNIMPHEJasYXrWO42J6S9HujBSvxOxXJ2Jkl5SsYP3PmTuTi0rbinV8SQnD4gUyel0xBadN/Chk3fZkwci0iuNn7xCyYs4vM2VXzNkdVGHdoR0+dBucicu0p4Hsedm7dAsC0baFiKLsOBBMIAuOeRGnA5RwcAqXSHDwuULJcLNgl1ENAVF1wbkErhWplFiUZItRNSOVH705E8yocShr3q7ZrdrVbllnMsFQ3Ex02k6hUStCqaXZtWTZK0TjFEmYnme8H0Mr09wUz7vW4Avb+3sXYuedp+N7tv8Lvvfhi1FZWcHh5Fb+977fYslgFczjqyuwS3FatYFX7kA0JzqrgmkEjRNlhmCvPQZQ0HG3GOIpX0AwBdfZehIGEH3jwvCYsbqPR8HF6ZRUhNFzbuKXiloDNGQKpIQPTXqw0G6ifakKfYNHqcUDYiHamCcw4ZezasojF+RnMzVVg2xbKpSqaYYCm72FZBrDLFSilUVup4fG6WXjY9H1ozVCpzqJankMozU6c1ZoH4ZTNmQRCw3bMpKRZ/cegQ7NoTjWa8KQEbzQh/Qa07cECh80taG2DOw64MO7N/JUVNIMQTHBYFRcrtRrmLcd4N2AcdRngdM1MBAhug9s2ZmGDcQVbasyIEnSlCoQSDaXxZBOoVGdQdUyf1pNnFlK2M5AP9ryMZFnD6WfYS2P8G3Q1ctq0ZCVLOIOsXO/2fPtkyTAr08dFnq5T0uQ/66rrfowzjG51v59roH71obX8Jll3YqNia1zCMp3wOG1hEBgfzy3PmOeEEUButnlpziPDAE9m8JMDFWEMz4wxKEjjboKZtQhmcKsTX3ksWkUsohn9uPPSavBuDbe1LE0nSCUHdiL6mzGGUtm4OgiCAJ7noVqtrgk3nmhoNba2xuf7/prfRRTHmfd9xljDODNGj3h7odaJ//X4+djXOGBWvhvjtCkWDUSrJU1ZKZht7EEYmsZbKsApgQuduJox78WUPYc2W+ssgdhVQxj64AKQCBFohkD7AC9DWOZgOouXECyvIIQ23X6twaWExSxjUNNIVqrLyAAupUwmJYQQifE78aPOjD9hs8rc+HcPwyC5HgQati0QBgGazYbZAh3lOf6v1ce6Kc+4Mxv/n0V9eX3GwB4VJENsfFfRT2f86Cd2muidxvmYn5/H/MI8du/ejV27duLUSePjz2v60BoolyvYsrgNS6dXoBXHju27oSEACFiWg2plFqdPLMNvNiGlRKPhY2ZuHiXXhWVZZiKDRatBhYXZmTkcOv0EoAHf93Hq1CkEQYh6rQ7XcTA/Nw+v3oBbcmFZNsJAQXCBWq0O1zWHzCwuLuLxgwfhNT0wxrG4uIijx45heXnZ7JyIqmf8Xa75jzNjvMAZLWrVpTWr2qO6LiwrMVzE36CSKvn3mXbC9O2gORgz7nY4NwaXSsUFgzkkUXABt1TByaWa2fIL4xPZh1lxbFYum3eudHyQoASsMxN9ShmflBZjcAWHwzhEVHc4MytxucUhAFhMw+YacM23YzEGwUzn0o06ypwDuyvGJzAiVyVuxYWA8bmqyw72KtdopArhyQCacTQqxnCgGYcNjcpcFbVVjRU7RKVqwytp1D1t3EI4NsIggKVhVtyUNRbgIAg4QmUOE10OJSosQKgk9lQ4qo4Nj0lIAEJb8KQyrkKEgOO4qIsAVdvsNPAkw4m6AIcPRwMiMAdMm/qKpF5Aa9hCYMa1UBXGwByEGk0pYUGhYmvMlizMlBzoFb9jO4JoEscVDLDMYacAwC0LodRYbvjwpDTvnIUocYWSsMxEm7DAokPFEOln/G/OYuOrjitbXOuS71drZfxtM+NCSMfGgiiTZwa5SAa5a7QgNgZE4SttDORxtFoL4ytbS7OiTZhwpTL/Gd+lpv1T0tRVwYwPYK0ltAyg4BpXVToyJ0eDcAYO2zaTsZwpaNaibcy4EFNKA5wb/+Ys8p0ujS91zY1BXkdnXQAscTvLuDATjDDts2UbndYy3lum4Drm0DFlmVX8XqginV37fuOJ27jM4jMXYl3lzKx0jicuGBD5qtXRquPkdbT8X8fVDzqxnafon6Pl9cIYWXxuoe7MQrIZM4pVHCzU8OJqE22jltKs7AqlMlvpQ21+02ZFH5ccfuCbFYJgCAIFKQOEgTS7DcLINYRUKFeMUR0IIaWC4gICbmRYVNHKNAbbcY0LMelB+pZx/wIL8e4JQJv3HvcPlXGLZHYJSrOaU4aJa5vY7VfsQicIQrN6WUo0mzXYtgNhmbMktI78IofRoenM1CcVncJszv5gCJqrENycnSI1AHAobQ5AtywbSpoJAWgJFYbQ2jJGWK2glI9Q+hBWGYzZZsIG5sBpS3AIwaFCGfmjlmBao1l7Ejo0K94Zt6CsAJzLZHeEig9Y0ApQ0YGJSkeSxSCYgFaBMSzBHCKsYsfN0eS+VGE0ccKA2EWDVgBkMnmrtDHyaBjDl227UZshgNAYpLSSkKECZ2a1aByeFuacBKkYmABs2zHGPW3OgNBxnwwAYwqMeeYsnaQ9LhmXNCw6SBvaaF9oVhIrGRqjrPIBZQ6q5owh9uuulAK3RPSeFcLAgy1mzhhKNUMQNCJ5Mx+v0tFEV6RZTEtjmGOmrWTCNiv3I00HM4fsMW7qOZRvdinJwBjngkZk2DN1y7JsKOUZQzKMLgHGLYICoIQLpUOI6LSLUGnY+kzfTcPsjOPcgdIMSjcivbCSb1/KEJw5sIQLzm34YTM6LJ0BzBi9tZRROTNwYYHzErQ2E/oSCiw6x4YpAY1VgJsDSAFAWA6UXAW3XAhhQwYWBHcQSD9yA2Jc2CjNoxXDXtSmmr1JoQSCoGl0QJsDzLUqIdTGcGu+IQnFovqqNcBt46owqqdaAdx2wBBNVEjjdg3MTDTblgsvHu8oDc1C44NeOABUtAo19rPSRUcT0W1tL+Nzdky/nVtWdEaSOXBeM/O9hL6HsOkZ9zZRvTWTSe3hnvElHr9fxOci2BzcsqGZcQmnES+wgHGzEgXhB4FZaRwEyQGpxihv1h0xZjS93gyjWXG+vpFohZkFJKHvI/T9aCW8Btc+6qunzbklUToYQ+Ti7MxY8kzZoe3fZ8pxna0EUT8pHmvrluda/m1czrDIPR4iF54AdDQZw0zfTcu4/2P8f2sWuyUBpAQ8L0BYCiAsC6vNJspO5JY1WQxxpl2HDhEGZn+rLQSCMIAOFaQloJmADDS460JrGB/6MgTXLrx4QoOZs2vMoeQcGuY/LowWSmnqRrxDz8ifqSNKcYRMAUzAC8zEqW1xaG1cxzIYt1xKGe2zOGALjUCZsOzqItz5nbCYhmAq0mrjjnZxxwzKHLCZRokx7JytYLVRQ01J8GaAMAjBHXNuiuswbHXKYKHRRWFb8EOA8ZLZMRU0UfcsWMpGsxmgWhFoBOZwV9uxYdk2dCjhS2NcD7wAlgsEgYLvS+jA7BxohuZMMQaGZsPsCKl5Hmbrdcw2PHOYfNBEM2ig4a2iXNbm7B+lwbXpy0plPA04jCMMzVKbeGGb7dpR2ZpdFqE0z5vqFbs91Qi9AA0/gJYBuGPOOIprBhfmPA8ODWkxSGEmycA0uNDgCCGYDVtwlLmLhlc3B5+DQYYB4Lvg0ZlSrluCKlnmaB1lxol2uWzOtIDZtdqNgQzsRSKt8bPTPaNYCT9pxu3GZBLkaZhOE9Y0llEv0taPaSkbpXRyGGXrymDHdZO/BYBKpYIgCJIZeduy4Ps+bGHBFjYkt2BzRL5hAQ1ltpgzI7i2bcP3oxPEBQdXxgDLhOlUBYEPx3XMVvxohYDtOolRP15RHRvALctKVkrH6eTcNMJSBgjCIPIbHV2X0qTXtlGpVFGtVHH06FEsLi4CMKuuY3czsYuQVgNknIbYuB7/HYYhvKZx+yGEDdc1h40Z44eCsMzhp62HpsZlGB+ourKygjA0HUcAKFcrgOYIZQDfNytdGWOwhPHXW6s3zJiHWbBFCbCMj1azANQcLsugYQsLC7OzaDabcBwHGgpLSyewZesCmjpAvdEAd4ybisD34No2FuZmcfSJowiF2aYXBqEZMggzmIXWOHr0KES00sKsKjiziyAut3i1vpQKgfSjht0MlEOYbe2xH2Xf91EuV9H0PDQaDcS+b+MDYTlnCIJ4sgfJJAVwxpB7xkrYWrdjCxMzAwUVG3J08izjHExEBlfLguu6mJmZwbnnnovZuTlUyyX4foilpRU0Gj58zwdnHFu3VHHOOWfjyOEncfr0MpaXV7G6UjO7BCwLx4+fNFvGhQWlzESHDCWOHn3SrDBSCkunTwMamJ2ZxcLcHO46cndkaOBQYGgoD1pqbN+6DbOzs1hZWkK9Xkep1IBlN/HAAw9hccscyuUK6o06nnH+M3Dq5EkEQQiAYXFxC377yG/RaNZRnSmbQWEyqGeJHSuZLMMZXY/ranImA2fgUecpdutkOS5c10UozQrWMJTrV7TDGHCMywxjTJJKAUzDAYPrWpitzppDS4UNzsuoN5rwPA3GBCQD6vE3qTUCJeFLGBeOylwTrmtWRYPDsy3oUJsdKFJDyjq4ALhjg9sWuC0Q+sbIpLhGk/ngFRdeIOGFGlwyhJwDKkSoTTq1DuCHZjW0Aw0ghG2ZA5j8po+StuE6zJwbaZuJDCVsaLOeHWho8KaHeZtBbymjBA17toJAlxFEr8TSIUR0sN9JbxVzVgmlsgPbEQgdFysAVk7XEPgBzn7GDszbgLQkFAOY5FhebUJyDsWMn+jVusCiK2BZHB4EHj7pgdsOHMbBAx/ab0KLUjQwSqyPKFkCi5Uy5uCjXLbRlBqrvoQrBBYtBXuxjPrerXjw2GryvbUOGrnWsBlQdW24cOFxs/VWw3TYlVIIgwDcuHlGKDXqYYCVWgPlmRIEi48nMwZBy4qN3+qMWx1lfJgLIRCGkYsXBgSBZ3xpM5YcvhzXZxGtFjID5TjNMCs+pUzOuGAsNkCZszYYZ5GR02yv9ZsNVKplVMsuKq7AihcCwoVgArZqJLoVDW2gwSCjQbAlhDG6cwYOy9QzAF6zCdu2USo78BshLGHOw5BSIgwC8x1IaVZvCgcWdyA5g2IKoa9Qdh3zvTINzQSEbSGUZmu9xRTcsgMZACoIsFyrQ7ESFrfNQDCGki2w6q/Cayg4lo1qdRZBpGFKayihowNAzbcPrqM2VpnJNQ0A0UFrWkMzBVtY5mBzaQ6545xHBpbYgM47Gz3iGYfYorJG3yMpj62GSEw3CQE0TjV8PHTwJNBk8Fm0Wj00Bp0wMuZzbfyQCmFHcZg205S7Od/DYg6YI2AxZtJrA7Z0EUoNHiqwpg+hAwgp4ZQcgNWhAQQyhJYCtlyAbfHIxZEEh0KoOTQDlApQq58ErwgIbkf1LYwM0cZAJyBgDodFtIK7CWgfMjQT8JwzgDkQvAymfeNyJpDm4DxhtCgMzNkJnBlNsu0SVlZPwLYFbEvA9xU0P2l8iyuzS8KyarAsF4AD32saJ0ZMQwsGZrmQAYMK62BoGqM83wLGLXAeQvAAoWygxBZg2w4soSCED9eugEcDaAEBpjwgDBEGTSyduM8cnCp9BKEPae/G9q37wMscIdNQ2oFtm3qnIc1ZFDBGGs4YNAM8vQRHCFjcRhgK2K5xi6d0ACkFpGbGcKwlAs+45LGsyI2U5UPrupl4Cc3K2iA0h3k6tgPbqcApV6FUYA7XZCG0Nt85YzbAOKquhfqScfrEGYNi0W6NMIQKQ3DbigymsQ9mYQ4m1RJMM6hGAxrL4JYFzTRW1XG41mx0kKZCGCzDDzxYkasEX3koswqEYw5QlDKE485BhT5k6MOXPlgYmkktAKFS8PxVY/CHWfHOI+Md16Yh1UxicSHypa8dBJohlEHU9guEKjATJMp887awYbklBH4dstkw9dpbhVNegF2aBWcMfu0kJLMBUYblzsHmJShvFaHnI5SrEBwoiR0QgiMM6wg8mLRAmsZd2NECjhBSeuagWe6a70IqLCzsg1Oag+NUjAG8bsYD4ALMMT68FTOrnZkGtGRwSvNQTQ3pN8AsQAgXAjZUGCIMT8B1Z6MpfQlYDLAcCFYFlA2NFXhBEE1saEi1Cs3mwUUFGj58fwk2A7gzh0rVgVvi8JvG77vSHH4oAfgIgxUwpiC0BYGSOWw8Oi9Fa3NIotHKEEI4xhUGM2Z732MIg9OwuYJlM2g4EPDBYPRfag+OqKBc2gKlgFDX4PlmBXY3tNJgVmTujPrMrYpruSU41Yppr5tNBEun0JBml4jSGoyXAGZFBmpmDh1P1FkloanQfJOm4RUtbbMNVuJQgkNGripPHT9uXGcJDoTGPaXvBYAfQnp+dLhwNIVm5m4hGeArYMUDIASYsACpoVv6pGvgDL7vYfnEcUh/Ba5QEFpDNBtoHD4MNj8HycwZN0LHE5LR2A1nFn2tHcfHE4Lm38nYMXJ1KoQFJqKzivwgmlw/g0oM+ixadBD5eI/PnVKmzwJmztoSQWSg5hqamzMOFBSU0PB9jt8dW4ZmArsdCypqI7Uyrn8si+PoiROwBTfGbARYWgpRiVyZcEQT5tBohB5WTq9iZq6CFb+JJ5dOQaoAqAUQ2uzwsWwHXqAAGffVqvClhAUNEeW55FbhNZuAMpNrofIxV3YQwkagOVxH4/QKwH1AWdr4vg8kAktDCAmtJRyHQVjafC+ijDBkOHmshmqphvMvPBerx0/AX1lFybZw3nn7oCohKkxhi8Wxa9bFOY0mTi4xHG/4ZqV+2MBM2YJlKXhhHaUZG6EHaCmgAgtKBdDhqjEUWwyaW5CeRKUiUK3MI1Da7L5ybAjLgVf3EEQHKgvB0dRA4GnoMDCTZLaNU6vLCAMfgRfi9LLGCdnEwUMnIf0A1UoFzVUPjmM0wfSJTkBxQHCBGeGAAZFBn8NyGcIZH+XyLITlQNgViEjfk0VIykzGaADCEhDcgWQSMgjMuT7K7J7UjCEEwJjZNaebNQhpzigAZwg9H8oLMO9YCKUHH6ZNKbuz2LKo4WoLLNSo1U8CqgYIDi5slOxZBBYwV6mgZNtQKkCJuaiv1iC1RqVS7apPrLUDSBAEQRAEQRAEQRAEQRAEQRBEOnj/WwiCIAiCIAiCIAiCIAiCIAiCaIcM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRAavXxQ9/+MMaALTWYIyNJ0XEVBPXFa01AGyIelP0+l/09KWltc5spDxthHwQ4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Msvv3zNbNXXvvY1POc5z+m4jVAphRtvvBFPe9rT1hlgjh8/js997nN4+9vfju9973s4dOhQ4ifr29/+NkqlElzXxZVXXtlx9eK9996LX/7yl+vSDRjj+dOf/nRccskl665prXHLLbdg7969655dWlrCDTfcgKuuugo//elPcfLkyTX3nDhxAp/73OeSsuz28pRS+Na3voWnPOUpa54/dOgQvvjFLyb+rnq9/C9/+cvYunXrujSurq7i05/+NN72trdB6zP+Edsrk1IKWmscPnwY3/nOd/CWt7xl3Uq0e+65B4cPH8YLX/jCpNzPPvvsNXEeOXIEN910E6688sqJNfTxzNu0dDTSMI0dqCw0m03cfPPNuPTSS9c1VAcPHsStt96KK664ouvOkFh3XvWqV63xExzrzszMDLTWuOmmm/DMZz5zTd299957cfjwYbz85S/HjTfeiPPPPx+XXnrpmjiOHj2Kz3/+87jqqqvwne98B0eOHEm+nW9961twXRelUglXXnkl/v3f/x2lUmndN9lsNnHDDTfgTW96E+bn59fl49SpU7jtttvWaScAPPjgg/jmN7+JV7/61bj55pvheR6azSaUUvjCF74Azjn27t3bsWMWT7IFQbAuTUEQ4LOf/Swuv/zyjn7ka7UabrnlFgBG91ZXV5NB7iWXXIJ9+/ZBKYVbb70VT3/609eE/9BDD+Eb3/gGXvOa1yRl1SltsU5+//vfx8rKyro0SinxhS98AS9/+cvXPf/QQw/hpz/9Kd75zncm2xaHWUnY69mN/A2SdvbnV7/6Fe6777419bNer+OGG27Aq1/9auzevRsnT57E1772tY7f8AMPPIBvfetbeNWrXoWbbroJ55577rq+x4kTJ/DZz34Wb3/72ztOKP32t7/Fj370I7zjHe9Yl6ef/OQn2LNnDy666CJ88YtfBGCMXK3f7MUXX4xzzz0XX/7yl/Hyl78c5XJ5TRgPP/wwvva1r+F1r3sdvvjFL6LRaCSrrG666SZwzrFr1y68+tWvBgC8+MUvxq5du/C9730PwJkyD8MQN954Y3JOTitPPvkkbrzxRlx55ZVQSuH+++/H0tISrr766uQez/Nwww034PWvfz22bNnS+8WMkI32TbTTnj/SzsEpinbGY5Y9e/b0HLMA5vyWz3/+82v6+0op3HzzzXjpS18KpRR+8IMf4Kqrr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hKuu+66df5H03Lo0CF85CMf6XqQ6KOPPoqPfexjXY3Xrdx88834j//4j0zp6MZ//ud/4rOf/eya33bt2oUPfvCD+Ld/+zdcd911uPPOO/HXf/3XqWaNv/rVr+LrX/96rmkcB4PWrX73j/r6sPdnfabIfPazn8UPfvCDvvedOHEC1113XarTx4tI3u8tTV38zW9+g3/8x3+cWJ25/fbbU23b9H0fH/vYx3DgwIF11y688EK8733vw0c/+lEcOXJkw9X/SdGrHDtdy1v7hn1+1Nr5qU99Ctddd13y3/Lycs9w/+mf/gn33HMPADPQ+Pu//3vcd999A6fxvvvuw9///d9nXhzQi09+8pO466671vw2DW1Qlro3aP0e5DqxsRmmfkxCO4H+Y5Y0eJ6Hj370o8khgHnxL//yL/jv//7vgZ87cOAAPvrRj05sUrAf49DOUber404PYdhM5SilxMc//nE8/PDDfe+99957BxqzDGLv+cQnPoGf//znfcM8ePAgPvrRj6Za+JU2jZuBU6dO4brrrsPp06dHEv647T133HEHPvOZzwwU5zDxZeEnP/lJ4gIGAO68807827/9W+bwvvWtb+HWW29Nvtkiuu4dRTn2XcF+++2347HHHoPW5pCJf//3f4fjOHBdF+9617vAOYfWGuVyGddccw3uuOMOnD59GtVqFe94xztwxRVX4IEHHsAnPvGJJMynP/3peNvb3oYvfvGLyW+Li4t4y1vegq9+9atJZ44xhiuuuCI5Sb4Vxhg453jPe96Du+66Cz/84Q/XXN+1axfe9773JTObaT6KVvcUF154IXbs2IFPfvKTyT2VSgXXXHPNmnS3hwEA73znO/HLX/5yTZ4BYMuWLfjABz4A13Xx0pe+FAcPHlxzT+wTvdPH176V4bWvfS3CMFzz/DnnnIN3vvOdAIzv++PHjyerRm655RZYloW5uTlceeWVeNvb3oZ77713zfMXXHAB3vzmN6870JBzjne9611QSq07sXjfvn1473vfi89//vPrJgYuvfTSvqvierkFybp9I62rkW4Mc3+WPHTaatT697DpH3QraR7lPkw4g1AqlfCnf/qnie7E/OEf/iF+97vf4f777+/5fKw7X/va15JV0YwxvPnNb8aOHTtSTaYJIfC+970PP/vZz9YdGrxnzx68973vXTMj67ou/vRP/xQ//OEPcfvtt6NcLuPqq6/GG9/4Rjz44IPrdCPWnXjlayuMMWzZsgXXXHPNGu2MedaznoUXvOAFXdPe/o7a39fll1+ORx99dE2azjrrLLzrXe/C5z73uXVhdHrnl1xyCXbv3p1o6XOf+9x1utCp7gDAu971LvziF79YE/8ll1yCvXv34vbbbwdgDkV97LHH1pWbZVl4z3veg23btq1r0Bkzp5l/4AMfwJ133ondu3fjRS96Udc8pGHc9X/S2tnpei/t6RRWvzRn2VJeNO38+c9/jnvvvRfXXHPNume//vWv46KLLsIFF1yAa665Bl/5ylfW9H/e8pa34Mc//jEajQY453j3u9+Nu+++Gz/+8Y/XhLNz5068//3v73gOCgCcd955uOqqq/Cv//qv6/pCL3rRi9a419Ja4+KLL8auXbuSb/Y5z3kOLrnkElxzzTX41re+tWYHy+te9zr88pe/xLFjx9aUyY4dO/CBD3wAX/ziF+H7Pnbv3o3XvOY1+MxnPgPf9+F5HqSU+Ld/+zcwxnD22Wfjla98Jd7//vfjJz/5Cf7f//t/a9K5b98+vPvd7+74PvJq54epr1nCG7av0um5PMLMI+5JhJMlvnFp56BaNy7tBMyY5Ve/+lXPMUsazjnnHFx99dX49Kc/nUzm2baN9773vYkP9mazic9+9rPJ6s96vZ7ozLOe9SxccMEFqeK66qqr8Otf/3pdmhcWFvCnf/qnKJVKeMlLXoLHH398zT3bt2/H+9///mQx0f79+/Hud78bn/nMZyClxO7du/HOd74zycPevXtT604edSdLmzeJMUuvNI16zNUeH2nnmXLP0m/LGt+4tHPPnj143/vehy984QuJnUEIgauvvnqdD/ZOYTztaU/D7//+7+NTn/pUonnxOGzbtm2Ym5vra+9ZWFjANddcg2984xtrVve+4Q1vwN13371mrNgtD5dffvkaW4kQAu9617uwfft2PProoz2fB86Md++88851Y9OnPvWpeMc73rHmt7m5OVxzzTW4/fbbUavVkvHu29/+dtxzzz09tbMTw9p7WmnP4xVXXIH7779/XZqq1SquueYazM3N4dJLL8W+ffvW3dM6Zn/yySdx2223AQBOnz4NKWVy/8te9rIkD5/73OfW5eGyyy5LdnZ2Y5B2t5Od7fzzz8cVV1yRnEH5wx/+EL/+9a8BGM8Ud9xxB+6+++7kbMN+i1m7fW+O4+ADH/gAfvSjH61bjHv22WcntsI3velN62y05513Hq688krcfPPNAMyYfe/evevKPX5+586dsG0bH/jAB3Drrbeucwv97Gc/G0996lMhhMB73vMe/Pd//zfuuOOONffs3r0b733ve1PtJp8W7exrYN+2bVsiSK0Vz7btNQkTQmDPnj3Yt28fZmdnk0qxY8cOrK6urlmZvnfvXuzZsweXXXZZ0nGzbRu7d+/GWWedtWaAGfv27gRjDLt378a+ffvWvZSdO3di165d69J42WWXYW5urmNYrf+emZmBZVnYv39/kv9KpYLdu3fjOc95Dvbs2dM1jJ07d2Lfvn3rtkQuLi4mzy0uLiaHk8Vs374de/fuxWWXXYbFxcWu6QPMexFC4OTJk8lve/fuxa5du5I0xH7uY/9IcR7i68vLy2sMcnv37sXWrVvx/Oc/P9lmHse7a9eupHK15st1XezZswdnnXXWOrHau3cvtmzZgjAMcemll3Yt97h8swwQOpGl45nmw8nS8Uwbfx6Gpaz39EtL1uda3+0w4XZDaw0hRKIbrb6LY93o1zhZlpU832pgj58PggCXXXZZR8PVeeedB9/3Ex0666yz1rln2L17N3bv3r3mN855opXVajXRt+3bt6NWq61b1Voul7F79+6u5ddJO2P27t2Lbdu2rfltbm4Ol112GYQQPQcXgPGV5vv+GuPZvn37sG/fPlx22WWYmZnp+73Nzs5CCJFo3fz8PEqlEp7//OcnE3bd6n+spa31aO/evXBdN8lDrDHtflYty8KePXvAOce2bdvwvOc9b008re+hVZvyrP+jZNLa2V5XOg2QBhmsjrPcx6mdKysrOHHixLpvWGuNEydOYHV1tWf/58ILL0StVlvT32nfQrljx451OtNKpVLBnj17sH///nWr2Pfs2bPmgOi4/yOESPo/8/PziVbu27cvGWDGaQyCYJ0Pdsdxkr6B7/uJ3/Z9+/Z1XD26fft2cM6TONr7fa39mwsuuCA5IDZORxzn85///L6Hq3djkLrTTzvb7+kX3yja3XFoUK+/xx3OIPF1eo/DxD3N2hm3s+19+NYxy7nnnttxJTvnHM973vOwuLiIcrmcfPOtBvY9e/bg4osvTlxinXXWWR0XPS0uLibfcKdzuuI+FmMMO3fuxKlTp9atCF1YWEi0dn5+HkqpN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\n"},"metadata":{"needs_background":"light"}}],"source":["plt.figure(figsize=(30, 12))\n","plt.subplot(211)\n","plt.imshow(np.hstack(images) * .5 + .5)\n","plt.axis('off');\n","plt.subplot(212)\n","plt.imshow(np.hstack(texts) * .5 + .5)\n","plt.axis('off')"]},{"cell_type":"code","execution_count":13,"metadata":{"id":"GOe1Miplnf7Z","executionInfo":{"status":"ok","timestamp":1678890441504,"user_tz":-60,"elapsed":18794,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[],"source":["# Embed both texts and images with a single model call\n","# See \"zero-shot evaluation\" below for how to do this separately\n","zimg, ztxt, out = model.apply({\"params\": params}, jnp.stack(images), jnp.stack(texts))"]},{"cell_type":"code","execution_count":14,"metadata":{"colab":{"height":331,"base_uri":"https://localhost:8080/"},"id":"PtATpe2Bn5I0","outputId":"21e157fd-b0e2-4f10-916e-69bb73b43f77","executionInfo":{"status":"ok","timestamp":1678890441932,"user_tz":-60,"elapsed":431,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"display_data","data":{"text/plain":["
"],"image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["# Visualize embedding similarities\n","plt.imshow(ztxt @ zimg.T)\n","plt.xlabel(\"Text\")\n","plt.ylabel(\"Image\")\n","tick_labels = [\"CD Player\", \"Truck\", \"Gas Station\", \"Chainsaw\", \"Colorful houses\"]\n","plt.xticks(ticks=range(5),labels=tick_labels, rotation=45)\n","plt.yticks(ticks=range(5),labels=tick_labels, rotation=0)\n","plt.show()"]},{"cell_type":"code","execution_count":15,"metadata":{"colab":{"height":206,"base_uri":"https://localhost:8080/"},"id":"bs9fr-1kCRQW","outputId":"affa9b69-6df8-4959-9a98-8a46b6c8f594","executionInfo":{"status":"ok","timestamp":1678890443073,"user_tz":-60,"elapsed":1144,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"execute_result","data":{"text/plain":[""],"text/html":["\n","\n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n","
 01234
itap of a cd player98.83%1.07%0.05%0.05%0.00%
a photo of a truck0.15%96.14%3.46%0.25%0.00%
gas station0.01%0.25%99.73%0.01%0.00%
chainsaw0.12%4.22%1.34%94.32%0.00%
a bad photo of colorful houses0.30%5.81%4.67%0.09%89.12%
\n"]},"metadata":{},"execution_count":15}],"source":["probs = np.array(jax.nn.softmax(out['t'] * ztxt @ zimg.T, axis=1))\n","pd.DataFrame(probs, index=text_list).style.background_gradient('Greens', vmin=0, vmax=1).format('{:.2%}')"]},{"cell_type":"markdown","source":["### Compute and compare sentence embeddings\n","\n","Since we co-train some of our models on pairs of neighboring sentences from C4\n","with the same contrastive loss as used for image/alt-text pairs, we expect the\n","embeddings to capture sentence similarities well. Indeed, our GLUE evaluations\n","show that CLIPPO learns good sentenence embeddings.\n","\n","Below we visualize the similarities between pairs of neigboring sentences form\n","different Wikipedia articles. CLIPPO models with C4 in the training mix assign\n","higher similarties to sentences from the same article than from different\n","article."],"metadata":{"id":"Fs-TpneMq2uX"}},{"cell_type":"code","source":["# Selection of sentence pairs from Wikipedia (collected on 3/7/2023)\n","sentence_pairs = [\n"," # https://en.wikipedia.org/wiki/Google_JAX\n"," ['Google JAX is a machine learning framework for transforming numerical functions.',\n"," 'It is described as bringing together a modified version of autograd (automatic obtaining of the gradient function through differentiation of a function) and TensorFlow\\'s XLA (Accelerated Linear Algebra).'],\n"," # https://en.wikipedia.org/wiki/Matterhorn\n"," ['The Matterhorn (/ˈmætərhɔːrn/, German: [ˈmatɐˌhɔʁn]; Italian: Cervino, [tʃerˈviːno]; French: Cervin, [sɛʁvɛ̃]; Romansh: Mont(e) Cervin(u)) is a mountain of the Alps, straddling the main watershed and border between Switzerland and Italy.',\n"," 'It is a large, near-symmetric pyramidal peak in the extended Monte Rosa area of the Pennine Alps, whose summit is 4,478 metres (14,692 ft) high, making it one of the highest summits in the Alps and Europe.'],\n"," # https://en.wikipedia.org/wiki/Claude_Shannon\n"," ['Claude Elwood Shannon (April 30, 1916 – February 24, 2001) was an American mathematician, electrical engineer, and cryptographer known as a \"father of information theory\".',\n"," 'As a 21-year-old master\\'s degree student at the Massachusetts Institute of Technology (MIT), he wrote his thesis demonstrating that electrical applications of Boolean algebra could construct any logical numerical relationship.'],\n"," # https://en.wikipedia.org/wiki/Z%C3%BCrich\n"," ['Zürich (/ˈzjʊərɪk, ˈzʊərɪk/ ZURE-ik, ZOOR-ik, German: [ˈtsyːrɪç] (listen); see below) is the largest city in Switzerland and the capital of the canton of Zürich.',\n"," 'It is located in north-central Switzerland, at the northwestern tip of Lake Zürich.'],\n","]\n","\n","# Preprocess sentences\n","sentence_lists = list(zip(*sentence_pairs))\n","first_sentences = preprocess_texts(sentence_lists[0])\n","second_sentences = preprocess_texts(sentence_lists[1])"],"metadata":{"id":"_dJrKpXu43f4","executionInfo":{"status":"ok","timestamp":1678890459181,"user_tz":-60,"elapsed":16111,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":16,"outputs":[]},{"cell_type":"code","source":["plt.figure(figsize=(30, 12))\n","plt.subplot(211)\n","plt.imshow(np.hstack(first_sentences) * .5 + .5)\n","plt.axis('off');\n","plt.subplot(212)\n","plt.imshow(np.hstack(second_sentences) * .5 + .5)\n","plt.axis('off')"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":700},"id":"8xKWKxzE7vhs","executionInfo":{"status":"ok","timestamp":1678890460516,"user_tz":-60,"elapsed":1350,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"6c5adddf-5ad6-4394-b7e5-adeede5cb1cd"},"execution_count":17,"outputs":[{"output_type":"execute_result","data":{"text/plain":["(-0.5, 895.5, 223.5, -0.5)"]},"metadata":{},"execution_count":17},{"output_type":"display_data","data":{"text/plain":["
"],"image/png":"iVBORw0KGgoAAAANSUhEUgAABLAAAAKaCAYAAAAuxqClAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAEAAElEQVR4nOx9Z3QUx9L2s1E5Z60SIJDIAoGEQOScMdFEm+SIzbXB7+/7+2LAOFwbAwYssgkmZyRAIoMEQgEhBMo5x9WG+X7om74zO7O7s0kIrOccDtqZnu7q7urq6urqahFFUehGN7rRjW50oxvd6EY3utGNbnSjG93oRje6KsRvm4BudKMb3ehGN7rRjW50oxvd6EY3utGNbnTDELoNWN3oRje60Y1udKMb3ehGN7rRjW50oxvd6NLoNmB1oxvd6EY3utGNbnSjG93oRje60Y1udKNLo9uA1Y1udKMb3ehGN7rRjW50oxvd6EY3utGNLo1uA1Y3utGNbnSjG93oRje60Y1udKMb3ehGN7o0pIZe/vvf/34nryikKAoikehtk2F10DdGvo91e1fxT+C197mO71K93jV6hUL3Jtz3tY7vUr3eNXqFgq7X+zyXvq99966im9e60Vno7o+uh+4+6Vp4X/vjfZ5n/v3vf/NW6r30wLK0A3UXVKa+txVEIlGnMae+OlIUZbD+QtrmbbWfLWCoPyxtC2NtbUswec3WPPe2eO1dE/TvO691pnzTRTevsfG2eM3WPMiUaW+T1yx5byzNu8ZrhvAu8xqNt8lrxmBpG3TVer0N8LUl/awzea0bXQudvWZ7n9ZXtsD7Okbehn3AkMyzNG8heC8NWJbCGBO8iwPAVKbSV0djg0RI27yL7WcOLG2Lrqz4WhPdvGY5unnNMnTzmnDYktdMbcN3UVm3hn7RzWvC0vzTec0Y/il8ZC2Yajg2dxOwK/Nabm4uKisrAXTQ+fLlS1RVVb1lqt4/mLtme594rRudA2vymqWbn6bwr1UMWG9jAGi1WiiVSiiVSmg0GoP0mEqfbvr29nao1Wqz8zMHKpUKKpXKavlZS1ExZffY3HaiKApKpRJarVZQ+YbSWxu65atUKsKH5nixvC1vP3r8vM3Ji+43pVLJGl/M98a+F1qGRqMxKhe62kRubXpNbU9dufcuwFyarT0O3xavURSF9vZ2zpzIl86S38by7Cy59i4uwA3VXa1Ws+YTc/KzhNdsMeYNeUDRdTVV1+lsXlOr1RbrYxqN5q3PubroTLnWGfOJsfro06sN8SIzz86SN8xybMEvluR58eJFZGVlkd/nz5/HixcvzKaDbw0nBBqNBu3t7RbVhS7fnDys0S+6MqGr8Jol/aKLty3vrK2LqVSqTteLbS0DrMFrarUa7e3tBtNYW65ZxYD1NpTI4uJibNmyBVu2bEFGRoZBekylTzd9QkICrl+/bnZ+5uD8+fM4duyYSd90hqAwZffY3HZqb2/Htm3bkJ+fL6j8pqYmbNmyBRUVFWaVZwnOnDmDLVu2YPv27VCpVBbzmqnvzUVhYSG2bt36Vifvuro6Mobv3LnDeW+NttFoNPjhhx/w8uVLi+XC24a15Zqx93v27EFKSopJZRiDrXntzz//RGJiosllWHscvk1e27lzJx4+fGgwjan1NdUTpit4Mb8tuWYMhup+5coVbNmyBd9//z3a2trMys+Stt23bx+SkpLM/p4P+ujRarXYsWMHtmzZghMnTpidZ2fw2tWrV3Hw4EG974XwzYsXL7Bjxw6rLAqthc6Ua7p6tC1gjJ4zZ87w8ppKpcK2bduwZcsWnDt3zqQ8bQ1blP+260RDq9Xixx9/RHZ2tsnfZmRk4KeffrJIZtfU1GDLli2ora01+VtrtGFWVhZ+/PFHsvn+tvuFLp/uF6ah0tI8uwosnS8PHjyIK1euWJMko3gXZMCtW7ewd+/eTi2/U48Q6lqZLbGE+vj44MMPP4REIhF89tfc85oajUavd4+tdta1Wq1gjyK6TFt5Wb0NC7parTapXH1eeNbgNSZ02zg+Ph5jx47lWOQ7Y9fM0jLoNjOFb6zFa+np6bh27RqWLl2KpUuXYuDAgaz8zUFZWRkOHjzI2QXg877ig7l1shWv3blzB1evXtX7PjEx0eqLTRoURbHk3tv0mBHyTWtrKxISEhATE4MhQ4bwpqHr8PTpU5w+fRoUReHkyZOcDRBb4+rVq7wGWyHQx1vM54bmK314m0omX51OnTqF9PR0i/Kl62QK777tOXT48OGYNGmSIJkl9L0pddJqtUblmFB9yxjEYjEWLVoEhULRKd7TgPn9YkwfE8JrtEy1NqytFxjTkw295+O1trY2JCQkYNiwYYiOjhZUprV4TBf6+lEqlWLJkiXw9fU1iRfPnz+PhIQE1r8nT54AYNPe0NBA3j9//tziepSUlODgwYNWPaVhCMz+mDt3LtHXAGD+/Pno37+/2XnryjqhvGbueKK/f/bsGW7cuIGlS5fCxcXFoKfo8ePHkZ2djaqqKhw4cEDw5oIxGqwlE2yxZmP2i6X5tbW14cCBA4KOmtpqzWaJXGPCnLU58/fff/9tsW5jCqwtQ/X1iyntYi2YbcDSaDRIT09Hc3Mz67mhxtI9N2mKJbSlpQXp6enEUGBvb4+ePXtCLP5fFXS/F8KQQpS78PBwBAYG8r7TdYmzlgIcHByMHj162HThLTQ/S/O3lQGMzlcqlWLgwIGwt7cn7yzhNVPg5+cHf39/QWktbQdr8pqTkxMGDBjAGj+W8pparUZ6ejpaWlqM5lNXV4eSkhL07NkTvXr1goeHh9H8jaG1tRV5eXk2EaK2kmuG8q2oqEBJSYne9+Xl5SgrKzM5XyGwlVxj5mmNNDTUajXy8vLg4+MDb29v3jQiUcftczU1NSgsLAQAFBQUoK6uTnA55tLHRElJidneovp4S0j/2FoOmws+2i3pF11QFIWMjAzU19cbTGNN0LzG/C0EPj4+UCgUnOfmjBdLx6wxXrNGrIsePXrA2dlZME3W5jVb606dAXN5zVB+TFiqR2s0GuTl5cHb29ugbOb7bW25FhISgtDQUM5zsViMnj17wsHBQVA+KpWKLEKdnJxY/+rr61keReXl5Xj58iV5X11djZycHME066KgoACZmZl4/fq1UV3H2htPIpEIwcHBLH0tJCQE7u7uZufbr18/uLm56aXXVnKtrq4OZWVl6NWrF2QymcF88/Pz0dDQAKVSiby8PINGJ1P0aHd3d/Tr189mstpasHQc0jJAqVQKLsvadbKVfUBomXTeBQUFZnn88aEr2Qf8/f3Ru3dvk/OzREZJhSTSaDQsS79YLIZGo8GpU6fw0UcfwcnJCRRF8TKnXC5nLZL5jixJpVJIpf8jRaVSsQSEVCpFbW0tTp48ic2bN7PSCqWZhp2dndFvadB1GjlyJCQSieA6KJVKSCQSDp1arRbt7e2ws7PTy1R0mQMHDoRYLOYddHztLJPJWDTyQa1WczyFRCIR5HK5QTd/pkcLnZ6mQSQSsdpUNz0NZp1NqYNuP0okEshkMlK2Wq2GSCTCjBkzIJfLWd8qlUqIxWJIJBJOHUyl2VTQ37W3t3OUDGYdAP5+YZavVquh0Wggl8s5Z/7p8cXkLTo9Db7x5ezsjOnTp7PoADp2SuhnumNeLpcTYwZfv6hUKpw6dQpr1qyBo6OjSe3VGeCTY0yeMSQzjI1XGjRv0TKiM3jNGE00aF6h38vlcmi1Whbv6fImX36m1onJmyqVijUe6DFPl2GI9/SBPntvb2/Pmmv4QNNpaB7gm3/EYjH5lqbZ0BjXbQMhsKTfdMc4wC+7LeVFQ/3E5B1a9kqlUk6dDOVP0ySXywXN8/q+pyEWiyEWi3HmzBnMnTsXbm5uFs2hpkBXeeWL18LXb4by4uM1enyIRCJSd93xwuQdJi8xwTcmhOhrfNDXxkLHAy2LddPTf+u+NzaHAvr5namj8vWTUJ3RlPlDtx+F6Ml848uQbkPLWqa+xkeTkPFgiWxWKpW8sllIHSUSCUv2WlrHwYMH650jTFlIKZVKnDx5EuvXr+dsbicnJ+Py5cuIiIiAUqlEVlYWcnJy8MknnwAAkpKScOvWLYSEhJg1Pzx+/Fjw0S6heesbPzQkEgnhNb71gm4/6M6ZNC3MsUSPlylTprB0et33uuBrM11e0h1PuqDTGxvbdKxYuVyud27QN34M0UxRFFQqFXx8fDBlyhQiu0UikV79y5DsBrhrAblcDo1Gw7sWMEQzLVd0aTA0vwDmGUqMhTCh66DVajl9RdfBWJ1M1TGN6XN8oMePMd3JmG4jlJdo2vXN88w68undps43xqBUKhEeHm6WAcuSdY8gDfHFixesc+MDBw7E1KlTWWlaWlqwfft2TiOsX78efn5+hMg//viD40Y4btw4jB49mvy+efMm7t69S35PnTqVd1fSEF6/fo3Dhw+znslkMmzevJm1+DAEZp1iYmJInfft28fZQR89ejTGjRsHiqKwa9cuREVFIT4+npWmoqICu3btwr/+9S+4uLjwlqnVavHTTz+hra0NPXr0wIoVK1jv1Wo1fvjhB45gX7ZsGXr16mWwPmlpabh48SLrmaurK77++mu939TU1ODXX38lv319ffHJJ5/gt99+Q2NjIwICArBu3Tryvri4GPv27WPlIRaLsWnTJtjb2xOXWb46fPjhh5wBkJmZib///pv8joqKwuzZs8nv1NRUXLp0CQA/r0VGRmLw4MH45ZdfyDfe3t74/PPPye/Kykr8/vvvrHJFIhG++eYbODk56W0bITh+/DhevXrFejZo0CDMnTuX/H769CkuXLjASuPi4oKNGzcCAB48eIBHjx7hq6++wp49e1BdXU3SbdiwAR4eHqiqqsLOnTvx9ddf4/79+7h//z5JM336dAwbNoz8vnTpEtLS0lj9AnQoDDt27MCcOXMgkUhw9OhR8k1kZCQWLVpE2pavXyZOnGhuM9kEurIoNzeXFVeuX79+WLBgAfmdkZGB06dPs75xdHTEN998wysvKIpCfX09fvrpJ/LM09MTX3zxBXbv3o2amhoOr9EygAlr8RqtZCiVSmzdupUzCX/88ccIDg5Gc3MzfvjhB6xZswZFRUW4fPkySTNy5EhOP9bW1uK///0v+e3j44PPPvvMaJ2+/fZbODo6orCwEAcOHMCmTZtw8eJF1hGKhQsXom/fvlCr1di+fTsWLFgAjUaD48ePkzQDBgzABx98oLfeaWlpuHXrFjZv3mzUgLV7924MGDAAa9eu1Zvmxo0bePDgAfk9Y8YM+Pr6Erk2Z84cDB48mLw/d+4c7zFEmUyGTZs2CVYCDM2h/v7+hNc+++wzZGdns46P6s6hALdfAgMDWfUuKirC/v37Wd9IJBJs2rSJV1Gjle5t27Zh8eLFaGtrw6lTp8j7wYMHY86cOQCAQ4cOwc/PD2PGjMEPP/xA6sSUa3ygee2LL74gu/ymoLCwEH/++Sf53aNHD3z44YesNDSv6S7Yli9fjp49e5pcplDs3LmT4wU2depUxMTECM6Dj9f69OmDJUuWAOiYb+zs7FhyDejYmNi6dSs++ugjBAcHc3hNX78I0deYHpr036Wlpfjjjz9Y34nFYnz77beCvFxevnyJv//+W++GJX0E/dtvv4VIJDI6hwJAfX09fv75Z05e9BxK47fffkNDQwP5/c033xilFwDevHmDQ4cOsZ7pkwHHjh3D69evye9Vq1axPIMOHjyI4uJi1jexsbGYMmUKAGDv3r3o06cPoqKi9Oo2t27dwqtXr7B+/XqirwHc+YaiKPz8889obW1llTd//nz069eP6Gvz5s2DSCQyOIfq4tmzZ0hMTMSmTZs4i+L9+/ejvLyc9UxXjx48eDAiIyPx22+/kTR+fn745JNPQFEUrl27hpKSEqxevRq//PIL8f7W5TU63lp7ezvCw8OxdOlSkh/Ns7bwntuzZw/69evH0pHHjBmDyMhIbNmyBRs3boSrq6ugvOi1wdSpUzFgwACin1nDQ5qpR/MhPj6e6NFffPEFvLy8WO9v3ryJvLw8wmsjR45EUFAQ9uzZQ9IEBwfj448/Jr8LCgpw4MABAPzHEF++fIm//vqL9czOzo6Xlw4fPky8qgFg7dq1ek/NAB1yLSIigtUvuqAoisyhX3/9NVxdXXk94plrNj5IpVJs3rwZMpmMxWt//fUX8vLySJ2OHDkCd3d3MofSaG5uxvbt27Fu3ToEBASgsbERO3bs4JRD9ws9h27YsAFPnjxhhSuYMmUKYmNjAQD//e9/OSeoACA0NBTLly9nPaP56/Tp09BqtWS+MRd0O/DJACa+/PJLPHv2DFlZWSw9mgbNa/QcylenuXPnYtCgQSSu18yZM2FnZ8eyD0RERGDx4sUA+NdsAODg4IBvv/2Wl066nYXoNp9//jk8PT1Z75hj+Ndff0VTUxNvHrRcu3z5MpqamvTaB+bPnw+tVovTp09j06ZNhNdo6M43hw4dQlFRESsv5nxjCBRF4ffff0ddXR1nbWAJhMg1owasa9euQalUYuXKleRZeXk5Tpw4gZUrV8LPzw85OTl4+PAhli9fzikwOTkZoaGh6Nu3L44fP44xY8ZwFmqvX7/GqVOnMG/ePJw4cQJ+fn6s8l6+fInCwkJ89NFHrGNi+pCUlISGhgZWHkDHAv3QoUOYOHGiIIOYvb09VqxYgXPnzkGr1aK5uRl//fUX4uPjOXV48+YNTp48iQ8++IAVR4IJiqL03qxHt5tYLMaSJUuQlJTEyaOwsBCJiYlYvHgxZ6H27NkzFBcXY8yYMbx1OX/+PORyOadNlEol9u/fj7lz5+pdMGi1WixYsADOzs5obm7G/v37MX36dNjZ2aGxsRH79+/H4sWLkZaWhrKyMk4ZFNVxhnzkyJGws7PD9evXsWjRIs4klJ6ejuLiYsTFxQHoCGgbGhrKyq+0tBRHjhzBkiVLIBKJEBERAScnJzLRMflPq9Xi6dOnqKioYOXR0tKCffv2YeHChcjOzsabN294af77778xfPhw9OnTh7ddDEGlUuHIkSPo378/Ro4cyXpXVlaGw4cPY8mSJbh06RKkUqnefpkzZw4oikJDQwP279+PcePGsbybrly5gr59+8Lf3x9arRZ//fUXIiMjOePn/PnzmDlzJgAgLi4Ovr6+vIEItVotEhMTERwczMqjsrISBw8exIcffojr169Do9Fw+uXUqVNYuXIlfHx8eNuEHj9Ax2KiqamJLKCjoqIQFRUlpGk59B49ehRKpRJtbW3QarU4dOgQxGIxFAoFxo8fD6AjXlRISAiL5oqKClKna9euQavVcvpBpVIhISEBM2bM4NSLubM/d+5cuLu7o7W1Ffv378eECRPg4OCA5uZmk3jNzc2NGJmrqqqg0WiI4WTs2LGQyWS4du0aoR8AeT958mS0t7cjJSUFy5Yt48jiR48eobCwEIMGDYJGo8G5c+fQs2dPFj0FBQU4fvw4WZTwjR+6TosWLUJmZiby8/N563Tq1CnExMRALpdDrVbj0KFD6N+/PytGVUZGBoqLizF27FhotVpcv36d00/l5eU4dOgQPvzwQ14DFUV13P7J57FKo7GxESdOnMCECRMQEBBAdjIXLlzI2UzQarXw8PDArFmzAAA5OTkoKCggNGVnZ6O0tBRTpkzBkSNHEB4ejqFDh0Kj0eDIkSMYPXo0QkJCoNFocPjwYUyYMAEqlQo3b94k9amsrCT9Nn36dNTX1xucQ8PCwhAeHg6tVou///4b4eHhrDZ6/fo1/v77b2IYf/ToEUpKSlhpaBmyePFiPHnyhNOvdN2PHTuG+Ph4Fq8BHYrtqFGjoNVqcfXqVY5sZso1rVaLrKws1NXVsZQsplzTVeLS0tKQk5ODlStXwtXV1agxUhcpKSmorKxk0VRbW4tDhw5h8eLF8Pf3J3PokiVLOPk/ffoURUVFnDnU0gVifX09Tp06hSlTpnD0l9zcXJw9e5a1KcMHrVaLw4cPo3fv3hg6dCjrHR2XZenSpXp1DDoPiqJ49TW6X2pqatCrVy80NTXh+PHjGDt2LMeblua1efPmAeAGVL937x6H94COdjxx4gTi4uKMbrbp1iMvLw/37t0jMoD5/sKFC0bn0OLiYmRkZHDSAB1zaL9+/RAUFITTp09j2rRpLAPumTNnUFZWRo458eHWrVuoq6vjHU+HDx8m8xA9n+hucKampqKoqAjR0dE4evQohg0bhgkTJrDyKioqwl9//YWFCxcS3UaX35m6jVarRVVVFUtfo3H69GlER0fDw8MDFy9exNy5czlGtszMTJSUlGD8+PHQarW4ceOGwTmUz0OF7ifmpnFLSwuOHTuGkSNHco6P5ufns/ToJ0+ecHTKpqYm7Nu3D4sXLyZGhoSEBMyZM4fUQZfXxGIxli5dihs3bljlmJCDgwM++ugjjiFHFzSPMmVNamoqnjx5YnKoA3pt4OXlxQodYK5sYsq1iIgIvXobHS8SgF6ameNRq9Xi3r17UCgUrH6rr6/H/v378eGHH8LOzg7+/v5Yvnw5jhw5wumTGzduoKWlhTOe1Go1Dhw4QBbWSqUSf/75J4YMGYJx48aRdHfv3kVQUBAx1uiCr1908ejRI6LbODk5QSQSwdvbG6tWrWLJcbpf+NqmqKiIzPsAu68mTZqEzMxMstk8ZcoUvHnzhsyhYrEY2dnZePLkCVauXAlPT09kZmbi6dOnvHLs+vXr6NOnD4KCgqDVanHixAn06dOHlZaeb2bNmoVFixZBo9GgsrISFy9exJIlS2BnZ8e7xqZ1mcjISISEhOhtM32wt7fHqlWryBFiuh1mzZrF6xXb2NiIkydPgqIoDB48mGwgLly4EM7OzqipqcGZM2cwffp0BAQEoKqqCufOncOsWbM4XklZWVkoLS3FpEmToNVqkZSUxFnfMOfQiRMnYuTIkWQNN2nSJKIz8o21c+fOwd7e3qAhPy0tDS9evMDKlSvh5ubG4TtmvgsXLuQ9olpeXk7WbPpsDMD/YpfR3lgJCQkYPHgwRo0aRdKkpqaisLAQw4YNw9GjRxEdHU3mKBrM+caYjPnggw9w//59q1ykZsqGglEDVkVFBWQyGUJDQ0mGDQ0NKC4uRmhoKMRiMZqamlBSUkJ+M3Hp0iW4urpCo9EgPz8fM2fOJIKSJjQvL49YtouKihAQEMAqLysrCzU1NQgLCxPUAJWVlWhvb+ekb29vR0FBAWenSR/EYjHCwsLIgBCLxfDw8EBQUBBHmcnPzyc7Zn379oVWq0V2djYiIyMBdHgmFRUVISoqiqMoiEQiVqeFhIQQYxGznVpbW1FYWIjg4GBIpVJWB5eUlPC6WNJpXFxc4ObmxmmT+vp65Ofns4QI/R1zgCgUCnh4eKC8vBz5+fmYP38+2ZF48+YNNBoNHB0d4e3tzSlDo9Hg6NGjaG5uhlarRUFBAT788EPWkTSgYwHEVIKcnZ3h6+vLyo+OX0N/5+rqiuDgYJ7e64C9vT08PT1Z/FRZWYn8/Hyo1WrU1taiqqqKQ7NWq8Xx48fJrqWpoCgK+fn5iIuLI3nTNNfV1ZHdIhcXF7i4uLDoAzrGWH5+PvFSk0gk8PDwQHBwMFlwUxSF06dPIzAwkMThcnNzg7+/P6s+GRkZKC0tJb+9vLx4d15oODk5wcfHh5VHS0sLCgoKQFEUysvL4eTkxHpfW1vLkgl8oMcP0DEWW1payG96p9TUxaJIJIKbmxtUKhUaGxtRXl4Od3d3SCQSlnLMV6fm5mYUFBQA6JgcXFxcOHzQ3NzMGR98UCgU8PHxQXV1NfLz8zF79mx4eXmxeM3BwQFeXl4Gec3Ly4u0SUNDA1QqFflNux7Tv2trayESichv+qh1UVERQkJCOAuK69evs2SPi4sL/Pz8WPTQcoqGvb09vLy8WPxZUVFB6lRTU4Pq6mreOv31119oamoihgp3d3cEBgayxuuDBw9YXil8/dTQ0EB4DzDPoKBWq0m/MON1BAUF8aaXy+UICwsDRVF4/vw5a/5JS0sjNLu5uSEwMBBBQUHkODMts9rb21FYWIjW1lY4OjqSfqquroZMJmP1m1wuh6enJ8LCwjh1u3TpEmu+cXV1JWOcOYcyPTb4eK24uBj5+fnQarVGZXVLSwu8vLzg5uaG9PR0hIWFsTZtmLKZpqG2tpa1C25nZ8eRvbpyDfhff9bX16OiooIjC4WiqqoKtbW1rDppNBoUFBQQ5by4uJjMoczFrkgkwu3bt3kvAzGHFuZ3KpUK+fn5mDdvHidWTGNjI2pqagSVRY8fXZ5ta2tjjQ9jaGxs5Ohrra2tyM7OJrvh9HjRp6/pegcxYUgmHDt2zODcQ5ejC1pW870rLS3lyDGAzWv19fUoLy8nvMXUcU6fPg2FQgFfX1/k5+fjgw8+YMXkyc7OJn3EpJHZV1VVVWhubmbRQHube3h4sI5Iubu7k/JoJCYmEv3P3d0dQUFBHANvaWkpRzYb0m2A/+kNISEhrDn2+PHj6NOnDxwdHZGfn49FixZxDJWPHj1ieZXom0OZY14I6DkrODiYo0cXFBSweIuef5hllpWVIT8/n4xVqVQKd3d3ciQP4PKaSNQRu8nJyYlX9pgKiURCaKqoqGDRrNFo0K9fP843FEUhKysLDQ0Nek9gGAK9NuADXz2M1Y35ztXVleMJ1t7ejoyMDPTu3RsBAQEm0cqcX2g6CgoKWP3m4OCgV1+kF8K6Y1qlUiEzM5N4ZTLHi5eXFynr6tWrLH421Bb63vGtDezs7Dg08fULRVF48+YNxGIxBg0axJI5NPz8/FgL/sbGRqjVari5uZF0dnZ2cHFxQW1tLXx9fXnnVLq8c+fOwdfXl8wPTD2BBr35Ro8HAERPDA4OJm3GnAcbGhrw6tUruLq6IjAwkNfQaYzXmOOFCT4vubKyMlRWViIqKoo4STQ3N8PDw4PwCi3XGhsb4enpSfTi0NBQzjo4NTWV5VHLJ8eYc6ifnx95BoDThjRUKhUyMjIgkUjg5+dH2pOvLYTqNsx+YYKmbfDgwXqPd+rLjx4fzPkmKSkJIlFHeI/8/HxMmDCBU25ZWRnHK4tZHrPcoKAgZGdnm2XA0qVfl68NtZfgIBO2cK8VKlx1IWTS0Wg0nGDSpt7YoVuGg4MD5s6di7a2Nr15i0QiTJ48Gbdu3UJycjIRbLQR4aOPPhJUFt87iUQCBwcHtLa2QiqVQiKRkMGq6+Wjm+eYMWOgVqs5dPO5vdLfGWtjXWVy8ODBvO1uyFrMLGPEiBEAQM4Ajxw50uQjHbq80adPH85Opi60Wi2HZtqCbSmUSiUnb6YxZPTo0VCr1Ryjqm6/ODg4YM6cObw8yTRKTJkyxeBOMWC8X4cPH86rgFkKevwAwO3bt/HkyRNOnUyVMyJRR/wzoMMz4PXr15gxYwbZRaIV+ZiYGPTt29dgXny829raqpcPTOWP/v37o2/fvgZ5zcvLi7TRqVOn0NDQwDpuCoD8PnLkCOs30LGAoSgKra2tHMVQd4dw7NixHAVCKpWyjvhERERwdmbMgVgsZvULDd3YEiNGjDDq8Wgqj6hUKiiVSjg4OJjMa4bSiMVizJw5k4xxWumjf6tUKtKvCoWCeP3u378frq6urH6jjVdCMGHCBKJE0vTJZDJW2/J5fjKh76ZGJvz9/TF79my8efMGcXFx6N27N5FdcXFxCA8PZ9GgyzthYWGYNm2a0XJEIhGRd/b29jbRNXTR2trK0Qf4dj3NpUXod8zbvEzhNSaMGdeFgJbNzI0OAAZ1HRp8+hjfnGpID2DCGv1vrBwhug1tUJo2bRrOnz9v0OuFjk9J15nebJBKpcS7rrq6mvzWFwPFzs7OqI5Jo0+fPkaP7bu5uXHmD3MRGxtLNmQtgRA9mkbfvn1ZHpF8/eru7s5bRwcHB9bcwtfn1uC1goIC3Lhxg/yOjo5GfHw85zRAW1sbbt26haioKIwfPx4vXrwAYJ0jgNaum0qlQm1tLa5evYo1a9bA29ubY8Q1hIEDBxLvJ5oOsVgMR0dHi+iSyWTkiF1JSQnkcjnmzJlDvDINLeT1gc/w19bWBpFIJOjEjy7o71NTU6HVarFw4UJB36WkpMDDw4PFyz169ICPjw+2bt2K9evXk0uPjNUB6PDI1z1VI5fLTYoD3d7ejpKSEty4cQNfffWV3nh3lvQps9/a2tqQk5OD7OxsrF+/HiKRCA8ePEBWVha+/PJL8o2bmxvmzJmD7du3Y9SoUYiNjSXtZmw8DR8+nHNclV5bC4VarUZDQwOuXr2KFStWsAy8umUL0W300Uzz0rNnz9DQ0IBly5Zx0hg08OiZb+zs7FjrRmN6BX26hQmZTGbw8gOhcs1cOxBgggHLmrBUYAv59vXr1/j+++95y7aUHr4YWBRFsXbMRo8ejYiICELD5MmTMWnSJMFl8KFnz57YsGEDtm7dCpVKhb59+2LRokWCv09NTeXEwLIEfG1WUlKCvXv3cp5bo93NpckYKisrBfOKMejWiRk7jpmGKSyfPn2K8+fPm1yWSCTCunXrIBKJUFlZafL37zt0+88Yv2VkZCAzM9NgfoYMIEL4paKighNvTei3xkDn0d7ejm3btvG+N7aTGhMTg+HDh3eKEQEAUUZtcb08jfv37yM1NRWbN2+2Sb2YcYlojw4aQvvV1N1zXcTHx7NcxK0FiUSCjRs3kh03faAoCgMGDED//v3NauM//vgDffv2xZo1aywhVxDoOBG6oCiKZVDqKmDygr54a+YEvOfLXxf65nLmbXJ835aVlVltTjUHtjAIGMLMmTPx6tUrUudFixYZ3TAxBL4YWBRFCY6XZC6MyR1LdTbm93zxb3T1aF0ILVskEmHDhg2C0ltap+joaNax3jt37mDXrl346quvyLPa2lr88ssvJN4ac/1gKa/aQo9+9OgRHjx4QOZMa4xbhUJBYtbZAtZsx99//x3Dhw/HqlWrTM6HjiU7e/ZsjgzojLWvIYwfP96kvjx//jx69uxJ4sDaes22d+9eEpvM1ptHNCiKQnh4ONFzhODp06d49eoVvv32W6OhDmjdhhl/1Nhagpnul19+wYQJE8hGvTVAh+ChHSd0YzfSZdPzDZ/NY+zYsRg7dqzeMjpDrhnVeuhzucxgfAEBAVixYoVNGGzJkiXIzMxklRcZGckJbMcHXZc2fQHImC6QpgzI5uZmHDlyBGPHjuWc3U9NTUV+fj75Te/eMYWFkJgexnYR6FgP9FEuup2mTp2q9zgMAJw9exZ2dnZYvXo1p07MYN2WICUlBWVlZZwytFotDh48yEmvz+Lc2fD09CTxPHRhaiBh3TpNmTKFN94afZ767NmzkMvlnDZraWkhHjaGYGqcGGtg2rRpeP36NWuMKhQK3vg9bxO6tBijLTw8XG8MOR8fH6PfG3v/4MEDvHnzhtPXFEVxLpwwB0xvnOXLl/PyhrEr62m51VmgabSlAYuiOuJzWLtutFyLjIxETEwMNBoNDh48iLFjx7ICZOq7Op6J7OxspKSk8L6j4xIZAt98Yw0IbTOhXrv6ICQmiTGMHj0aRUVFLLnk5eWFjz/+mLXbKJFIsGLFCt6YPfQRClso6uZCJOpw9T9w4AD69u3LCfpeUFDAirNCwxo7oPPnz+f16DV2MYG3t7denc2QgeJdhUgkQlBQEJHtT58+xZ07dwivGTIwMsdsW1sbDh06hNjYWE67P3/+HNnZ2bapwP+HsUWqNQyDtB49evRozlG6tLQ0vHnzxqIy6HJMMXZZuyxmn6ampqK0tBSrV6+Gq6trp2zaGvLoMFb+33//DRcXFyxevNiqc6a18rKVbNb1mANMX7MVFxfj8uXL+PDDD+Hr62sw1pE5eP78OeuCJiZ0Lwjhg6l9MHbsWLi5uWH//v1YsWIFr9y3hNdoNDU14ejRoxg/fjyJN2UKKisrcebMGd53NTU1rONzujBHd4mIiMDw4cOxb98+zJkzR2/8OOB/soBZJyFlVVRU4OzZs5g/fz7hJWu0tS4tQMcGDF8b0TrSvHnzON6xtt5MEVIfowYsb29vlJeXo6SkBFFRURCJRAgICDD5VkAhoPN+/vw5qquryTGmwMBAci7V2Pc07O3tDcZG4vtGF7pModFoUFRUBE9PTw49zAj/QEcsLzooJ9DhFZGZmWn0aJYxesRiMTFUSaVS4tZOx+nRd+SuoqIC/v7+nDYRIvSEora2FnV1dZwyNBqNYIH0NhYNMplMEK+YA29vb4N5V1RUwM/PD0FBQSzlkXlm2xxYc6dUd1Hs4+OD0tJSlJaWYvDgwUZlgrUUDlsvKh0dHW3GB0DHWKupqeGUodVqLTZEMtuGlhH6rn3Wd8PJ24I1jS66PPL8+XNotVqzPWuMeR0VFxdjyJAhCA4OJjGwjI15PjQ3N6OsrIx1uyEN5q2jnQ2tVou0tDT06NHDrFsBaegbu0qlEunp6QgPDzd4a5QQeHp6orGxkcSalEgk8PHx4WzsiEQiKBQKThxJ3TTGYG25ZixOS0lJCYYOHYrg4GBWWn0xpaxBm5+fn0HlXB/kcrlNZakhvC3jI1PnpHerRSIRUlNTDV4vzqdjTpkyhcW3FEXxxiMxFZ11hNMQtFot0aPp2J00mDczdmWoVCo8ffoUffv2NXp7sJOTEwICAjp1PJgj19rb2/Hs2TM4ODggICDA5LhX+mDt8WhLD8HW1lZkZGQgMjKSw5vG6MnLy0NpaSn8/f0RFBRkkVesPjQ1NaG8vByDBg3ivLPFKQx6jBYXFyMtLQ3h4eEcPcBSYzcd087Pzw8KhcKs+HDt7e0oKirC4MGDOe0uNOa1KXB2dkZQUBD8/f2Rm5uL1tZWThw0Wrfp1auXIN2Gybv5+fkoKSmBv78/FAoFOfZpqK0t0aN9fX0NyiehY8EaMGUMCx5hEokEY8aMIYui5uZmi88zG4KzszPGjRtHfre0tHCCTJoKiqLQ3NwMe3t7QcJFaN1aWlpY50VbWlrw4sULFBYWkmtjExMT8eDBA4SEhMDR0dGsBatGo0FbWxu5ESMgIIDclJWQkIDy8nKTYkYxYzbYChqNBs3NzYIGV2tra6d7gOgDRVFoaWmBXC43uttsKlQqFdrb21n8zNwFsEa/WGNX0VheUqkUY8eOJbysTyZYcwevs0HLDN14GtYCHXfLUiOOkLbhi4vVFWBpv8pkMjg4OKCpqQmOjo6QSCQkBs+DBw/Qu3dvg67OtqLN1PlGLpdjxowZnD6yxsJVF21tbZyjzLpQq9VoamrCxYsXMXPmTDg4OJjNP3zt2N7ejurqaiQlJWHNmjW8njkajYYooHK5XG8MDibEYjHi4+OJ0tfU1ETmTEM00fOP0Lgn1pBHSqWSXIBiSn502ra2Nr3Xt9NQqVRGA6fT40XIzWhMmk2BLedUJqw5T9A0G4ufqjue6I1LlUqF//znP0bjUhpDa2urVWKd2cJL0xBkMhkcHR1ZslkfdPVo4H86qqW6v7WhVCpx/vx5BAYGGjRgOTg4oFevXiyvSaVSCaVSSWSSUqkk/GXu2sBSqFQq1NXV4ebNm1ixYoUghwHgf+PDEG9aezzS+pglZdHylslbSqUSlZWVSEpKwieffCLYw4Rug+fPn0OpVJoU1oUJen5l6tEqlQotLS1wdnYmY8fe3h4zZ87k1NUanot88PHxwdSpU/HLL7/A3t4e9vb2pP1bW1uh0WggEonMsge0tLTg1atXyMjIIDGvdCGXy2Fvb09kiFgsJnqBbgzgSZMmcU4Z0LFkrQ07OzvMnDkTBw4cQENDA7y8vEgbtLe3o6amBklJSVi9erXR20qB/8nmlpYWZGVlobq6GsuXLxdMD73Wthba29uhUqlsaufhg0meZEITqlQq/PDDD9i6dSu2bt2Kn3/+2aYTYWVlJSlr69atvEfQhIKmU6VSYceOHZyBri8+k1Ds3bsXd+7cId/t2bMHcrmcFbB93LhxmDp1KrZt2ybYoKOLvLw8/PjjjyQwtaV4/PgxbzwefTCH5sLCQmzfvl2Q8nXq1CmcPXvW5DJsAYqi8Ouvv+LZs2cmf2fsfUZGBn755Re9aVNTU7Fz506Tyn0bUCqV2L59Oxmj//3vf0mdbK0kmxufzFS0tLRg27ZtrAC+1kRZWRm2bdtmk10i3foePnwYV69etXo5hsrsDAwePBgrV67E9u3biedDY2Mjtm3bhlmzZrGuq+8sUBQFlUqFH3/8sUt6FVy9etXoEeXXr19jx44dUKvVOH36tF4XfcC8fn/48CGOHz+OTZs26fXuqq6uJvLl8ePHgsrUarX4+eefyXc7duwQNP8cP34cFy5cMJjG2vydmJjI0m1M1UVOnz7NirnGh8zMTIPzDdCx+bBt2zZBu/gpKSnYt2+f0XS6oON5ZGVlmfzt28Rvv/2Gp0+fGkxz4cIF3niXloDZXwcOHEBSUpJV8zdWpjnvdTFw4EB89NFH2L59u9E5dO/evZxj1ObyWlcBHdPvjz/+IM+Sk5Nx/vx5bNq0Cc7OzkhKSsLWrVuxbds2m28o60NqaioOHDiAb775xuBxKz7s3r0bjx49MpjGWnJTqVTihx9+ILdHm4uPP/4YEomE1S8pKSk4d+4cNm3aZJIXED3fhIaGCg7YzofFixcjKCiIpUc/ffoUCQkJevvF2uNVHyQSCb7++mtkZmay5ptTp05h69at+Omnn8wKA5GQkIDm5ma9xisAGDVqFObMmYOtW7cSQ1RlZSW2bduGZcuWCbqQxhAsbaPly5fDzc2NtZZ++PAhjh07hk2bNpl8ZH7nzp3w9PTkDdhuCHl5eURfswaePHmCXbt2kd9vQ7c3BqPbwpcuXYJKpcL69etZz1UqFfbs2YNZs2YhIiICnp6e2LNnD6eS48aNQ0BAAJycnLB+/Xpcu3YNjY2NrDQRERFYtGgRiQUTHBzMKa+urg67du3CihUr8PDhQxILQKlUIikpCffv34dYLMaqVaswceJEFBQUcIwzEokEq1atgo+PD8tNjW/g8D2j65CYmMiy6E6ePBk5OTlkoUK3AZ8XC/OdrqtcaWkpMeDU1dVBo9GQOkyaNAkhISFYuXIl/vzzT46wiI2N1XvFLtBxU1lOTg6rTXr27ImlS5cKjr9jzDIaHx+P4uJiVhleXl5Yu3Yt/vzzTwAdsck+/vhjHDhwgDPQYmJiDNZBF3///TcqKirIrvGJEycgk8ng7OyMpUuXCsojNjYWwcHBHF4RiURYsGCB3slco9EgISEB7e3tZEd67969EIlECAsLw6RJk7B27Vrcu3ePdUMN0NEGq1atgkgkwty5c/Hy5UtW+T169MCyZct4A+sJBZ8bJkVRSEhIQFtbG9rb26HVarF//34Sv0NfzDhdXLhwARRFccZoe3s79uzZgzlz5rB28Mx169b9ztiYDQwMxOrVq3H48GGoVCoEBwdj8uTJ5L0hGuggvLp8YGdnh7Vr13KO0ZhTJz5ec3d3x7p163DgwAFO+nHjxqGwsJCknzhxIisWEh2fkH4/ffp09OrVC0uXLsXevXs53hSjRo0yePTbnDqNGDECISEhvONn8eLF8PX1tekFA4aOuvLJYSH5CIVYLMZHH32Ex48f486dO6AoCmq1GleuXMGtW7cgkUiwcuVKDu/MnDkTeXl5pM3mzJmDyMhIg3NoYGCgIO8YoXWi40Xp9ptYLMby5ctJzDfmGDfkmWSs7QzFbjD0vYeHB9avX09koSEZcPPmTVRVVXHkEh2rbNy4cQgJCcGqVavMmkMNySNDcHd3x/r163Hu3DnOArV///6YN28eKIrCgQMHiKcNRVH4888/IRaLERgYiJkzZ7J4jcawYcMQEhKCxMREAP+LT8jsV3q+SUhIAABeXrO3t8e6deuIEcbZ2Rnr16/H1atXOUeOIyIiOAs1ZluMGjWKowcAHf21ZMkS+Pj4oLi4mFxcUltbC4qiSPqpU6ciLCwMK1aswL59+6DVaqFQKFhyLTg42OAcStdp/fr18PT0xODBg+Hv789SyGnMnDkTfn5+JP2ZM2fIhoJIJML8+fPx8OFDjt7KxPjx443qnK9evdL7PZPmdevWISUlBbW1teT56NGjUVBQYPCSESHQlS1+fn5Yu3Ytjh07xjHyDh06FD169GA9M2ce1/X6cnR0xPr165GUlGRQjwY6+Ds8PJzVrq6urli/fr1gryxjvKbVasnGTk1NDUQiEXk/a9YszvEf2tNu/fr1uH37NicMR3h4OD788ENS9+joaAQGBpI8IyMjsWDBAtKWI0aMQEBAAE6dOgWKovTKFa1Wiz///JOlc+7btw8ikQihoaGYOnWqoPbQBd03zc3N2L17N2+aIUOGYMiQIVi/fj0uXbrE8vSYOXMm0tPTeQPy65PVSUlJyMnJAdChN16/fp3EjFu1ahVHt6EhlUrx8ccfw8fHh+NVY+wItqH1GBOGZLyhMuh1qC6kUilWrVrF8cLWLVskEiEiIgIeHh5ENoeGhrJiyw4YMAC+vr68cmzKlCkICAggt7jr1kW3XH3zTUBAACtgOLPOkydPRn5+Pv744w+sWrUKU6dORUhICG7fvs3bJnxl67bns2fP9HqPLVmyBG5ubpw51NXVFevWrYOzszNEIhF8fHzI/MWsPwBERUVh9OjRemnSbaOLFy+isLAQFNURO/X8+fPk9saVK1fy5sHslwULFpC68eWvD8zN/5SUFKSlpXHSiMVirFy5kne+8fPzw+rVq8laWzdvPjqY843uBW89evRgraP5vq+rq8OxY8cAdGwat7W1EZri4uIwYMAA1lgzV0/UB6MGrJqaGshkMo4Qb21tRWlpKTkKFRgYSGIzMBEYGEhcMQMDAxEUFMRRiBQKBXx9fUFRFCoqKhAaGso7aZSUlECr1cLT05Oc12Se2xSLxRCLxfD09IRGo+Gc6ZRKpQgMDGS5MTMbTK1W4/Hjx+jXrx9cXFygUqnw5MkT9OjRg8SUoevAVGYCAwMhFotJPQcPHsx75tXJyQmxsbHE5VG3o+zs7HjrBXRM+vb29qSddZVvhUJhMEaJt7c3WlpaWJNtUFAQgoODERsbq1cZsLe3R0xMDDmO4ejoiNjYWHKUg1knZ2dnUBTFot3LywuBgYGIiYmBl5cX7O3tSTwAXQNWYGAgPD09oVarERMTw+vC6+Pjg+joaIhEIvj5+RG6mEGTaffWQYMG8bpCOzg4IDY2FnZ2diStbnuLRCIEBgbqvXJWJOqIo8Jn7fb29oZIJCIxx3QXfgqFgpwp9vb2RmtrK+rq6sh73X4JCAhAVFQULx26daL7hclbYWFhZBdAoVAQRZVpDPH29oZYLMawYcN4dww8PT0xbNgwiMViVFdXw8nJicPjLS0tKC0t5Ry30GdsMrYgN/Uoop2dHRkfKpWKLMKHDx/OOzboOolEInh5eaGtrY0Tb4jOUx8tdnZ2iI2NJXxEjxe6z43xmru7OwIDAzF8+HCOsdTDw4Ms1gBwxqinpydUKhVRHGnXbroNdNs3MDAQ7u7uUCqViI2N5Rx90K0jvejTBZPXHBwcIBKJ9I4fuVwOFxcXxMTEGDw+IhaLMXz4cLi7u3PeeXt7k37SBf1MLpcjNjYWeXl5aG9vh6+vL6tfjEF3vDCP/ISFhbECsffs2ZNcsx0QEMCKN8Y0gNDzhW69vb29oVQqCa/Z2dmR8WRoDm1tbRVUp6ioKAQEBHDay8nJCTExMZBKpXB0dGTxFg3aYEIr27pjnJbNxo5E9e/fn7WLrdtPQuWaTCZDQEAAaUNDMqCurg4NDQ28NJeXl6Otrc2iOdTco9FSqZTwia6npUKhIBtqCoWCKN9M2ezl5cXLa/T3QIfySMux9vZ2ltFYoVAgKCgIMTExcHFx4eU1el4eOnQovLy8iK4UHBzM0deCgoI4sorZFm5ubhw9APgfb8lkMrS0tOjVdRwcHIgcCwkJgUajQWBgIEuu0fFHgP/pNsw5lFknkUgEmUwGiUTCG+cjMDCQyFZaN2EasAIDA9G/f3+Dx1A8PDx4dU6mDPDy8iJzqC769etHjtjROibzKIxCoYC9vT3RRwYNGsS7ucaUzXS4CiZoY9LQoUPh5+cHuVxO2lXXgKVQKODl5QWNRsOaQ5k6M3MO5YNI1HHxUGxsLN68eQOVSoWwsDBSJrNNdfVoAHBxcYFCoWC1q6urKxnjYWFh8PDwIDTxLYAM6dUODg4sOaj7ns9oLxKJSD8FBwdz9FSFQsHSO11cXCAWi0neuuPHzc3N6K2eNIKCgniPswq5KIQJXeOSn58fhg0bpje9m5sbSybQBix6fKjValKnIUOGwM/Pz2A99K3hJBIJRCIRR7ehQa9FxWIxvL29yVqApoVG//79WbqEPh20ra0N9+/fx+DBgxEUFMQ5om5I5tNtSBsp9R0zpuv04sULiEQi9OnTBwDw4sUL1NXVscaP7lqaKeeAjo0FqVSKkJAQjp6gUCjg5OQEsVjMWrPpgh4n+uYbT09PiEQiDBs2jMw9zHdqtRqVlZVkvjEWG9OQIXPgwIEG5Sqth9B6AD2HMmUAAJYc0zVgKRQKeHt7Q6vVctY3uhugtDGMBlOfk8vlEIlE6NevH0s2i0QiVr/QtBrTbXTBlM36wgLQNg6++cbX1xcKhQIxMTFk7UDPN/rGIj0n6843QIesMXaU2FD8aBcXF16jsSHok+F60xtyC/v3v/9NnTp1CkDH7j8T7e3tSEhIwOLFi60W0J0+fhcZGckJUkffMvDpp5/a7Dx8a2srfvvtN8yaNQt+fn5obW3FoUOHsGzZMsFnwg3BXE+UbnTDVNiS106cOAGpVIrx48eznre1teHgwYP48MMPWQFAu/n+/UZX6t8DBw4gKCgI48aNe9ukdMMKoOM6HD58GMOGDSOxhfhw5coVVFVVkbiQzDwOHDiAadOmGQymbSq6Et934/3G+8BrBw8eREBAACZMmGA0LUVR+OmnnxAVFaX3ZuD3BW1tbSgpKcHp06fxySefGA0Kb2u8D7wmFLW1tdi7dy/WrFnDu3lmTfz999/QarWYNGkSgA5PHxcXF5a307uGlpYWvHz5Erdv38bnn39ukzix/xT8k8adqfj3v//N2zBGDVgURSEzMxPHjx9nvbO3t8fmzZsNWvfMAUVRuHbtGstNHuiwlq9bt66DaBt1Mt0We/fuRWFhIZycnPDtt98SKzsznTVo6MoMa+jIR1el+X1EV+Q1iqKQnp4O2rhNw9HREZs2beKMF3Np6+a1zkVX5DVTyzLVbVtfPt2wLYSO+YqKCvz666/417/+ZfQKeoqikJ+fj/3797OeSyQSfPfdd2QH1do0v618uiEM7+L80pVpMxemHul+HwxYhjzDaFy6dAkFBQXk6HNn9/v7yGtCwVz/2roNKIpCdnY2OXK1fPly9OrVy6p6uakeLJbi0KFDkMlk5Ej5P5WPbIGuPC47m9fMNmABHTsEuu7ZYrGYHNExBHMq2NjYyImmL5PJSCR/ayokfN/U1NSgvb2d1BGwzsC09AyotY0Qtq6TuTFDzKXB3Px1v+dzK+0sdOYiSWjf8aVrbW3lxH7QJxPexsLPGmXy5WFtXrM2zebQ8rbzETrebHGG3tb5mFOeIXlkTp62lrv6fhtKKxQqlQrV1dXw8fGBRCIxmg99+w8T9LEAsVj8j+I1IXqStXnNVugMXrNVPsbysiWvmaODWUuP1n1uKq9VVlbCwcGBc7TlfeO1hoYGqFQqvTeVWVOP/qfxmiXlCP3OlLyZa2kPDw/OpsrblmumtlttbS1EIhGv99rb4jVb2wdMocXWZZnyfVebQ02hxyIDli1gym6MKXma0oGmPDOFBsB2HgCm0Ps2lNHOXvgJgS14TUiZ5vantcsw97tuXjMP7yNdncFrgO0CrlvynaXf2hLvI13W7l9L5viuzGvm6DuW4G3Mo0LwT+Y1S/vEHJ3Y2jqzPrqArqmzdZX8O6MfdPMGbL9me994rbPq8C7xrpDygG5eMyf/d53X+KDPgMWNJmkAhoxdpkIkMu2YkdA8TX0v9JkpNJjzvdBvTKHX3HpYWn+hMMRPXZ3XjNFnSX8Kha37t5vXzENn85pQdHVes/WYeRv118W7zGum0N6VeM2SOb4r85o5+o4lsPY82s1rltNiaZ+YoxNbW2fWR1dn85qtF2W2Wu8w62bLOnTWmu1d5zW+/GnYitc6i3etrb8YKq+b18zLn8a7yms0hPCaSQasrrbz9r6js4TF24IhfurqvNbV6TMVpvDau8iX3bzWdfC+85ohdPW+fJfHCR/+ybzW1fG+8Zop6Oa1zsX7zGvvOv3/JLzrffWu0/8+wVpOFF0VQuiXdgIdXQaWutw3NTXhzz//xJIlS/SeVxea582bN/H8+XOIRCKsXr0aDg4OnLSd7bIppDxrnCM21wXx1KlTKCkpgYODA1avXv1WB6hGo8Eff/yB8ePHIzw8nPOez/ptzF2VD6a0lSVpTWlL3bSm9idFUXjy5AmysrKwfPlyvd++TV7rTFAUhb1795Kr28ViMVavXs17hbehPCyttzXbSqvVYt++fYiLi0NkZORb4zVdHDx4kBXPceXKlXB1dTV5PBYXF+PMmTNYs2YNua7amu337Nkz3L59GwCwZMkS3mvS6+rqcPDgQSxfvtzmNygx0Zm8Zo5s0eU1jUaDPXv2YOLEiazrwnVhCa/ZIq2h+eJtHec7cOAA6uvr4enpiaVLlwr6JjExEXV1dfjggw9MLq8r85ol6CxeEwpTdGOh5Z88eRIeHh6c24ptQV9XQVfkNSFlp6en4/Hjx/joo49srj9Zk9esTV9NTQ0OHz6MVatWwcXFhZSxf/9+REdHY+DAgVYry1K8C7wmBGlpaUhJSSG/o6OjMWLECKvk3ZV5zVCet27dQnp6Ovk9fvx49O3b16Ae/ebNG1y+fBlr1qyBTCZDXl4eLl68CACYPXs2QkJCOOW0trZi3759+OCDD+Dv728yneamM/SNKXlY1YBlqbJp6/PbxvI29l6r1aKyshJqtVpvvsYMAxRF4f79+6AoijAkffWoJW0nFO3t7bh37x6GDBlCBLRu2YbqZE6Zut+Y2ucqlQp3796Fu7s73NzcIJfLTabBUt7kQ1VVFZRKpeBvzVnEC+0XU/I0Na218xKJRGhpaUF1dTXnnS15zVzYgnd001dXV0OhUMDf35/IhOzsbLS3t2PQoEFGy7JGvZ88eQIPDw/07NnTKrK5qqoKbW1tb5XXmKAoCr169UJLSwtaW1vx6NEjaLVak/Kl07W3t6Oqqor3+Abdn9nZ2YiLizPpamm6nT09PdGrVy/cv3+fNd8wodFoUFVVBY1GY3L+lsAavGar+YYvPd0ffLLa1LyEpuWr04MHDzg09OrVCwqFgvWsvr4eT58+RVxcHGQyGSf/mpoaPH/+nPz28vJC//79BdOqj0ZDaG1txcOHD6FQKBAYGAgnJyfB+Tc2NnIuARJKV1fmNWOorq5GRkYG65lEIkFcXBzEYvbhB6ZcuXfvHqKiouDq6sqbryFea2lpwaNHjzjfxMbGEkM7jZycHJSVlQEAi9eYYPJav3794O3tzSlfrVbj7t27GDBgADw8PMjzsLAwNDQ04O7duxgxYoRJi0NTdRtz5Zq11xxvi9dMqQdfutbWVl59TIhcs4aM1n0v1EBvjT5n/lar1aiqqiJ6AY3q6mqywWgu/om8JgQtLS1obGxETEwMAMDX15fMN8OHD4eDgwOn3KamJjx58oRXrjEhlH+sYR/gg7lyzd/fn+h99+7dM6pHZ2ZmorKyEpGRkeSyGhcXF/Tp0wd3796FSqXSS19lZaXe93x08tXp4cOH8Pf35zWSGYMl9gGrGrAsFWTmDgp9leQbxMY8Y0yFOXVISUnBmDFjMHz4cME08L035xulUonExESEh4ezDFiG6mDpotFQ2wuBWq1GUlISPvroI4SGhppFh7UmWWZ96FtEdJ+bYnTSRX19PaRSKe8CwdbjxdB7a+9OODg48HqNvE1eE2J4FAJz6xAREYHo6GjyOysrCw0NDQYNWJbwmi4ePHiA8PBw9OzZ0yaGQD50Bq/REIlEZGevqqqKd6EnFDKZDJ6enrwKkEgkQlVVFW7duoXY2FheA5YxXgsKCoKbmxvu37+vlwaJRAJPT0+TDGRC5JoQ6I6vzp5D9dGkr02ZslrIN0Lf6wPzG41Gg7q6OmRkZHBuV5ZKpXB2doabmxsAoLm5GUVFRUhKSsKwYcMgk8k4intJSQmePn1K8ujRowf69+8vSA/SR6MxtLS0IDExEV999RU8PT0FfWNNmWGtRVNnzaGNjY0oLi5m9RPQ0d99+vSBu7s7x2CkVCpRVVWFpKQk9OzZU68BSxdM41V5eTmnTADo2bMnvL29YW9vD4qiUFdXh1evXiE3NxcikQi9e/eGp6cn61Y0XV5zc3ODnZ0dXFxcSP2VSiVqamqQlJSEoKAguLu7k++HDh2Kx48f48aNG0Y9Kiztl/eZ13TxNj1qbK0X8G3ImSrXjMFQHeg5VdfAbCvo1kGpVKKlpYU1joTgXec1Ozs7jB8/npRVU1ODxMRE9O/fnxiwmHQ0NTUhMTERUVFRBg1YgOn2AVsZGfl+60OfPn3Qp08faLVaPH782Gj6jIwMqFQqLFu2DEBHPXx8fDBmzBiiQ/LVTSTq2CyVSoWbgvjqcPfuXURHR5tlwKJhTttbxYBlqduYuQsvY4tUfYsLfemYeRrKVwhdfIsyQxCJuNcK69JtqlXdWBpbCCdD/WrqQtVY/tagyZT8mGklEgk+/fRTowLQmADT5eGjR48iODgY06dPF1w/Pjr5ymLSYAqvGWszIYtAXXqGDRuG6Ohoi8aYbp7GeI3vORN87WIJLOU1U/M3ldesASF5vm1esxR8eQYFBeGLL74wSR4zYSqv8bWDu7s7NmzYYFZdDbW5vvQ03cz/aRjrR2vAWJ765BpTVr8NXqutrcUvv/yCjRs3EkMVjUuXLuHo0aP45JNPAHQct3v8+LHecX3p0iW0tbVhw4YNvPXnA11vIbTS9dKnE1kKIXzB14+GdIe3saA3Vt7Fixeh0Wg4/dTe3o4tW7Zg6dKlrOOsFEUhMzMTZ86c4c1PX/8x637//n1kZmby8sb27dsxatQoxMbGAgD27NmDcePGYdq0aaAoCt9//z2mTJmCqKgo8o0urx09ehRZWVn48MMPSZm5ubk4fvw4q10s5TVL+tXUb0zltbcBa9NnrH+ELPjNkZVC5jxj6zO+56asF43xmpeXl+A51Ra8lpmZiatXr2Lz5s1vhee6yprN0PdC5yM+YygTfPwjRA+ydJ1grkHWlDHHlMPMZ7r5OTg44Msvv3xnec2oAevMmTOQyWSYOHEidu3aRVwr7ezssG7dOojFYohEIrS2tmL37t2c7z/88EP4+PigsbER+/btI8/d3NywcuVKJCQkoL6+Hh4eHlixYgWpSFVVFQ4fPszJb+3atXB0dERJSQlOnjyJdevWwc7OjlPRN2/e4MKFC1i/fj1kMhlevXqFCxcusNLI5XJSh+zsbNy+fRvr1q3DiRMniGs1ACxevBh+fn562yghIQG9e/cmu0yGhLC+wUdRFKkTE2KxmNThzZs3uHjxItavX4/Lly/j9evXJN3cuXM51k9TJouDBw+Sndw///yTPHd3d8fKlSvJb2P9UlZWhhMnTmDt2rVISUlBVlYWSTNlyhT06dOHlHvhwgXk5eWx8gkPDzdovNGtz7Nnz3Dz5k3WMxcXF3KW/+HDh8jKymLxGo2PPvoIrq6unDr5+vpi8eLF2LNnD1pbWxEQEICFCxcSoVBUVIS///4bADBt2jT07t2btPX58+chEokwZcoU7Nq1ixzvYfKaSCRCW1sbdu/eTfjBwcEBa9eu1VtvGjSvRUZGIiEhgTz39PTE8uXLAQDJyckoLi7Ghx9+yPpWq9Viz549GDVqFNzc3Fi8FhwcjNmzZ2PXrl1QqVQICwvD7NmzSZvn5uaS89Q05HI51q9fD7FYjMzMTKSkpGDt2rU4fvw4ysvLSTpaBtA4ePAgampq4OrqilWrVpklPOlv6DrFx8fDxcUFp06dImlCQkIwd+5cAFxemz17NlQqFS5dugQAmDdvHoKCgki+x48fR2lpKavMQYMGYezYsQCAw4cPIzg4GIMGDcL+/ftJGlquiUQi3L9/Hw8fPgTwP14ztX66ePPmDc6ePUt+9+zZk8VrvXv3xrRp0wB0jP/s7Gxcu3aNlQfNa0La/cGDB3jx4gVWrFiBP//8Ew0NDeTdxx9/rNeLkwlduRYcHIx58+YRGt+8eYNz586xvpHJZFi/fj0kEglevHiBpKQkwmtMuXbs2DH4+vpi+PDh+OOPP/Dhhx+iqKgIycnJJK/o6GiMHDlSL30pKSl48uQJAGD16tVwdnZmvddtp6tXryI7OxtisZjMP6YoNKdOnUJRURHrWf/+/TFhwgS93+jm/fDhQ7KztnLlSo43Y3JyMlJTU1nP/Pz8sHjxYgDAjRs3UFtbi/nz52P37t3kaBs938jlchavzZw5Ez179iR56S6CKIpCVlYWrl+/zipTl9eampqwd+9eTv1WrFgBDw8P1NbW4sCBA1i1ahUyMjJYu4/x8fEYMmQIKIrCH3/8geHDh7O8E+k59O+//yb9UlhYiNOnTwMApk+fzopXeO7cOUgkEkyaNEmvbkNRFNra2rBnzx7O3K0r14TA0KJxz549GDhwIBYuXIgTJ06w3mm1WuzevRvDhw9H7969TVKAzVloCaGZhjFeo0HXQR+v0foan24THByM9vZ27N69G3PnzkVQUBCLvvT0dNy/f5/wWlpaGokhR4M539y/fx85OTkG5VpFRQWOHj2KNWvWcDyir127hoaGBsyfP99g2wht95MnT8LBwQHr1q3Dnj17BOcjZMHP5Nvy8nIcO3YMS5cuhZeXF0m7fv16pKSkIC8vD3PnzmXxGp3nzJkzkZeXh19//RXr16/HhQsXQFEUPvnkE6L76/Ja//79ERAQgJ9//plXjxaJRGhoaMD+/fuxfPlylncfnU9NTQ0OHjxI+qWyshJHjhzh1HvdunXEW0OX12jQ+hqzbXTb68WLF7hy5Qrrmb29PdauXQuxWMzitaNHj6KyspKkW7ZsmdH4uHfu3OF4Vfj4+BB9LTExkXVEeOLEiXB1dSW6zZQpUxAREUHem6pHCzEgAyBrNrpOhhabur9v3brF6wlIY9KkSejbty80Gg12796N9vZ21vsZM2YQAy9FUTh79izy8/NZaSIiIjBlyhSIRCIcP34cnp6eGDFiBGv8ODk5kXi5hmgWiUS4e/cu8coWotvQc+jChQsNpqOhy2unT59GQUEB+c0XIzA9PR1JSUmsZ8w6PXr0CBkZGVi1ahUOHDjAOqq9atUquLm5obq6GocOHSLPfXx8sGTJErK+8ff3x6JFi0g7FBUVsfRooGPDnl6H5uXl4fLly0QGMPtl3rx5CA4OZq1v+vXrh4kTJwpqIz6YOh/duHGDc3Q7KCiItO/FixehUqkwe/ZsFl+oVCrs2rULM2bMQFhYGCdfuk7z588nHmI0Bg8ejDFjxgAADh06xHscl15L6/IaDaYM4ANfO2g0GuzatQuxsbEs3YZPruk+Y+prCxcuREBAACu9sTlUCOg5VJfutWvXElmtjz4hhkKjBqympibU1tbC3t4eAwYMYCmsSUlJGDp0KFpaWvDixQsMHjyY8/3z588RHBwMHx8f1NTUICYmBk5OTqAoComJiQgNDSWE3rhxAyNHjkRRUREKCwt587t//z4iIiKgVqtRU1NDKlhbW4unT58iPj4eUqkUKpWKvE9PT0dVVRUnP4qicOvWLURFRUGpVKKyshJJSUnw9fVlTbSZmZloaGjgTL6tra24e/cuQkJCSAA0fQ3+8uVLFBYWAujYhcvJyUFjYyNEIhFGjRqFV69eoaysjJfGlJQU9O/fHyqVCtXV1bh58yZcXFxYaV+9eoWmpib069ePPGPS8fjxY9TX15Ozro8ePYKzszPs7e0RFxeHhoYGZGdnQ6lUsvKl+yUuLg4lJSUoKCjQ2y+0y2N1dTVu374NuVzOSltaWoq2tjYMHDgQt2/fhrOzs94+iY+Px7Nnz1BTUwO1Wg2KopCamopXr15BLpdj1KhRePz4MZqamnjzSExMRGxsLFpbW1FaWsriNWab9OrVC3K5HDU1NRg5ciRZjCYmJpKFAv07Pj4eMpkMzs7OGDhwIJKTk8nEy1ygVVVVITk5Gf3792eNF5rX2trakJ2dzVl8JSYmIiIighMThZmmvr4eWVlZaG1t5fRTYmIi4uLi0NLSwjLUMVFbWwulUgm1Wo3a2loyXmj66LhsFEXh5s2biI+PR0ZGBqqrq3nbmZYBSqUSFRUVSEpKgp+fHysg4PPnzxEUFEQUx4iICGRlZaGmpoa3jqYatGpra5GWlgZ/f39OmyQlJWH06NFoamqCWCwmQTjpBRKdPjc3F42NjejduzeSk5Ph4+MDX19fVjlarRYpKSkYOXIk6uvr0dzcDJVKxdsPI0aMQEtLC6mjbkwFIejbty9HsVOpVKirq0N8fDwkEgkoimLxmlarJePn6dOnqK+v1zs+hg0bhvr6erx8+RJAx/GlgoIC3LhxAwA44ycsLIzVN48ePUJ4eDiCg4P11iE7Oxt1dXW8/RIfH4/s7GxUVlbqlQODBw+GUqlEbW0t785xQ0MDnJycoNVqUVNTg3v37nFkY3t7u8E4LKb2U0hICFpaWvDs2TMWTVqtFrdv34ZGo0F1dTU0Gg1u3rwJsVgMX19fREZGIjk5GZ6enpxFjlarRXJyMkaNGiWI//38/NC7d2/cv3+fFQOLoijcvXsXarVab7+PGjUKzc3NKCwsRFJSEiIiIlhyip5v2tvbSbvo8qEujWlpaYJ4LTc3l3f+SEtLQ1hYGFxcXFBTU4OUlBQ4Ojqy0tbX1+Phw4cYNmwY6urqOItTkUjE0guAjs0MXVlNo7GxEbW1tUhJSTGq2/Ad49WVa7r1NlWO9e/fHyEhIZyYZ3Q+tbW1kMvlrIWVkN1jQ0YzYzTev38fzc3NaGtrA9ARj8Pe3h5OTk6IiYkRxGtAx4UDxnjNkG7T2NiIXr16EX1AF0qlkizeHj16ZJJeoE+uSSQS1NTU8MqE5uZmltFLFwMGDBAkS1QqFVJSUuDt7Q1/f3+zLmOg+7FHjx5wdnY22K8ajQY1NTVwc3NjXRLy/PlzFBUVkUUFH6+9efMGOTk5qK2tBdBxdFUikbDiXumWa29vDzc3N739BoDIblqOtbS04N69e4iLi4ODgwOhWavVIjc3V+/a4N69e4iIiICjoyOePHnC4jUaRUVFaGlp4f0e6JBDuvMV8D+ZEB0dTXSdxMREBAQEIDAwkKR79uwZQkJC0KtXL16PiXv37qG9vV0vb44cORLNzc1Qq9UknEBFRQXKy8vJN6WlpWhtbcWgQYOQnJxsVI825zhcQUEBXr16hcGDB7P4RKgXCLNNdOlKTk4mc3paWhrnkhe6/JaWFvTr1w+3b9+Gm5sbp470fBsfH4+GhgZUV1dDJBLx6mMxMTFkY0pfHfz9/REeHo4HDx4YHLu03JJIJLwXOBmDWq1GcnIy3N3dWWPn5cuXEIlEiI+Ph0gkwuPHj9HY2KiXV2JiYtDW1oaysjIkJiYiJCSEFWLlyZMn6NmzJ+zt7VFTU4O4uDhyhFjf+iY3N1fvOjQ5ORkDBgyASqUiR511+4XWo3v06IHa2lpotVrOsXkhcHBwwPjx4+Ho6Mii4d69e2htbSV53rlzB3K5HK6uroiOjkZKSgrEYrHe8RsfH4+mpia0t7dzeICiKNTU1EClUqG8vJxlBAsICEBoaChqamrw4MEDuLu7s8pgrg0iIiLQ3NyM1tZW3Lt3j9g86Iva7t27x1lnM/uVXocKAUVRvLKaD7pjzNPTE/369UNycjJHNj98+BDNzc16aRwxYgTKy8vJOqqtrQ2vXr0i7RoXF4fCwkIUFRXpldWRkZEso5k5jgyCjhA2NDTg5cuXWLt2LYm30draii1btqBXr15obm5GXl4e1qxZwyHit99+g0qlIjuVw4YNI8asn376CRs2bICXlxcqKyvx3//+F0OHDsXr16/x6tUrfPbZZ6y8KKrD1dnV1ZWz81lXV4fbt28jLi6Oc56TZkRd62ZbWxu2bNlCrK0ajQZZWVmYP38+q2F///13tLW1sQxYLS0tqKioQFZWFpYtW0YEkb5OKC8vJ95IKpUKpaWlqKurg0jUEZclJycHNTU1WL16Nes7tVqNLVu2wM/PD1KpFFqtFtnZ2Zg5cyZL8UpISEBtbS3LgMXE69evUV5eTgTz69evSfyNuLg4AB3eVTKZDCtWrCD5VlVV4ZdffsGQIUPIRKrbjhRFYevWrXBxcSFt9OLFC7LLQuPEiRMoKipC//79kZKSgg8++IAzeeXm5uLGjRsYNWoU8vPzUVRURGh+8+YNZDIZHBwcMGrUKKSmpkKhUGDGjBkseurq6rBjxw5irGhvbyc33tEKIkVR2LFjB+RyOdnpGTFiBFxcXFBcXIw9e/Zg06ZNcHJyQkFBAfbt24cRI0ZAJpPBw8MDo0ePxt27d3nburGxETk5OVi7di3hRSav0QuxzZs3E+WiqakJW7duxfr16/VO/sw+ofuJRkVFBX799VcMHTqU91t9+Y0cORL29vZ49eoVDh8+jO+++w5yuRwvX77EX3/9hZEjR+L58+eQSqWcHXW6TnT70eNnwYIFLAPWzp070d7eThZ60dHRaG1t5TVgmSPEAJDbKZmePa9evcKRI0eI942Xlxdrl0Qmk5Gdpz///BN1dXXo2bMnsrKyMG/ePE4/3L9/H8nJySS/mpoayOVysqsCdAT7/PnnnzF48GA4OTmR2+NMiVNEIzIykve5WCzGqFGjIJfLkZ2djZMnT+L//u//IJVK8fz5c5w7d46MHxcXF4wbN471Pc1rkZGRqKmpIXKpra0N1dXVZLE6ZMgQAB2Lw6ysLKxYsYIcgaIoCj/88APs7e0NGrBKSkpgb2/P2l2keS0uLg5FRUXQaDSYOXMmeU9RFDlqY+qZ+ry8PERHRyM+Pp48u337Nh48eKA3Dguzn4wp+xRFISIiAnK5HM+ePWO902q1ePHiBVQqFdrb24msFolEUKlU6NOnD7KysjB79myWBwkAEitm5MiRgsZAaGgonJ2deWNk5ebmYuDAgaT/aNByjQ6W2tLSguzsbKxZs4YsUjQaDf7zn//A19eX1S58ChVzIfDs2TO4u7sTj0caLS0t2Lt3LwYMGICSkhKkpqbim2++4dTx559/BgAis3NzczFq1CgMHTqUpL18+TJycnIwbNgwo+1Dw8PDA/Hx8XpltT7d5vvvvycGk0ePHmHTpk0c3tCVa0ww6yeRSODt7Y26ujpOsFR6rNFKHwCy0cWHxsZGlscH0OEJq3s0UQiE8NmrV69QW1tLjAu5ubnEaBETE4P79+8jNjaW4+FoKq/JZDKWbtOjRw+SV0JCAmpqagzeIMlEXl4eAgMDWTIA6DDK/Pjjj8QYSesFtFyjjZ4//PADHBwcTJY9zPHA1HuADuNre3s71Go1vLy8IJfL0dbWhpqaGmRmZpL5xpzFHl1mWFgYy3OgubkZLS0tADp2//lutVWpVKitrSUbDbq74nS9qqur8erVK5bnKD1WaR62FlpbW3H79m1ERUVx6CkvL0dFRQWWLFnCeq7VarF161a4ubnBy8sLycnJ+O677zjfX7p0Sa8BDOgwnNjb22PKlCms5y0tLfj+++/JWFer1cjKysKiRYtYG12//vorNBoNevXqxTu+cnNz0a9fP5ZcAzrmyV27dhHZ5urqSnSV06dPo7a2Fh9//DGADj26sLAQAwYMQEpKCubNm8fRozMyMnDmzBmMHDmSd04zZOSsra1FQUEBCgoKON4WQr1hwsPDOcYdmtdycnJgb2+Puro60k+6vHnkyBGUl5cjIiICycnJWLJkCUfOPn36FJcvX2YZyV+/fs26obyhoQHbt29Hv379iAFLXx169OgBJycnPHjwgLdOzDpkZ2dj5MiRetdbhqDRaJCcnIylS5ey4o3u27cPHh4eZA5NS0uDn58fZs2axfqerhN9cQctx5jrUIqi8OOPP0Imk5F2GzFiBFxdXQmvbdq0Cc7OzigsLMTevXsRGxuLkpISNDQ0EH2Nlom0rPb394dYLCayetasWSyZ8+eff6K2tha9evWCt7c3tFotx6NdCBwcHAj/M/Hy5Us0NjYSg8vLly/JBmF0dDTu3buH8ePHE+MvzedFRUU4e/asXk98XZ5oaGhAZmYmqqurydxAGwffvHmDqKgoFn33798nBiy67Lq6Oty7d4/YPGh6Hj58yPkeACorK3Hs2DHExMQINmCZAt0x7+/vD1dXV9YNkDRSU1MREhLCOs0hEolQX1+PAwcOYMiQIaisrCRrh/b2dlRUVKCpqQkAMHz4cLx69QoFBQUkPAINrVaL77//Hu7u7iw7C98GtTEIMmCFh4eThR4f+vbtSyZsfUfkDMHQsToh6awFe3t7fP7557wCX7dRU1NT8fDhQ2zcuFFQg48aNQrx8fGgKArbtm3jBHHnA199pVIpPv30U04AUGM00G6ujY2N2LZtGxYtWoSAgADWd9HR0QZdPePi4jBixAij/UC7CDKt54bAHFjMiY8W5LSx9IMPPkBISIjJRg43NzcSr8acnXFT0aNHD4OuoJZi2LBhVrmaWh8M7dgbgqHxIyR/SzBx4kRW/A5zy7C3t8cXX3xBdmQMISoqiqPoMhETE0NijtgCxuibN2+e0XoMHjyYKPO//vorwsPDMXnyZFYad3d3fPnll2bROGHCBI4hhQl6gmRCSN/pq9PixYsNXgfMh5EjRxo8YiiUNqlUSiZr2rCoK6s///xzQbxlDPq+F4lEWLVqlaAyAgMDORsmTISFhZF+58tLSD85OjqSPJjHwozRtnLlStbutD4w5TlfnsbK6dWrFzHM22pu8PDwwBdffIEdO3bwesbquu4bwpUrVzjHmyIiIrBkyRKz5Tagvy+XLVtGdqZ//vlnrFixghzzYuZrKa8B/xs/usH2Te2TxYsXC+J/XblG6wfmQt9Cn6IonD9/Hi9fvoRcLsd3330HiUSC9PR0XLx4EZs3bxa0wWEKf1IUhTt37uDOnTsAgG+++YYsXplpysrKsHfvXmzevBlXrlzh8CdFUVCr1fj9998xf/58REZGcsJcWBPG2n/UqFEmyU/dRZHufKPbnnPmzBGUv6OjIysGohCIRCKsXLmS5G3t9Yy18jt27Bh69OiBjz76yGBZptSdoiiUlpZi37592Lx5MxwdHVnzgTXQr18/EvZCHw18MKUeFRUVZHNb6PrGUDmW6Do0XF1dTeZFfZg4caIg/te3DqVhZ2eHzz//HIDlfEnzGq3bAEBZWRl27tzJCs1haD4KCgoi9OjLn4nevXsjODgYW7Zswdy5c9GjRw+yGTB//nyLgpXrg4+Pj0l6tqF2NZfXjJXh5uZGaIyJiUFMTAwxlupuHAuh01KaTQrizuxoOzs7fP3113B2doZI1BED6/fff+cQ29jYyIqdIZTwyspK7Nixg/Pc0qtMzcHSpUshkUiI1ffgwYMYNGgQ1qxZIzgPc5jHlG/mzZtnchlC0nt4eGDjxo3EPbG6uhoHDhzgpKMHtxBIJBJ88cUXSExMxOXLlyGTyfDpp5+yPOf0TZCGaBYyqTLfr1mzBnK53KQrvulyOhudYXgzhNzcXM54pChK0DGJZcuWmXTLhbUQGhqKDRs26J1kafD15/Hjx1FcXMx61t7ebvGNb+YofvryEpJPVlYWZ8FrjH/fNq8JQVtbG37//XfMmDEDCoWCdYyuK9NOURROnjzJ8bLRdWk3BmNpk5OTOXFWmG1kKqzVpk1NTbzzemNjo9Fvx4wZQ+bgdevW4e7duzh27Bjxxrh27Rqqq6vx1VdfkR1Ma7appVizZg1HXt68eZMVL9AYpk2bxop/A3R4jfz3v//lzKFAB79lZ2fj8uXLevOMiorieGkyYaxdaA9HJizhNV2Yo9ukpaVxYmMKPcZNe4nxxTExFSqVCjt37sTYsWOJp7hEIrGpPkhRFPbu3Ys+ffpg48aNAMDrBXHjxg2Ul5fj66+/5vXOAoD8/HycPXsW69evh5ubGyfWkjE6bKGTVlVV4eDBg5zn9NpAoVBgw4YN2LdvH5RKJUJCQvDBBx8IpiUnJ4cT79Mamw407ty5Q2Jj0rBkvJw7d44zvnU9PXXB1xbNzc3YsWMHZs+erfcUAN/3Qvr5xo0bqKioMMhrpkBoXzg7O5P1y5MnTzgxfTw8PIwa6mgkJSVBoVDg66+/hoODQ6foGenp6cjNzdVLkyGsXr0acrlcbzgRfSguLmZdzEDDGE/pg7E1mynfG+M1Opbfjh07IBaL8cknn5Bjk/q+0/fczs4OX331Fe+N8LqIiorSe2LCVrAl/y1duhTp6elEV1uxYgXxxjcFFRUVvPqetbx2zV5VisVichyruLgYWVlZvK799+7dMyt/R0dHvR5KCoWCE89CKMyZVGnjDR3zoH///pBKpXj27BlxX33bMMdNUwgkEgnp55cvX6KgoIDTLxRFcSYGQxCJRHB3d0ffvn3h6+tLzidTFAU/Pz/eeCNC8zUlrZCg2kI9D0zhq8DAQIwcOZIV8FgkEmHSpEksmph5vu1FuYeHh95+cXd35z0OSENIoG99sMSYIpPJTI4polKpcPPmTfj5+XGUuMLCQo5Ry1SYo9Cbqzg/fvwYDQ0NnPGqVCo545VZhi6NLS0tuHr1Kif/4cOHIyQkBA0NDXjw4AFGjx5tE9dnoOPIxvXr1wltarWaHHlxdHQUZAAxBlsb7tRqNW7evAkfHx+Ol1hxcTEnSK05oKiO+BwajYbT7/X19ZzAoZ0NuVyud143dBQVAOs4kJubGzQaDXFZBzr4tLW11aw4QraGvvlG19vIGJycnDj1Kysr07tIEYk6btYy5O1tigcYH4KDg1mxV5gwtX58oHUboUrv/fv30dLSwqlza2sr73EJJkQikVnHMflQWVmJp0+fIioqCgqFgpcvVSoVR67Rx0yamppMXhQ1Nzfjzp076NOnD8LCwvSOhVu3bsHJyQkDBgzQmyY9PR1+fn4YNmwYPD09TT4Gb6lHGx9ycnJQWFjIq4PeunULQId3iIeHB4YMGQKNRgONRkMuMhk0aJDBC5mePHmC+vp6Tv60XmAIQjaGUlJSoFarOfnTc6g5CA8P58TrBDp0d1PiX8lkMgwbNgxv3ryBRqNBnz59BH1nzEBx69YtSKVS9O/f36hsFjoHC52nmetUf39/TrsbM6YxeTgkJAQKhQKPHj3CmDFjzJZtUqkUEyZMQH5+Pssg3LNnT44s9vHx4RxHpmFozcecb0wxYGVkZKC8vJzTTlqt1ij/mwNrGrhp2d2nTx8iw5OTk0FRFDw9PcnRPhrGeI1epwqBnZ2dzXTftwEXFxeEhYUR3n/69Cm0Wi0J3yPUkcSQHceYkVwIBBuwDHV2eXk5UlNTeeNEpKenm0WYk5OTwXggzNsbTIG5bnTM76KiolBSUoJnz56hd+/e8PLyEuRh8jY8d6yJ/Px8vHz5kuWKSdfJHEMlrZypVCrs27cParWa3Iph6u1OtoI+Y5WQdPrg6+sLZ2dncg6YjuuluxP0to1WTHh5eZltrLXEMGDqTp+lZajVaty5cwcfffQRZ1F2//59vQYsPtqamppITBNvb2+z4mBZUufnz5/Dzc2NxAVj0qVrwDJUhkql4uwCAh3HvJydnVFaWoqUlBTExsbabBLXarV49eoV65mvry8aGxvR0NBgEV80NjYSD1K6n2zBaxqNBnfu3MGKFStYMX6ADmOjUAOWPtpoufTo0SPExMTwxiUyxYDFDIzt7u5uUd/StMnlcoPzelVVldF8rN0vb8PjsLPKpCgKvr6+vAtcS/OlERYWJvgIbmfg6dOnCAkJ4RyzpwP2C4VMJoOfnx9qamogkUjg6OgIiqJQVVVl1JhWV1eH4uJiEpOH1g+Z/W5vbw8vLy+WXKO9xAoLC+Hl5UV0JCH80tTUhLKyMuTm5mL58uWsozW6375+/Rrjxo0jcXT4UFZWBhcXF4O3oxqDoWOt5vD/mzdvkJeXx4mPq9VqWXHuRCIRiXlIx78BQOKP6TuenJGRAUdHRyxYsID1vKWlhRjI9NVDSH0eP36M6Ohoji5VUlJitgErMjKSZeSgaTK1jenLkY4fP04W/bq3DxoDs0z60qlXr14hNjbWIK/RMMXL0FSaFAqF3guShKBnz54IDQ3FqVOn0KdPH/j4+PDGjGOWydcHEokEI0eOREJCAoqLi4mxZe7cuZxFvb+/P+GVzpgzcnNzWfHWaKjVal4nBX311Gq1JFajg4OD4Bu4rVFHOgyNVqvF/v370dbWBh8fHwQFBcHHxwdubm5obm5GZWUlfHx8IBJ13Aqvq3vQdfDw8BBsrLSE/q508oGiKAQGBhJ+PHr0KGpqauDk5GTU5sGsg7Ozs02dfAQbsGiihDSytQw1Qo0HtgYfHVFRUejRowd27NiBL7/8UpCgt8WOVFeAqXXS9faQyWRYv349KIpCZmYm9uzZg//7v/+DWCzuMgOaCWvSRF87awjWEGy24DtLzjYLyVv3u7fJC8bqykfbgwcPyKS/ceNGszxDbLFYNxVubm688QOscazC0BFJJuRyOT755BOOEXD37t0ICAjgDfgpFPfu3ePEiulMXjO1DQ25wuvLS0hf6b7Pz8/H4cOHAQBLliwxy0XeUJnMMW6JLDHkPWhunqZAl3Yhu5OdpQvYio+N8Zqh59auuzm8LQQ+Pj749NNPSczSYcOGgaIo7Nu3Dy0tLXq9zoCOWGUajQbr169nPWf2R58+fTheLs3Nzdi6dSvmz5+PoKAgFt3G9ICHDx8iMzOTFbeO5jVdGj766CNyNEZf20yePJlldDCnDfXJd2vxJR9NuvJAoVCQ+euvv/5CdnY2li1bZlEZzPwthS11M6HzKxOLFi3CnTt3cODAAWzcuJFlpGCCL0+mPKfjRX377bdWPyFi6mkLa8HPzw+ffPIJtm7diokTJ2LIkCEmz2FM9O7dG/Pnzye/hRx16+w1pKnH/ICOza/ff/8dWq0WQ4YMwZw5c1j5mXqcz1RaRSIRxGIxibv4+vVr/P777/juu+8wZcoUvHz5Ert27cJ3330HmUyG169f49ixY6x82tvb8fvvv2P58uWcDUd9sLSPLNFlrG0AY+ZHh2mgL0L58ssvzTpSyMwbsLy/TT5CKNS9s6mpCbt27UJTU5PRGFhCQVEUdu7ciVGjRlnNzdtc0C6a33zzDY4fP47IyMgutQvZWaioqMDBgwdNioGl0Wjw22+/YerUqaybRbqSwcKWYAqpo0ePshbly5cv57i4W6MdysrKcPjwYaveGKRUKvHbb7+RCwKsia7W94cPH0ZBQYHReFpdHenp6bzHAU0FRVHYvXs3hg4dapEr8Llz56DRaDheYp2J0aNHo2fPnryx/fhgbQXy2LFjePPmjVnXnQtFYmIiJyaWLmwx5lpaWvD777/zLhiZ5e3fv593QW8IEyZMQHFxMbZv3w6gox8nTZpkOdEmoqmpCbt37wZFUYiJieENZEqDqeDu2rULAwcOxOjRo/Wm70q7skIhEonIfPPpp58C6PD+27FjBz777DPY29tbtU5Hjx5Ffn6+QQ/Xhw8fmhTmgIlPPvkEKSkp2L59OzH+3Lp1i3V81VZobm7Grl27QFEUoqOjMXbsWMHf8rWxn58fNm7ciISEBIwaNQqDBg0CRVH47bffEBsbi8mTJ0MsFuOLL77AjRs3kJOTQxbYZ8+ehUqlwoYNGwR7Ez9//hzJycnYuHGjTcJc0Lx26NAhEgOrpKQEf/31l9V4LTMzE5cuXbKZ4eDmzZtWPdotEomQk5ODK1eu4PPPPzfL8zs6OhphYWH44YcfsHLlSnh7e6O+vh5//PEHgI6LT/Td6gt0xIsqKirCv/71L95g5+/yRr5IJMJnn32GW7du4eXLl2Rxf+nSJWRlZUEikeDzzz+HXC43ynvZ2dlk/gI6wtNMnTrV4Df0fLNy5UrLKyMA+fn5OHnyJG8MrLexYaQPTPsAfUOqPvTo0QOff/45du7cCZVKhbCwMGzYsAG//vorgI6jyufPn4dWq8WJEycwaNAgg/O0LsypY2VlJQ4ePIj169cLlpUSiQRffvklrl+/jpcvX1pNjz506BAUCgXGjRtnlfyY0Gq12LlzJ8aMGSPIK9MQrBJZOSgoCPHx8axgwWKxGHFxcWRn2xT06dMHjo6OuHTpEufdoEGD4OvrC0dHR0yZMgW3bt0CRVGQSqWYNGkSiaUkk8kwZcoUSCQSDBkyBBUVFZz8RKKOuEOenp6854SNKY9isRiurq4YOnQo6urqcPv2bZOYnIkBAwagtLSUl8Zx48bBz8/PKkFF7ezsMHXqVGRkZODZs2ewt7cXrBT16dMHDg4OLBolEgni4uKQlJQkmAaxWIyYmBgUFBRwjgXJZDJMmjRJkACIi4tDdXU1p83EYjGmTp0qKPieqXj27BlKSkpAUR2386SlpaGwsBBisdjgDY5MlJSU4Pnz57w32D179gxBQUF6z70LQWRkJJydnVntIpVKERcXh8TERJPzGzp0KCorK3l5MzY2Fq6urpyr3fnQ2tpKztGXlJSgtbWVBB/lu3b5bUAmk2Hq1Kl4+fIluSIW6Ih/IJFIWNeIG0Pv3r2J8qbrat4ZC9Phw4ejqqqK1W9yuRzDhg3jHa+jRo1CbW0tSR8fH49evXpBJpPxyuL+/ftbfI49IiIC5eXlvLw1ceJEeHl5obm5GWq1GleuXOG0WWRkpEXHAoCOozz6FAZdL6EbN25ApVKhoaEBFEXh+vXrkEgk8Pb2ZsWA9PX1xfjx43Hjxg1QFAV/f38MHDgQU6ZMwatXr/DixQuSVqFQQCKRkFuZ7t69i/r6eqIw3rlzB46OjuRq6ZSUFDQ1NRFj9O3bt0kdRo0ahbFjx6Kuro7Vpg4ODhg0aBDu378vuF18fHyIMm3sCBofrwH/0wOcnJwQEhICjUbDG1CcvvXHFDg4OMDOzo7EP5NKpSyZzyerCwoKIJFIBB+LUigUHN2GxoABAxAUFAS5XI64uDikpKRAqVTy5kNRHbFgmJfQDBgwAGFhYXB2dgZFUUhMTER7ezuampoIr0mlUnh6emLYsGGYOHEirx5Dz5mWGEDNlUdjx45FfX09hyZ6vqEN/i4uLoiNjcXNmzdZu6/jxo2Dv7+/IN1GKpViypQpyM3NRXZ2NnkeFBQEiURCjuDGxcWhpqaGRZO9vT2GDh3KewzMEEQiEVxcXBAREUE8aD08PARtZPD1FY1hw4YJ2sGmeevOnTuCNp+ampr0ltm3b1+EhobC1dUVMTExrPE6ZMgQhISEkPHj6uqK/v37o6ysjKTx8PCAr68vOQ70+PFjVFZWQqPRgKI64na9ePECMpkMEyZMgEgkIvzM9GrV5TUHBwdMnToVaWlp0Gg0RH979OgRtFotJBIJpk6dCnt7e0RERMDJycmgbuPi4oIRI0aweI2GsfAUw4YN4+g6dNw+c3QnXYwZM4YzXmjZzDwCKQS0zllSUsI5fi6TyRAbG2u2jmFnZwcvLy/ExcUhNTUVYWFhCA4OxsiRI3H79m2jMYjb2tpQXl6ud/03cOBAeHp6YtKkSbz9pFAo4OvrC4lEgilTpvCuFeRyOeEzGubIMYrqiOFLx1AEOoyKcrkcrq6uiIuLY6WnZULfvn1RUlKCy5cvY8KECejduze0Wi1SU1MNGujUajVu3LiBsLAw1gY+0HGZy40bNzB+/HjExsbqXd/ExcWZfAuiEPCtQ2UyGUaMGIEbN24Izodef9NH2K0NFxcXTJ06Fffu3YNWq4WbmxtGjBiB4cOH865V6LmDPvYmlUrh7u6O2NhYaLVaeHh4sGL1enl5sRxS+GSGPl5LSUlBY2Mj0QWSk5Ph4OAAJycnjB49GqNHj+adM+m1tCmx1WhHGrVazTIwPnnyBBUVFdBqtdBqtXj06BFevnwJmUyG8ePH49GjR6ipqSExF+/fv4+MjAzY2dlh3LhxGDRoEGcOBfSvrXXl2rhx4xAZGQkXFxfe+SgqKsoqYYKMGrA8PT2Nxnfy9fWFi4sLDhw4QAauo6MjJk+ejOLiYri6ukIikSAgIIDFQHy/JRIJQkND4ezsjBMnTrDKEYlEWLFiBdlViYmJQUJCApRKJQIDAzF+/HgkJCSgvb0dwcHBiImJAdCxSLKzs+MooXK5HJMmTSLxDfR5vtCMqlsHGlFRUXj8+DFvnBgm6Lbx8/ODk5MTawD06tULjo6O5Kw+DYlEgpUrV0Iul6OpqQkBAQF6j1EIEdxyuRwjRozAyZMnUVVVRRZuPj4+RoNt0woOs198fHwwadIk5ObmwtHRETKZDAEBAbzKtIeHBxwdHYkB6+rVq5xYZj179uQEfROLxQgMDOQM7P79+yMzM5MT18LZ2RmTJ08mE40xASqTyRAYGEholsvlpA4URUEul5P31dXVhGY/Pz80NzejubkZEomECELdfqAoCmKxGAEBAbCzs0NFRQWePn2KzZs3c9rp999/h1qtJgYs3X6lx5oudMePk5MT67prX19fTJw4ES9fvoSjoyOpI523nZ2d3t+RkZGQy+UkECoNuVxOdmwdHR1JYGpdmunfGo2G1d+enp7kt5AJQh+P+/v7G53MPT09WbdQ6co1Ly8vODo6QiqVIjY2FmfOnEFZWRl5P2/ePLi7u5Nb0Hx8fHjP9TPlWnBwMGdRrnusg6no1NfXk2vO/f39eceQvb09q590fzs4OJB+6NevH3JycljGqr59+yI6Oho5OTmcRdjAgQPx7NkzYlxRq9UICgqCi4sLjh49yqFl2bJlcHZ2RlVVFQIDA8lOL10nff1C85ZYLEafPn1gZ2fHMWrQi3KJRIKqqir4+vpybu4DOuJG+Pn5oaWlBYGBgbwLS2dnZyLbKyoqSAB4Gg0NDaitrSXjh66D7vEbiqJQVFREFBN/f38SE01XYaWNDgkJCVCr1ZBKpZBIJIiNjcW5c+dY19nPmTMHXl5eaG1tJR4FtEE4ICCAxGagZXVpaSlqa2vJ+4qKCgAgsmfIkCG4d+8eXr58ScoYNWoUfHx8UFBQwApsy4eAgADY29vDw8PD4C47E/369cOLFy84gV4dHBwwefJkQr+LiwsOHTrE+X7x4sVwd3dHXV0dqx90oSsDaDkGgMNrVVVVLFnd1NSEpqYmSKVSUBSlNzA1LavlcjmvbkNj/vz5xAgxYsQIPHnyxGAblZSUsC4bWL58OdEDgA4vJXoBFRAQQHhErVZDLBZj+PDhuH79OmfODA0NJbqOEOgaZXXlEZ+M1dXXgA79aOjQoRxeAzrmG9obzt3dHT169GDpawBbt2lubjaq29Cy+ezZsygtLSXv58yZA09PT2LgGTBgAJ4/f84yCNC3RL18+RJSqRQuLi5GFWhmW/To0cPoMRJmeg8PD9TV1emN1TpgwADe5zTv0XKM1teePn1qsGygY1Hn4eGht0z6SKJIJEJ0dDRu376N3NxciMVijBs3jhPQOjw8HPb29uRGvilTpiAsLIy8r6ioIDLZ398fdXV1qKurg52dHSiKQnV1NVQqFUcv0OU1Ozs7jBgxAseOHUNdXR3c3NwwefJkHD16FA0NDfDw8CAyRIhuQxvomLxGY9KkSQZPhERGRkImk7EW6xEREYiJicGLFy8gl8tZawXd8aJPLwBAZPODBw+Qk5NDnsfFxSEgIIB4Ebq7u7NuJXR3d2fpAh4eHnBwcDBZj9YHiqLg5OTEqRPdL3/99Rfq6urQu3dvjBgxgnODIjMfui3c3Nzg6uqqlxd79uwJhUJB+knXIMbktZiYGFy8eJEz//fu3ZtsGnl7e8PNzU2vHDO2PiopKSGXdAUEBBDdz8vLCwD/mq1nz55wcHAgnjrh4eEQi8VITU3VWw7QcQLlwYMHWLp0KXr16sV6l5qaiqtXr2LcuHEYMGAAMjMzkZyczErj5ORE1jfOzs6C1jfMNRlzPUOvb+j3vXr1goODA86dO0e+DwoKwrhx4/DixQvY29sTGcXXnrp6tC74+oVuE1qmOzs7G42Z5ejoiNjYWBw6dAjNzc0kllV0dDRu3brFWYsHBgZyvLPp8UODyYNeXl6cDYa2tjbWOlQfr5WVlZENmYCAAKLL0afGhgwZgocPHxIZUFFRAScnJ4SGhhI5V19fj+bmZlCUsJvePTw80NzcjNLSUvj7+6OyspKMPX9/f9TX16O+vp7QXl5eTto7ICAANTU1qKmpIToU3xwKdGxs0LxHg0+uqdVqsjnHlNV0er75hq8tjUFkyFL873//+93182TAWKPwvbe1h8TbOBpgaZnm7m68a0cgLIEQXnv69CmuXLnCe+nB7t27ERwcjGnTpnUaTbbA2xg/5ozzrgCtVott27aRgO9isRibN2/WGyCURrdcM73Mn3/+meXtsXHjRjx69Ai5ubmcwMCG8n9Xec1cdMX6dpUyaR3q119/RUREhGBPXFPy72pt/67B3DbUpx+fPXsWtbW1nItXrAlm2b///jvCwsKMHi8yNX9b8trevXvh7e2N2bNnW0Tnu4b3dbzS/PjLL79g0KBBJsWdfFfrbAooisLr169x6NAhfPfdd3ovPVEqlfj++++xZMkSjgErLS0N165d410b6CvzXee1u3fvspxLRo8ebdHFEULAJ9fb29vx/fffY/HixRzPOL7vLV1L09ixYwdiY2NZnn4XLlxgGYpnz56NoUOHGswzJycHx48fJ3G9rI23yWv//ve/eTP+RxiwutGNrgT6CNK+ffs4gnTBggUIDAx8r65k7YZhUBSFlpYWFi84OTl1ecXjXURLSwtrR8vR0REqlQoajcYmLvndeP/R0NCA33//HYsXL4afn1+37H6PQMeHojcXaIwb13GDn7FNBkvR1NSEnTt3YsGCBcSD+11Ba2srxGLxO0VzN/Sjrq4Ou3fvxtKlS+Hj42PSUad/Ai5cuICGhgbMmjXLoP5GURSam5tx/vx5jldZREQEJkyYAEdHx3+M/tfe3s7yfpLJZDaXGXl5eRzPIJlMho8//hhOTk5GT51ZiuTkZNy7dw8AsGLFCnh6erLGk1KpZB0JtLOzM2qUUqvVaGtrey/XDvoMWLbtpXcctEXRWpbFt+EBZWt0e2Xph756ymQyuLm58cYe8/X1tYrwtiavdRW+fV95TSQSWRyv7W22zbvEa/qONOrLxxR63gVeswbe5XFoC16zt7fHuHHj4OPjY5bs7uY1/egKvDZy5EhOAOOwsDCTjFfm0iSXyzF27Fir6wWdwT+2Nu51wzbQxxu0nPPy8jLJeNWZsuptysXIyEioVCqjAbjpo39DhgzheGB5eXnZJHZvV4ZcLreaMVRo/3t5eWHcuHGsZ2KxGC4uLoJjSVrCaz169CBH6Dw8PCCXy1n52dnZmSzvJRKJTS7K6GyY0q6CPLDeFWWpKyg7lqKr0dPZ+KfXvzPR3dbdeJfQza/d6Cy8D7z2rtThXaHTUhg6ptiV6v8+6NH68K7QaSm6ea3z8K60ta3wrtS/m9feXejzwBJkarTWWU9LICQfc+jsap3e1ejpbLwrvPY+4J/Oa5bAXB75p/CWLfBP4VddHunmtc7H+8BrQurQFebM96GthUBfPbta/W2lR3fzWuehK/Da21yzdYV1ZzevvRv9ZOk3naln/dN5TRcm3btsTkdZq2H/qR30T0VX57XuxeH7g87kNXO+6+a19wfmKFrdvNYNW+Ft6mfdvPbPQjev/bPwNtds3evOdwPvQz9188jbg0kxsKzRUTdu3EBjYyOcnJzIbT26+Rpyk7t8+TKUSiW8vLwQHx9vMT22QmZmJsrLyzF+/HjB3whxA3yXXAUtcXe0RR3NuXVHH4QaubpqX1mT17piPR8/foyioiLye/jw4QgMDORNq4/2/Px8pKWlAQDGjBkDDw8Po+UaijFibV67f/8+RCIRYmJiOq0PMjMz8fLlS/J7wIABnDgOujBE28OHD1FSUgKJRILp06dDIpGYnZet8PTpUzQ0NGD06NEG09lSrhniJ2u2CUVRuHjxIgYPHgyFQiGYFkPP+ZCYmEiuLQeACRMmwMXFxTyizaChK8+1NTU1SE5OxrRp0/TGBrElbdaUXYZgLD+1Wo1Lly4hJiaGc1V8V5xz+KDRaHDx4kUMGzYM/v7+etMZq09SUhK8vb0xYMAAW5BpEyQnJ6O6uhp2dnaYOnWqYJnxNniNxt27dyGXyxEdHW0zephz6JQpUyy+bdjUtObWp7m5GdeuXcPEiRM5sXbehfFIURRaW1tx9epVq8031dXVSE5OBgAMHToUwcHBvOVao/9u3rwJLy8vk2SAqetQS/vxzp07sLe3N3pjnlDw0ZORkYHc3FyIRCJMnjz5HxNrz9KxXVRUhGfPnmHatGkQi8WdNmZpu01kZCTCwsKM0moKXSZ5YFkD9fX1eP36NTIzMwHwTyx8z5RKJfLz81FdXY2amhqW8msMtjiCUV9fj+LiYr3HPUpLS5GVlWVSWUI6zRSGs7Telu5avW13Ryb9+trX2rQwy7R1X1nSv9bkNXoRbS4ttsCbN2+QnZ2Nmpoa1NTUQKVSscqqqKhAZWWl3u/LyspQUlJCvler1QbLo/PWNSjw8QNFUSgqKjJJhvHh9evXyMvLY+VtK9D1aG1tJW2Snp6OiooKo9/pm6QKCgpQUVGBmpoa1NbWCvZO6kxeoygKhYWFyMnJMZqWWU9r8zVf3oZ4zVxQFIWnT5+irq6O85yPFn00GkN2djby8vIILzFvhrS1XAOE0fo2eA3oWCimpaUZlDmdxWvMZ7rzqTXR2tqKgoICFh9otVqkpaWhsbFREI2mwFIdh/l9YWEh55ZCGlqtlhjADcEYr7148QLFxcUGaTEFncHXDQ0NKCgoQHp6ut401uI1uh/4eMUU5Obm4s2bN0ZpNAW6vNbS0oLy8nKkpaVxLgfggynlW3sNwYRGo0FNTQ0KCws584OhPDvT+81QWSKRCO3t7UhLS0NbW5ugb4xBrVajpqYGT58+RW1trd5yhcJQ2vr6epSVlaGoqEjvulP3eUlJCbKzs61SPhNarRYFBQVobW1lPecbP6ZAyJqNHj+pqamc8dNVZaHQb+i1gblznqE0tbW1ePbsGUt/ZKKxsZGXt/hoZP4vBM+fP0dVVZUgWk0ZL51uwPrggw8EWWd1G6eqqgr79+/HvHnzsHr1asyYMYM3HV8+tjiC8fz5c/z1118cGuhvRCKR4NsMjJVlLsxVcpl16IzJR+iAMRXM+ltjgn1bvKaPBluX1Rl52nIhplAosHr1aqxevRqhoaEsfr527RqSkpI439DvL168iKqqKvK9j4+PwbL08Zq+djl69KhBA7dQXjNFxpgKPl6Ljo7G6tWryXXDxqCv/hqNBgcPHkSPHj2wevVqrFy5UvDVxZ3Ja5aU1dlyzRLvLiZ0ecpWO3UDBgwg48vV1ZU8N9fw/67LNeY3YrHYqhsI1pjjbMFrNAoLC5GQkGB0o8AaYPKzpbym1Wpx8OBBvHr1Su93QmW0IVoM8YM5uo0tdQe6zBkzZmDkyJEm06ZbjtAyjxw5wvIONgfGxp05MluX14YNG4a5c+ca/KYzjT5C4erqitWrV+PatWt49uwZ5721ec0c6CtLKK+ZCj8/P6xatcoqN4TqQpfmOXPmwNnZGceOHeOk1SfPRCKRIK8dU6HRaJCQkIDCwkKTvzUEIeN++PDhmDNnjtHvzSlTCLRaLbRarc14zdjawFwYWi9QFIUXL17g6NGjgvJh/i8EdLnWlmsmHSHsTFi6k0orKdbYPTGWB9/70aNHY9SoURaVbU10JWOHqWXYmoauwmuW0PA+oCu0X1dpXyG89sEHH7wVGt4HdCavdZb3p7nQNZxs3LgRMpmM931n0NAZ33UmhC7A6XQKhQKbNm0i12ybUoalvNZV5tquSIM530mlUnzzzTes8WQOVq1aZdZmBR/N1jJId3VeMweLFi0y+L6zaO6KbWMMlvKaobSW5vO+tKepGDNmjNFQO+9i2/Chs3jt0KFD8PHxwdSpU98pXuvbty/Cw8N55xFb0/zFF19AKpVavRxBBqzi4mLcv3+f9Uwul2PGjBmkMZRKJS5cuMCxsE2YMAHu7u7WoVYHWq0WFy5cwKBBgxASEsJ6V15ejvv372PmzJl4+PAhRCIRhg0bhvPnz0Oj0QAAZDIZZs6cSerQ3t6O8+fPc+owfvx4QfFvgA7mv3r1KpqamuDq6opJkyax3gEd3h1MF1ag4/x0WFgYqdPgwYNhZ2dHzlcDgL+/P2tHi69fdOvUDdujKwosfW6ibwO28uB4G2htbcWlS5c4MmLSpElwdXVFU1MTrly5gilTpqCgoIDlvj1gwAD06dNHb95PnjxBU1MTxowZw3qu1Wpx/vx5DBkyBEFBQdBoNDh//jzUajUCAwMxYsQIgzTTsnnMmDFoaGhAamoqederVy8MHjyY/M7JycHz589Z3zs7O2PKlCl686+rq8ONGzcAAFFRUejZs6dBegzhzp07kEgkiI6Oxrlz58iRIl25Zmy+aWlpwaVLlzBp0iSUlpYiIyODpOnbty/69u1Lfj9//pxzRNDDw4MTN4KiKJw/fx7t7e0AOow9M2fOhEwmQ3l5OVJSUgAAI0aMYMVb4+P9goICPHr0iPXMzs4OM2bMIOlN4bX8/Hy8ePGCpBk4cCB69+4NiqJw6dIl9OnThxOrrKqqCsnJyZgxYwbkcjkqKirIfBMTE4OgoCCS9u7du4LmUEvR2NiIq1evcp5PmzYNjo6OADr6wdgcev78eURFRXFikpSWluLBgweYNWsW7t+/D4lEgtjYWFaa9vZ2XLhwAfHx8fD29ual0xT5Stdp6tSpeP36NYvXBg0ahPDwcJJPamoqXr9+DZFIhJkzZ3JiYD148IAV2w8AAgICEBcXR+gpKirCgwcPWGmY+hodwmHGjBlISkpiHX2ZOHEi3Nzc9NblypUraGpqIr+nTZuGV69e4eXLlxCJRJg+fToxvFEUhQsXLkCpVLLyGDZsGEdfM4bCwkI8fPiQ/A4NDWXFKcrLyyPxCmk4ODhg2rRpEIlEePHiBUcGBAUFEV7r378/IiIiyPtnz54hNzeXlZ+npyfGjRsnmObS0lLcvXsXABAXF4eAgADWeyEyoK2tDRcvXgRFUQgPD8egQYNY6WkZoIupU6fCyckJ9fX1uHHjBqZNm4acnByWp9iQIUPQo0cPwfWhoVarWTKARnx8PCdWmSFcv34d9fX1rGeRkZHo168f+Z2RkcGSawDg7u6O8ePHCxp7jx49QltbG0aOHIlz584RDz+JRIKZM2cSr19mnYKDgzF8+HBWPvrmG921wbVr1zjHRXXnG3NQWVmJ27dvs57p1sESvHr1Ci9evMD06dORmJjIOiJIzzc0Jk+ejLKyMly9ehWTJk3C5cuX0dLSAjc3NxLXmIZuH9HrG3oOpaE739y7dw8lJSWsNEFBQYiJiSG/DY0fIcjOziahbACgX79+iIyMJL/T09M5Hn2myoCysjLcuXOH9UwqlWLmzJmQSCQoLCxEWlqa3jk0Ly8P2dnZmD59OkQiEXr16gW5XI5Tp04ZlNUUReHKlStobm7mrEPp93xzaHR0NEJDQwXXTx/oOZTW3wzJNSZ0ec0U0Hk/efKEc4zRx8eHxDB98OAB1Go1YmNjiR4NsPsFAFQqFc6fP0/qQM+h7e3t5OiiSCRCYmIi5/honz59WLHKsrKyOF5V+uwDpoC5FmBCd75JSUlBeXk50deErsdoPZqP17KyslBUVITJkyfr/b62thaJiYkALF8b8MGo5CsrK0NxcTFLaQE6Ovv169dQKBRob29HaWkpmpqaOJ1QWFgIjUYDLy8vk4mjqI44KRqNBtXV1QA6Aivb29vD3t4e/v7+yMjIQHBwMEchamxsRHp6OqZPn478/Hy0tLTAy8sLjY2NhCElEglev36NwMBAqNVqo3Vwd3dHQUEBAJC4OK9fvwbQseChJ7Lm5mYUFBRALpezGLS9vR3FxcVoaGjgCPDS0lLI5XL4+fkhIyMDDg4O8PDwYLV7TU0NXr9+jdDQUFRUVBjsl8DAQIuC21lrN8Tc7/WlsSVdpsCSOlizDH3oSgYjS9rJmm3Y3NyM8vJyAB2CWSqVkhhSAQEBRsdLfX09SktLec+nFxQUIDAwEBRFIT09Hb169UJNTQ1rfJaXl8PR0REKhQKhoaGcCaG4uBhVVVUcAxZFUXj+/Dl69uyJoKAgUBSFpqYmFBcXo729HSNGjCD1bGpqYsWlcnZ2hrOzM9LT0xEYGAiNRsOiqbKyEvn5+QgJCUFxcTHKyso4MkWj0SAvLw/BwcG8ngStra0kzklwcLDBSaqiogJNTU3EDbu8vBz29vYQiUQIDQ3FmzdvoFQq4enpSdIBbLmmVqtRUlLCK6vp+UIqlSI9PR1hYWFobGzk9IOzszOCgoJQWFhIaNJFXl4eUeaUSiXy8vLQ0NBAlAWRSERooucboEMR1ndhAEVRJL6abpl0GUFBQWhra7OI18rKyuDg4ACFQoHMzEx4eXmxDFgURaG5uRnPnj3D1KlTAXT0c2NjI/Lz8xEREcFaUOibQ82Zb/QparW1tbztQpcfGBgIe3t7wXNoaGgox4DV2NiI58+fY8aMGXjz5g2kUinHgKXRaJCeno4hQ4boNWCZIpOUSiXS09MRHh6Oqqoqvf1Ep62qqkJZWRmmTp1KDFgURZEYoLrto6sX8LUhU1+j49dFRkaitraWlZbmLX36Gm2Qpzcl8/PzWePnzZs3CAgIgJ2dHekn3TglJSUlkMlk8Pf3R35+PpEDFEWRPnFyciL6VGlpKWQyGYvOqqoqvHnzBqGhoSgtLSW6GxMqlQqvX79GUFAQKisrkZWVRfihvLwcWq2WfFNeXg4nJycoFAq9MkEkEhGZUFNTQ8aBVqtFZWUl8vLyiBwTi8VQq9VkPA0YMIC1oCgtLTUoAxQKBezt7aHVatHY2Iji4mI4OTmxDFh1dXVGx0tbWxuePXuG3r17o7KykpW2tLQUdnZ2emUVH5qbm1FSUsKSATRow6oxI5ZarUZBQQHq6uo4scPKy8vh4uKC4OBgvf1AURRev36NkJAQ1NXVoaGhARRFQaPRkH4AOoycBQUFqKysREBAABobG4nRTSwWE151cnJizakURbEMWI2NjXrnm8LCQmi1Wri5uZE6tbS0cOrk7OzMG+BbCCorK1FUVMRpB9oYHRgYKOg4PxO6elV1dTWeP3+OyMhI1NXV8coET09PAB1GxpKSEuTk5GDSpEnIzs5GfX09/Pz8OAYsJtra2ohM0F1wFxcXQyqVwtfXV6+cq66uJmNeyBxq6FgfHYOUTy8QKgOMHTctLy/n7TfmurOurg4ZGRnE6NbY2Ijq6moS8qKmpgaZmZmYPn06gA5DjJ2dHc6ePYtRo0axdEi6T2kepdehdnZ2rHWoUqnUO4fSslmfTCgvL0dzczPUajUoikJ5eTmkUinEYjHRlZqamvDmzRs0NjYSWphyTalUGtVtaF4zBbS9QHeOBf43/4WGhiI/Px91dXXw9fVlyQRmv2i1WkIjXQc6D09PT3h7e0Oj0SA/P58zhwIdem5BQQGCg4NRVFSE8vJyThqtVou8vDyEhISgvr6eGPNpewdTjvHxWktLC5HFupsJxcXFEIvF8PPzA9Cho5eVlaGhoYFl4KXrAHTMqWq1msxjbm5uRI/m47Xy8nK8ePGCY8BiyhZ6ji0oKEBISIggA5Ypaz6jBqwbN25AJpNh1apVrOetra3Yvn07VqxYgerqaiQmJuJf//oXx4r822+/oWfPngZ38A3h1KlTrInjxIkTEIlECAoK4tBkCEVFRTh58iQ2bdpEdiza2tqwZcsWrFixAvX19bhy5Qo2b97MqcOePXsQFBSEuLg4HDp0CEBHx1AURX6PHj0aY8eOJUd7rl+/ztk5qqmpQUJCAr799lvODRinT59GdnY2qdOdO3fQs2dPVh1zcnJw6NAh/N///R8SExMhkUhY7ymKglKpxLZt27B8+XKLLOmGGMhS44iQ7/WlsSVdQmDKccHOaCdrwBwjkTWNhZ3Fa0VFRSRunUajgUgkIuN3+fLlCAsLM1hednY2kpOT8e2333LS/fLLL+jbty/xZjpz5gzGjRvHGp9Xr17FuXPn8Nlnn2HhwoVGadfXxlKpFMuWLcORI0c49czPz8epU6eg0WggFosxePBgorxcuXIF0dHRLJru37+PEydO4JtvvsHZs2cRHh7OkavV1dX4+eef8fXXX/N6oopEIrJrZay/7ty5Qzy8NBoNkpOTkZKSAqlUik2bNgHoWBScOnUK3377LaRSKZFrW7ZswapVq1BTU4OrV6/yyuqdO3ciLCyMeKWdP38e8fHxrDolJibi9OnT+PLLL3Hy5EkMHz6cU+eSkhL88ccf+OabbwB0LCKOHDmCb7/9lhhqNBoNtmzZglmzZsHBwUFQG4hEIly5cgWurq6cMpubm/HDDz9g9erVKCwsNInXxo8fz8rvypUrOHfuHD799FO9dDBBURQCAgKwYsUK/Oc//+H9xtgcKtSbQ1/7PHv2DOnp6diwYQOHtp9++gkxMTEICQnRO4eePXsWL1++xPLlywXRwQdbxp45ffo0Jk6cyOqnixcv4vz58/jkk08AdHjvKRQK7N27l/WtVqvFkSNHMGvWLLKYofHixQscO3YMmzZtQmJiIuRyOUcvoPvp448/BtCxoDh8+DDWrFnDWqz89ttv6NWrl8Fd1aioKIwZMwYUReGHH35AbGwsVq1aBYqisG3bNowePRoKhQJHjhzBv/71L87i+uTJk8jJycHy5ctx7NgxtLe3g6IoaLVaEt8lIiKCxAlKTEzEgAEDWHVKTU3FsWPHsHnzZly8eJHEomGioaEB27dvx2effQagw5i/cuVKiEQiJCUlITExkfDatWvXcPbsWXz22Wf4+++/SZ2YKCwsxN69e7F582bcu3cPT58+BdAhB+7cuYO7d+9CIpFg06ZNkMvlCA4OxrJly3jH07Vr1+Dk5KRXBnz88cdQKBRwdHTEqlWrsHPnTk4eGRkZSEtLwxdffMEaUxRFYceOHRg+fDgxWp86dQpTp05lLWTPnTuHrKwsrF27lpO3PhQWFuLkyZP4v//7P47nz/79+5Gfn2/0aHtraysSEhLw6aefcm5mvHXrFv7++29s2LABp06dQlRUFKeNysrKsHv3bmzcuBGPHj0iXjgajQb379/Hw4cPIRaLiewuKyvDsWPH8O233xKjhkqlwn/+8x8sXLgQERERkMlkWL58ORISEjj05ubm6p1vdu/ejZCQEIwYMQKHDh3CJ598wjHgJSUl4fTp0xy5JhR37txBbW0tGbs01Go1tmzZgnnz5pns4cUng1tbW3Ho0CGsW7eO1S///e9/ERkZiQkTJvDmJZFIIJFIjHrhVlRU4ODBg9i8eTPxpqVx8uRJ5OXlYdGiRTh8+DDmz5+PmTNnstI8f/4cR44cwXfffad3Dm1qasLWrVuxbt06vbfoAh28P3r0aNb3N27cwJkzZ/D555/jxIkTvDKguLgYe/bswaZNm4waDW/dugWVSsXJg9Zlli5dyvmGyWum6r50evr/+fPn49q1axwvsurqaiQkJGDTpk2c2yTpOVTf2vrWrVtkXavRaJCUlASRSAS5XI5vv/0WQMcmRkVFBb755huiEzU3N2Pr1q1Yu3YtiouLcfv2bYO6jT5eM4aTJ09i7NixLHsDvTFy4MABfPfddwA69Lvjx4/j22+/JZtE7e3t+M9//oMlS5ZAqVTi3Llz+O6770gdWlpa8P333+Pjjz9GSEgImpqakJCQwMtrKSkpOHnyJDZu3IgzZ86gX79+nDatrKzEzp078dVXXyE1NRX37t0j7frw4UM8fvwYIpGIdw4FOjYg6PGga6w9evQoXr9+TY5ET5o0CX5+fjh//jwrHa0H0PMvRVE4fPgwgA6vSN3NPRpC12xeXl5YuXIltm7dqje9KXnrwmzfU3t7e2zatAkymQwKhQL9+/e3yYL7iy++AEVRKC0txf79+7FhwwY4OjoaFJZ8Smh4eDgWLFhg9Jp2+ntmXVauXAmxWAyJREIGwL1795CamorPP/8cAATlawpmzpyJgQMHmvSNSCSCnZ0d6RdbwJjhgvneFh5RXe04mi49ptJnSnvaCubk35nGQnPf66J3795k/P7111+QyWSYN28eAFh9vKxfv16v94YtERkZia+//ho7duzAkiVL0KNHD+IBsXz5cpOP7giBr68vaVdjxxlmzpyJ6dOnQ6PR4IcffsCsWbPI0R362z59+uCDDz4QbBQzhDVr1pBdKEPQ5SV/f3989913RLlRKBRYuXIl50gXjR49erDawBy54OjoiM2bN0Mmk5kUHNUcXjPHUNO7d2/Mnz9f71xnS9n/2WefQSKREA9KPkyfPt1iA5Qt5dqnn34KLy8vq7dN79698a9//QsSiUSQYRzo4LWvvvpKLz9bioCAAGzevBlyuVxvfel4a0DHEaZTp06R+Gv0Nd9AR1wi3eOvtgTNa4Ywffp0TJkyBVqtFjt27MCUKVPQv39/AJbNJUwZYAyxsbEYPny4IF76/PPPBYfBMAVvQx+j5xv6hMP48eNBURR+/PFHjBs3jhj2ad4ODQ3F0qVLbcLrdGwyem1gK73b2uDrNycnJ3z11Vcm1+HTTz+1mA9mz55t9rfmYO3atSYdd+VDV1uLWAPG5tB58+ZBq9VCpVJhx44d+OCDD4hspmVmv379MHv2bJsF7jaEL7/8UtBavGfPnli8eDFkMtlb60cvLy8ix8aNG0eOOP70008YOXIkOSJvaA7Vh/nz5xtNQ1EUHBwcsHnzZgAdm0LJycnYsGED2ZTWd7uuNWCNdjfbgEUbS2io1WocP36ck073ulVTy6AnHVqoyuVyUq6uCzPzO12IxWLBN0Xofs+c+Og86MWWXC63CfNLpVKDE8moUaNQXFxMPEpGjx4Nf39/Tr/QsNYgNZYH870t2uVtTxi65Rv7bWp+pr5/n2HttmHKALFYbJJMoNHW1oa//vqLU7Zu3AuZTMYx5gwaNMigC210dDSKi4tx/PhxzJkzh8QlunXrFmbNmsU60qUPEokETk5OWLBgARQKBWQyGTFg8dFkDZjSjrRMU6vVEIlEkEqlnG/58pPJZFiwYAG8vb1RU1OD9vZ2HD9+nNMPuvON0Drr5qNLgz65aohmQ/nrS8PMw1JeoxXL2bNno7CwENevXydHPB4/fozy8nIsXLhQ8BxmSh3NkVv9+vWDq6srmdMGDBiAvn37svQADw8PLFy4ENeuXeMcQxk2bBh69OjBcaencefOHdTX1xMjXHx8PIqLi3Hq1CnMmTMHEokE+fn5ePToEebPn291A7RcLuco10OGDGHFXjIHzH6hjZ/0bi4NjUbDWUjI5XKbxcoUi8WQy+U4e/YsJwZWcXExPD09WfzO1KeYO+J0nYwtqvPy8gjf0NA9uigENK+lpaVxPBeYR8OkUimkUinhNT45Zg6MyRkmpFIpmpqacPr0ac473WNsfLxnLtRqNU6cOMHhnYqKCpPi11y9epVzUUFlZaXR75j8TvcDfSuY0PlEIpFg/vz5Jh2fBLh6tO7a4PLlyxz5LKROxsqprKzk8LdWqzX71k4++UzzvimymymbjcHb2xsLFizApUuXoNFo4Ovri7FjxwJg6wVAhwcLfSSfhm68tPz8fE6bCG0PIXrBs2fPUFxczHrW2tpK/ra1bm4rww89h169ehVqtRqenp5ELzDWJnQ/0WNfyHjjayehuo0poHnx0aNHJLQPDV1DDJ9MkEqlWLBgATnaLhTXr1/nhE+oqqoif0+dOpV1+mP69OlwdnbmlWP0uLd0PhFihKbb3s7ODhRFkSDrdnZ2NuNtplyzRhlWWc3U19ejuLiYV2HoTOurtSDE8+Ntg/akoAcqfaZbIpGgZ8+eFhtWDMWgMpRfZ8SGeldgaVt0pUDstsS7xGsURUGlUnHyDA0NNRrnz8/Pz6A3UGBgIFQqFS5evEjc55ubm5GVlYXZs2cbnNCY9ZRIJKxAuO8DdOukrx+Cg4Ph4+Njcv7WGGtdideYR0D69OmD3NxcFBcXE0W1pKQE1dXVnGMaQumyhlzTBR3fgw52WlFRAYlEApFIhLCwMMhkMohEIshkMqjVao6+obuhVV5ezgqYnp2dDXt7e3IEjz4GkJSURDwA6uvrkZubi3nz5lndq5oGs210j1CZkwcTxcXFKCkp4bSNPqOerdDW1oaCggK0tbVxFpX6Nh4tAe0VoIvevXub5HlDHzeprq7m5GeuscBWqKmpeWt6t0ql4hiwFAqFSfzMN4bd3d05x5psAbFYbFZgdX0yTaVS4c2bN2hpaeEde5ZubNJzgS569erVKe1lDTg6OqJv377IysqCSqVCQ0MDkc8BAQGsI+EajYZTX0dHR/Tq1Yu0jaExb8oNrvrAR4NUKkV4eLjN5gYmzF2zGZufHRwc0K9fP7x48QIqlQpNTU2kH/z8/P4fe98ZHkXVvn/P7qZsyqYnpHcIEHoVpRelSRMBsaIgNgSU97Off4Ki4ougNEGKCCJFQaXX0FuAkFASUgikt91ky/w/5D/zTt2d2Z1tYe/r4iKzc85znnPOPc8555lznrH6EQ+l4MjcxprMBw8eoLa21i7bzbQJoaGhSE9PR1FREa2j2WxGVlYWz1klZMfCwsLoY39ZWVnQ6/X0RwkePHiAgIAABAQEKBIwXwnYwzVr98TkSSlH0RhYUnDv3j0cPnwYn332mWAMLFfD2ccIPMWhkJKSgtdeew0AsG3bNty/fx8BAQH0NmBH3q7aS0CpOw06Chx9WK2lcWc7MRf0znY4ehPXtFotXnvtNVG51McmnAGpXCNJEiaTSVJcCqX0Yn7dSW6Z9tjrgIAAzJkzR7Qs7ttaW3CUJxaLhRUMlKuXPc+QO7lmDc60azqdjh7Tjh49Sr+1/OCDDxAeHo6amhps27ZNMAYWF+fOneN9jS8zMxNGo9EpOxGlwpk7oY8dOwZ/f3+6DSno9Xp8+eWXDpcrFdXV1di+fbtgrJjdu3cLBh53BFlZWZg0aZIisvbs2YPnnnuO14ZUDCxPQX5+Pq5cuYJPPvmEZ/+/+eYbp5Wr0Wgwe/ZswaPScjB+/HhRh5cnvCSWAyp+1MKFC3kvqY4dO8b7sq9cxMbG8vgoBHe8HKYCelM7R6xBpVLRx5wfPnyIX375BQDw8ssvsxyKw4YNY30RUAjp6ek24605gj59+rC++m4vXNUncna2EARBHzMrLS3Fpk2bALQ/kz179nT6+GhrbmMvdu/ejZEjR/JiOD548ABbtmyRLCc1NRVxcXH48ssvQRDtMY8DAwOxZMkS3tzuxRdftBpvDWj/KnSPHj1AkiRWr16N2tpaREdHY968efTOJ0fhjA0P1DNNzW8px63ZbJZ1Ak5umVLhvlmcE9GRHCRSMXPmTFgsFrS2tuLrr7/GnDlzPMa725HhzVyT6hDx5jp2JEjth9bWVqxcudJlNqCyshLr168H0D6YMz9vLwVS6+XJOzcfPHiAHTt2AABmzJjBOxbmqXp7MoYNG4bnn38eQHtw/oEDB8r6ktekSZPouEQU7t+/j6+//hpLlizxmng1PnRseLJdswVv1bujwh070tesWYP6+nrExsbivffek5w3JSWFjhu5f/9+3Lp1CzNmzFBMN0+Bpz8jCQkJdD8cPHgQt27dwuuvv+6wXG+2a0zMmjWL/iqkEs6Z+fPngyRJVFdX48svv8RHH30kedeboxsm5CI0NBTLli3Dxo0bkZ2djZEjRwJo/3jF06dPnRJXUQ4kvyonSZL1VqStrQ07duwQPd+t1+uxfft2WTGwlHrrcvr0aRw/flwRWQBw6NAhXL58WVJaZ7w5EpJ54sQJnDp1ir7WaDTw9/enY96QJIn79+9j+/bt2L59O+tMri3Z3N9s1UlueilQuh09UZ4z2lmqXraO7cmBnDJt/eatXPvzzz9x/fp1h+XExMTg1Vdfxf79+7F9+3bk5+fj1VdflfxGrLi4GLt27UJraysOHz7Mi4VDQW47W0N4eDgdV0jorYxSzx61G9AaDh06hCtXrgjek6NHTU0Ntm/fDoPBICk/tSWesr3W0onBYDBg+/btNndW/fnnn7z4ILbQr18/9OvXjx4PoqKirH7y3B6IjTdSuXbz5k0cOHCAvlar1fSYZjKZbB49y8vLw+HDh+lrKj/zn0qlYm33T05OxpQpU7Br1y5s374dFRUVmD59OiuIuC04YoeVABVfQ+yY4IMHD7B7926HdXI0v8Viwe7du+n4Is6eK7W0tNicg0rV4eLFi/j3338dVU+wPKZdMxgM2LFjh+h8zRbq6+uxY8cOWcF3qX7Zvn07jhw5Yle5QPtX3MTGG1ug6p+fn4/9+/dbTVtbW4vt27ez4hFJke+MOaC1tQFJkjhw4AC9+0rp8s1mM3777TfRD37Y4po9oOqkVqsxYcIEAMBLL72EzMxMm8e0nj59ih07dtAx8ag4ef7+/pLieVFzG2vjgD1jqJz2qK6uxo4dO1jzAiE4y/4XFBTgn3/+waxZsxAeHi5anrXyqbkN9fww+4G5m14OxLgmF8x5tFJz/XPnzuHo0aN25QWAkydPYvfu3di1a5dVnwcTt2/fxr59+0CSJI9rfn5+dExCOXEa5TgEuf4BR+Dv74+xY8dCpVLR87vnnnuO3jEpt1+UfDZsroqSkpLQ0tJCfzqTAnOyFB4ejvT0dBQUFNANzD3fajQace/ePQDtAc6MRiMtMzo6GtHR0XYRPjs7G83Nzbhz5w79W2Njo+yYOmFhYcjIyGDVgQI3CCkAREZGIiUlha5DVFQUwsLCcP/+fQDthq61tZXWKzY2FoGBgcjJycHDhw95b3+DgoJEj0SIBVxsampi1Rto75fOnTsjKCgIZWVltH7Um2wx2cyHQ278LGvp7fXCK+VNpsoXqqcjEIpRIKXezmpnqTJspXNmX3kS1yi7Rj0/ycnJNj+LHBERgbS0NNy5c4dXDhVwmAl7uBYUFITOnTvjjz/+gMFgQFpaGutoTFNTE0pLSwH8z85RdUhKSgJBtH89hNoBJKef09PToVareTaltbWV/tS4EAIDA9GlSxdRJ5vSW5qt2WprE0shPTIzM2E2m3l1bmlpsck1JoKDg+k2txaThCAIpKSkwGg08sqkAtsDynMtLi4OJpMJ+/btA9AeF4v5Rcry8nI0NDTAYrHAYrGgrKyMjj+VnZ0tKpdbN7PZjKKiItZ4I+eZZs4LmHVLTU1FZGSk1TGUigFJoaKighf/qKGhAV26dKHLDA0NRWZmJnbv3g2z2YwePXpIrq+1+snlPEmSKCoqgtlspp0XhYWFCAgIQFBQEJKSkpCdnY3GxkYeb+rr6+nykpKSoNfrWWlqamocega5Y6gUUDbhwYMHtF0gSZK1+GTKCwkJQXZ2NoqKiqBSqRASEiL5K2HULlNuu7S1tdl1DJ06ZsqUV1dXJ9rPnTt3pueg1PNSUVGBxsZG+nhVaWkpLBYLVCoVsrKykJyczJofUqAWjwRBoLa2lv7qpsFgQE1NDZ0+PT0dUVFRSE5OZj0vlP2T298EQaCiokJwrsucN4eGhiIrKwt3797lHaXR6/UICgqC2WymA+A/fvyYtq8EQSAiIgI6nQ45OTmoqKjgORerqqpo3TMyMkCSJK+N9Hq9oG2mYs1Q6ZnPsbX2aGxspIN1Nzc3w2Qy0TJSUlIQHh5udW1AHbPJyclBeXk5amtr6fvcBeq9e/dgNBrpehcVFSEoKAgBAQFIT08X1K9Tp06C4zL12Xu5czlHdjwTBIH79++jV69etL6ZmZm4f/8+q97WUFhYyJsrhIaGIiYmhn6e6uvrefVlyhcbQ4XiKgmBIAhUVVWx8hMEQdcpKyuLxQMKzI8jVFRUoL6+nj5eVV5eTgfBz8rKQkJCgmA9qDVacHAwNBoNMjMzcffuXRAEAb1ez+KawWBAVlYWqw2Ki4t5X5lj1rm1tZWOjSy0DqXSFxUV8cZQrVZr82g+lb9z586oq6vDnTt3aLsmBVLnNkLrBbHnJzAwEKmpqcjKyoLBYGC1uVhgeK4/gHnd0NCAR48eoXPnzry5XklJCcxmM8LDw5GTk4PKyko0Njay0nCdXNXV1TweGAwG5OTk8J6FrKwstLW10emzsrIEjxkGBwfT8U25Mdmam5sRGhoKi8WCwsJCkCSJ8vJyWCwWFBQUQKVSISwsDPHx8XSe8PBwVr9ERETQx6HT0tJgMBhQUVEBoH38qa2txePHj0XXYJWVlaitraWdoo8fP2bZZiXiyNl0YA0bNgy3b9/G7t27Wb8HBgZi8eLFUKlUiImJQadOnfDNN9/QnR0SEoJPPvkE69evh1qtRktLC08GdT1s2DC88MIL9O9ihpM7GVWpVJg+fTr27duHY8eO0b9PnToV2dnZ2LVrF72g407AqDKot7Kpqal0Hbge/nfeeYd3Vr9r166Ij4/Hf//7XwDACy+8gNzcXNE6jh49GoMGDcKrr76K7777jhcDYuLEiejVqxfMZjPrM9JMnQmCoA3O0KFDcefOHV55AQEB9Ce1Kyoq6PRynSlCsGdB7oxtjUxY00mMR1Lz26uDtWtntbOt/LbaSapuUtLbKt+euklpE6EyqQFRo9GwJtzDhg3DrVu36OeHOm7HjOXENbCdO3dGfHw8vvvuO17ZCxYsoL+QRwWbJgh2/DA5/ern5wez2cwb3CorK0VtzKxZs5CRkYHZs2ez7re0tNA6caFSqWgb8dJLL+Hs2bM8+ZGRkVi4cCGA9skX9fKC4gEVTJVqYylco+rIXQRRX2PhpmfKy8jI4I03FObNm4e4uDg0NjaK1lmtVtP3Jk+ejCNHjvDqHB8fj3feeYeVnglKLlWH+Ph4ut2F3hwy9Rg5ciRu3LjBKzMoKAiLFi2CSqVCVFSUQ1zjgrrH/YoQhUuXLtFvCQmCwMWLF3Hx4kVoNBosWbLEamwzqg2ouBHLly+3qgMFi8XC+tJcbm4u4uPjsWbNGl7ejz/+GKGhoSAIAq+++iq+/fZb3i4T7hh66dIlXLp0iZUmKysLr776qmgdrE2q5NpVCsx2tybzzz//pOuk0Whw4MABAO0Omrlz52LGjBnYs2cP721ydnY2Zs6cCUB4vtajRw9MmzYN33zzDQiCYD3zXGg0Gl4bMOvn5+cHtVpNc5y6Zt6n+Dtz5kx8++239KJPpVLh008/xT///MOb/yQkJGDatGlYuXIljEYjunTpgsmTJ0uyW2PGjMHly5d5z1NoaCj9SXBundVqNcvOUNcEQeDll1/GP//8w5L34osvIicnB5s3b+bpMX36dPzxxx84evQo1Go1lixZgry8PHqyrlKp6J1J/v7+WLx4MYYPH478/HxRG0AQBC5fvoxDhw7R9x48eEAvTOfPn49u3bohPj4eq1evptNERUVhwYIF+O9//wuVSkVzT8g+UHZNpVJh2rRp2LNnDyt2oNDLv8TEREydOhUrV67k7dR47bXXkJaWBr1ejz179rDm0b///juA9i+Fjhs3DrNmzcJPP/2EJ0+esGQ8//zz9IclJk2ahGPHjvHaKC4uDu+++y5Px2nTpuHAgQM4fvw4VCoVFi9ezBtPhFBeXi46ps6dOxfp6emIi4sTHW+otcGsWbPw448/shav77//Pm7duoX8/HwA7TuEqcW3n58fDh48CKDdSSXmwBo0aBCKi4vpeFEUKK7J+UiBEEiSFLQJVL8zx3XmOE+BctJxx23ucxsTE4OZM2fim2++4e2emzp1Kv3F2VdeeQW//fYbazct0P6VWuqIodgYGhwcTD8/QvUE2tt9woQJvPXT8OHD6SNSkydPxuHDh3nyExIS8PbbbwMALl++jGvXrtH3Ll++jMuXL0OtVmPx4sV4/vnnUVRUhF9//ZUlg7IBVHvFxcVh5cqVsFgs6NmzJyZMmEBzrW/fvpg6dSrdztQOHkA8Pm1TU5Mon8eMGYOBAwfaHENtQaPRYObMmdixYwfu3bvHqpPQ88a0Q1lZWfTchvs8UXMbbl4KBw8epG0U8/mh5mtTp07Fn3/+yTqBNXnyZHTu3Bk7duygnc3UV0spcOfpJSUl2L9/P5YtW8YbD9evX4+KigpMmjQJs2bNwoYNG2jHDoXBgwfTH4aZMGECvYuLiejoaCxYsIBX1ylTpuDQoUM4ceIECILAp59+KtimcXFxmD59OlauXMl7qTlz5kxkZ2ejra0Ne/bsYW042rNnD4D2L1UzX45nZWWhU6dOdL8MGDCAjiVGEARycnKsfviCy8Xr16/jwoULrOvr16/TdQoKChKVJRWEte1cX3zxBQmAfivLBeUYopTnbmFXq9X0FzhUKpXoFnfqk/bWQMlnlkmB+4loSpbFYmEda2ESkUpvNpvp8oXqwK2nUH4AkusoVgb3vlCbUP1A1YP7RpOrL7PfhOrAhVK7k+RA6TLlOrSUlC+WxhkOKbk6OSLfHWU6Kn/Xrl3Iz8+nn6FXXnmFFxSU+XyoVCrcvHkTe/fuBdA+cU1JSeENXrZshDU7JRXMMigHvJDOXFALFiFbJaYT06ZwbQYT1AB69OhRnD59GkC77Rs3bhzS09Oxbt06fPTRR9DpdFZtOdWvYnaOGQidm0esjZgQ6geAPQmixguqTtzxg1tnbnquDtw6SOGuvWOqWB2lOFaY8oTaXWwuIDaGMmVS8lpaWrB8+XK88847VuNVrV69Gk+fPqV1WLhwIaKjoyWNwVLHUCFwnyeuPKH7cmCt3YW4KKSDNZ2F+smWjWDOTSjnE/OZZ4LaBSjURgRBwGQysbjDvOZyQWxOaItLXJ1tzYWs2S3ufeoZ5l7bsgmUbZUyB6XqaO15kqOzmAxm2UxoNBq6X6ToTNWZcmC99dZbAMTnMdaePyq9FC4LHVWi+p0px5pt5kKoH6i/hUCSpOg8mspnrc7cduXWifm8Uf0iBmuONiljgSPzJe7zxJRH2YSGhgZ8//33tJOBesa///579OnTB4MGDbLpLLRluynYY+coWJt7MW0xt9+ljom25g1MHeSO89x1JLPO69atQ1JSEkaPHi2pjkKQug61BiY3pD5v3LmK1OeJy2spz4+9/gBmOTdv3rTqwIqNjaWdP2J2TApXmbuTheaolJ5y+9oVcyHq4xQff/yx4H0pz4ctUO3yxRdfCCaW5MCyJljq754AT9aNCW/R093w5HbyZN2eBTx+/Ji1rTc+Pt7m56YbGhroIxtJSUmsz+U6qz+9jSdVVVWsrfzR0dEICgpCSUkJ0tPT3fp1Nx+sw9lcu3fvHi5evIg+ffogOTmZ97lpJkpKSljHlVJTUx3eSaAUvOWZ9GQ9PVk3T4IntJPFYsGuXbuQlJSE5ORkJCUl8dJwdyi4W2cmvHEt4g442h5tbW0oLi5m2WqSJFFcXIywsDDFAjr7uCaMkpISaLVaxMTEOLUcJeAtz6QY16hjxZcvX+Y5Ybp164bExETJR9yVhjva8MCBA7zj3omJiUhPT3fJh6LEHFhWVxvWDIlYA7qTnLY6VuoD5UyCSJEtVx9PMgpSdVFil5Kn1FkIUnVzN9fk5vEWrnXq1Ine2i+VazqdDjqdTlCes+psj1x7nx0l+o6KV8iF3LhBzoAnPEtK2DVnwdlcU6vV0Gq1yMrKslkWM/6WsyHXprnqWXJUtq007uSet9k1peCNXAPajzXFx8cjKSlJsDxbOru6D5jlSV2LWFvPUO3gKTySAldzzd/fnzfOEwSBtLQ0aQpLLK+jcc2WTKlw5ZjJhdDz4ah/wJ39KKQP828qNmZ+fj5v51RycrLgHNhZOnLhDv8AFXieiZiYGKc5ryTPgezdgeWDDz744IMPPvjggw8++OCDDz744IMPSkJsB5b1w652gnKKWXOO2cpvb15HynWmTHvyM/MItYkcmUJpreX3xDa0JtNRrjmSX2l4GteErpXUSYo8uWU6Q6ZS+Xxccx/XXJFfSZlKccYTnh9HZcodw7j3fVyznU+JsdCT6qSkTEfmW47M3ZTK4wltyM3rbc+PvZxRkmue1o9UfmdzzRH4uOYaHRwtT2p+T/YPKCHfnrmSnLmQPeUJpXc315ziwLK13VJKfke2vDljW6LQdmM5DW6PTtztjda2P9pTvtzjloBjD6fQ1kdHoRTXHMmvNDyNa0LXjsiXeo9ZZ0ePl5Jk+5dFNm3ahE2bNqG0tBQEQaCtrQ1btmzhfUVEjmy5+Xxccx/XuBCqs7Pb2Z58rs4vdnTDVhqpMsU4JtdOyNlu7+Oa7XxKjIVK1MlRrlmTZa9MR+Zbjszd7MlD1dlRfiupk1BeR3VyNdfs5YySXHPmPNoekCSpiC11Jij9lFy/2AMf15TnmivLlyKTeaxSCXlS09jqq47INckOLGd51J3pqbfnja0UKDlRswUl3hxILUcK7KmzUhNIR+FqrilRvo9rysguLi5GaWkpHefq0aNHuHbtGvLz8xESEgK1Wo2amhpcu3aN/vfo0SO7y3f3G0hnca2iogIFBQV260XBWc+LXFirM0mSyM/PR3V1tWBern4WiwU3btxgfYremXAm14qLi/HgwQPWb3JfalRVVSE/P1/wvpCsyspK3Llzh/WbXq/HtWvXeJ+JtgaSJHHz5k3WxwZspXc312xBCtfcPb4pCSVfoHFl3blzB48fP1ZEtly4mmtKwFnzaAB48uQJbt++7ZLdO2JyGxoacOPGDdEvESpRhr3ppMDas+LNXKuvr1esXyg4Yx5NjTfXrl3DvXv3rLa7M9qqtbUV165dg8FgsJpOCYjpbzabcf36ddaHk+Sgra1NsA5Kvsjgwpl2DfifrmLjjRKg5gHXrl2TPF+zBwUFBbh27RrPVnNh7V5paSkKCwsl55Giv2QHlhRh9mwnU3KRyoU9b2wdhdLbVKV6mx0tV8obd6Vku2qLJTe9o2/LrcEZOxZswV1ccxTOLEOMa1euXMHjx48xbdo0TJs2DWVlZTh48CCOHTuGyZMnIzY2FuXl5Th48CAOHjyIvXv34urVqywZUst3NtesyWDq4QzcunULhw8fdliOPW+YnDEZNxgMgp9Btlgs0Ov1OHLkiKgjk6ufxWLBv//+i0ePHslyuAD2736zl2u2cP78eZw9e1aSDKHyW1tbcf/+fRw9elSybkVFRThx4gT0ej29aGlqasLBgwdRV1dn9RPaTFgsFuzfv1+yA1rI7iltWx2FENf27dvH2jkqlQeOcE0OlBrnld6x9u+///IcpUrIllJfR7lG2SXmot7ZTgpH5tG2dCssLMShQ4d4sp3BNTGdy8vLsW/fPlgsFpfNo72Ba/aW6wiY+lZUVND9IgSTyQS9Xu/w/EmJNjl8+DD27duH06dPu3zN1tLSgj179qCpqcltXDOZTNi7dy+ePn1qM5/JZILBYGDd1+v12LNnD8sB1lH8A4cPH8bt27cB8HUjSVJ0DioEofz//vsv9u7dKzpfE8onFSRJQq/X4+TJkzh48CBOnDhhVSZlu4XqdPXqVVZ+Zh6mTDm6KnqE0JlbspXI5wq4a0u0p20Pd7ZcZ3NNiZ1PzuapJ2y/93RQdZwyZQqmTp1K/z59+nQsW7YMn376KVSqdjPYvXt3LFu2DMuWLUNiYiJPhtwy5ab3ZK55ApzhAF67di3Onz/Pu/f48WOsWLECb7/9Nnr16iVJnlqtxqeffor8/Hz8/vvvsnRxxpZsJdLbK2Pr1q14+vQpPvzwQ8lyhwwZgmnTpuHLL7+kdxZFR0fj888/x9atW3Hx4kW7dZaLjmxbvY1rntyWTLhi7tbY2Ijly5ejqqrK4XJdAVfNZ5VqA2+ZR7trneBJXLt16xZWrVpFL3jd2SaffPIJunXr5tJyPV2mWL4rV65g9erVLi/XEyCk25o1a3DhwgW78qtUKnz66afo0qWL7HKloKGhAcuXL8ekSZOwbNkyzJ8/X9L84YcffsClS5dklyd3vNfILsEHj4O9nmpPK8MHz4e3co1yUoldA2zj6eP6swFrb90tFousAZVK6+7jo0pgxIgRDtWDaleh54wJ5rMu1H4EQUClUsl6M6dSqTB37lxERkbyyvBGVFRU4O+//8acOXPoT1mr1Wq88cYbiIqKcrN23gNvHbu4UPJIFSVv+/btGDBgALKzsxWV/axCjAdK8sPb7ZotpKSk4I033oBarRa876ojkrZgj5Pd22yR0rKUtmGejGnTpiEoKEj0viM7PynuWesbR/uOmgdbm8txy1BiN6sUSHZgUQpKaQypDcZM56gX3dmQOiA5wzDZkuns8hytk1B+pXhkL9fcwTN7dLX2+7PENVt6PXz4EDU1NazfdDodsrKyHOKR0WjEjRs3AAAxMTFITk4WlfH48WNUV1ejW7duuHXrFlpbW+l73bt3R0BAAF3WzZs3YTQaWflTUlIQHR0NkiRx/fp1mM1mAO1t3rNnT5SUlKC2thYqlQo9evSgJ3ZU/AHugJGdnY3Q0FCYTCbcuHED2dnZMBgMKCkpodNw61RZWYmysjKWnICAAHTr1o3XT/n5+ayjcj169ICfnx+am5vpOFlUncRw//591NXV0deZmZmwWCz0ef6srCzodDr6/t27d9HU1MSSER0djZSUFADA7du3ERoaitjYWNy8edNqHcTw6NEjlJWVoW/fvvDz82PVmdkvFNLS0ljOkoyMDNTX1+Pq1avo2bOn6MBP9Qu337p06YLg4GD6+vbt29Dr9aw0nTp1QkJCAkiSxK1btxAVFYWwsDB6qzoAaLVadO3ala5Tc3MzcnJyWHKoOiUlJSEyMhIkSeLGjRswm83Q6XSIjY2l09bV1eHhw4fo2bMn7t+/j4aGBvoel2uJiYlISEig79+5cwfBwcG856etrQ35+fno1q0bAgMDERgYiD59+qCwsBApKSno1KkTACA3NxdGoxF37tzh1YGJxsZGOs5CcHAwgoODWX1eX1+Pe/fusfKoVCqr/UShoKAAzc3NrN9iY2ORlJTES8u0JwaDAbdu3UL37t1RVVWFyspKOl1SUhKrjUtKSli7a1JTU+lnlpr0NzQ0oKioCAAQGhrKmhwXFRVBrVYjJSWFZRPUajV69uxJ68S0a0zk5OSw5N26dYsXlyQhIQGdOnWibUBMTAxCQ0NZx/KCgoKs9pM1m3zv3j1eHLmIiAikp6eDJEkUFhYiICAAiYmJrOdHo9GgR48etNy2tjaWDfD390f37t0l6VFaWoonT56wfgsODqbfdBcXF6O1tRXZ2dmCtlqtVrOeF4pbVHmFhYXw8/NDUlISbty4gaysLLS2trJsM9OuWQMls7y8nBdrhWkDSJJES0sLK4ZhcHAwsrOzUVpaim7duqGxsRFFRUWs8YZCRkYGwsPD6euioiKWDQCAyMhIpKWlAWh/XrRaLeLj41lc8/PzQ25uLq/duVzjjpnc8QYAkpOTERMTQ9uthIQEBAYG4u7du3Sa0NBQZGdny5ovUWNqVlYWbwzV6/Ws48lcG/D48WOUl5ez5DHHn7KyMjQ0NND9wkVJSQn0ej06d+5M21J/f39W/BhqbmMNctZsT58+xePHj5Gbm4s7d+6wxpuuXbtCq9WyZFJ/A6DrxLRrANsGUHUS2yny4MEDmM1muk7UvEClUiExMVFyv4nZNQpMG/D48WPex3sCAwPpnVSlpaVobGxETk4Ob75GzXXEwGwni8WC69ev85w21NyGybW2tjYUFxfTabg24MmTJygtLaWv4+LiBJ0jQvNqoTkn9/mxxjVKZl1dHe7fv0/fDw8PZ51YcBTc5yc+Pp6eB0jhmhioeFHc+Zuz7Fprayvy8/MBtM/XwsPDZa0Bubaaqn+PHj2g0WhYaa1BqDxb82hbujmyDqW4Rh0xZI6ZcmVKdmDJ2Zkg54213DyugFDjieknNZ0jsEemI84NR+okte2U4pE3cU2qY8bbuKZkeVLrDvzPcOv1ely/fp1n7FNSUpCQkEBPvuToQaGtrQ379+8HSZIYOHCgVQfW3bt3ceXKFaSlpeH06dOsRVhCQgKioqKgVqvpM+XcxfCIESMQEhICf39/HDx4EBaLBRqNBiqVCunp6bh69Sq9QE1PT6edHE1NTTh69ChvYKZkmc1m7Nu3D3PmzEFtbS2OHz9Op+nZsyeioqKg1WphMBhQWFjIO0sfFhaGtLQ0uh0tFgtaWlpw8uRJ1iCYnJyM8PBw1NbWYt++fQCAl19+2aoD6+LFi7h79y69UAkMDITZbKbjbAUHByMgIAD+/v5oaWnBhQsXeIuD3NxcREdHQ6vV4sSJE0hMTMSAAQNYsbp0Oh1dB5PJRE/kSJJEW1sb3RdarRY3b97Eo0ePsGDBAjq/2WyGXq/H8ePHWY5JABg3bhyCgoIQGBgIgiAwcOBA3LlzB7t370b37t0FHSMmkwkNDQ04fPgwbwISERGBxMREaDQa6PV6nDt3juXUAID+/fsjIiICWq0WR44cQbdu3dClSxdWnakJiVarxY0bN1BWVibowDp48CBeeuklREVFwWKx4NixY2hsbERGRgZrkVRRUYE///wTGRkZuHTpEmuxzeXaO++8w3pWTpw4geTkZN7z09LSgn379iElJQWBgYEICQnB5MmT8e2336K1tRWdOnUCQRAYO3YsDh06hBMnTlidqNbV1eHw4cNoaWnBtGnTaMci0D6hLCsr48Vw8/PzQ2ZmJoKCggTf9lOTyby8PN7EuVevXoiMjIRWqxUdY5qamrBv3z7Ex8fj9u3buHLlCn3vhRdeQGhoKAIDA6HX63Hz5k160gsAo0ePRkhICIKCgmiZ1dXV+Pfff6HX6zFr1izW5DsvLw9qtRoRERE4cuQIvWgKCAhAZmYmtFotLBYL6uvrceTIER73oqOj0alTJ5p7Z86c4QXDHzRoEMLCwhAYGIh//vkHffv2RVpaGqtdY2NjkZyczNKbCTF7rtfrcfnyZTx8+JB1Lzs7G3FxcdBqtThz5gxCQ0MREhLCen60Wi0yMzMRGBgIi8VCc4GCTqejnWBielA65Ofn4/r166x7cXFxSEpKQlBQEK5evYqqqirEx8fj2LFjtGOFss0hISGorKzEgQMHWDaAKu/cuXMIDg5GXFwc9u/fj1dffRWNjY2smHFMu2YwGOj4V0C7U7S5uRkqlQparRZ6vR4FBQW8Y7ZRUVG0DTCZTHj69CmrTTp16oTExEQEBgZCo9GgqqoK+/fvR1paGj3eUPD390dgYCACAgKg1+t5NgBod77HxsZCq9Xi9OnTiIiIwNChQ1lcCw4ORkZGBssp0tLSwuNafHw8oqOjWWMmd9E1dOhQhISEICAgAIcOHcLQoUMRHR3NqmNSUhISEhKs7oJgwmQyobGxEUeOHEF0dDRCQ0PR1tZmdQylbAA1hp47d44lMzw8nB5/bt++jcLCQtqB1dbWBrPZTLfH9evXUVlZiezsbBw8eBDDhw9HZGQkq07Jycn03MbWnEnKnO7Bgwc4ceIEMjIyeONNTEwM4uLi4O/vz7Nx1PNy69YtOmYoBaYNuH79Op48eUI7sFpbW0GSJAIDAwEAly5dQmtrKz3eXLt2DQUFBbBYLMjNzWU5gFtaWlgyWlpaQBAENBoNTCaToF0D2p1bQUFB6N69OwwGA+7evcsLHxAZGYnU1FRotVrcunULRUVFSElJwYkTJ+hyCYJAamoqdDqdqBOLaiez2UzPz7gxgYKCghAQEACSJLF//37MnDkTzc3NOHLkCJ2me/fuLBtw7949nDp1ir7ft29fQUcodzeM2Jxz+PDhCA0NpeectrgmNIZmZmYiIiLCqiOF2W9tbW2sa2Yb6vV6PHz4EHl5efRvAwcORHh4OD1G2uKaEN/NZjNaWlpYtpqCM+yaSqWCwWDA4cOHodfrMXToUCQkJLBerjDnoEajkTUHNZlMqKys5M1VVCoVzT3KiSV3fS42j+Zyzdp4w4RYnZjzasr2ms1mNDc34/jx4/TLCpVKhbS0NISGhsquk+8IoQA8yZlmL5TeMeVIuR19q7NUyHFWPStwlGsWiwXff/89xo4di0mTJrHu37t3D19//TWWLVtGH71xNurq6rBixQp89NFHiIiIoH//7rvvMGDAAGRkZGDt2rVYvHgxQkJCWHkPHDiA/Px8vPnmmwCA8ePHo2fPnjCbzVixYgUmTZqEKVOmwGQyYcWKFZg+fTpIksTvv/+Ozz//nLf43rx5M27fvo2xY8cCaI9L1K9fP3z22Wd0mvPnz2PNmjVYvHgxNm3ahMzMTNZ9AKipqcGKFSvwySefAGhfQK9YsQKLFi1i7Y5auXIlhg4divj4eFltlpGRgdmzZwMAduzYAbVaTevwyy+/ID8/HxMmTMDXX3+N119/nfeW6NKlS/jhhx+wZMkS+rqoqIhVD2a/3Lhxgw4mSZIkTpw4gZMnTwIAFi9eLKhjeXk5Nm7ciKVLl/IG8N27d+PmzZuYO3eu5Drn5+fj0KFDWLp0Kc/BtX79eiQmJuKFF17A119/jfnz5yMuLo6V5tSpU1i3bh0++ugj+rqgoIBV58rKSqxYsYJuFylQqVT45JNPsGvXLt7bWqB9EbBy5UrMmTMHM2fOpH/ncs1dSEpKwpIlS7B8+XLevb1798JkMvH43dbWhhUrVmDOnDnIyMjg5WtpacFXX32Ft99+m/emOS8vD2vXrsWnn35qU7cff/wRo0ePZpX/999/Y8uWLXjvvfewevVqDB8+HC+99BJL5/r6eixZsoS2k2lpaViyZAm+/PJLwXIKCgrw8OFDfP755zS3DAYDVqxYgbfeegtVVVU4cuQISyaFtWvXIjMzEwMGDMC3336LhQsX8hzQx44dw4YNG/DBBx/Q1506dWLVq7y8HF999RWWLl3K2k1oDSaTCd988w2mT5+OGTNmsO7l5+fju+++w+effw4AuHnzJu7fv4+lS5fSdWhubsZXX32Fd999FyUlJTh79qygDWAutITGn7Vr12Lw4ME8npSVleGrr76ify8tLcU333yDzz77jHbAU7Z6ypQpNnf0MbF9+3b06dOHVSbTrm3evJm1u2rjxo0A2p3dH3/8MdatW4cePXrwdH7y5AlWrFiBxYsX48yZM3j06BErTWlpKb7++mssXboUQUFBePjwISwWC7777jtMnz4dU6ZModPu3LkT+fn5mDZtGlauXImZM2eybAAA3LhxA6tWraL76fr167h37x6La01NTVixYgXef/99AO07ClesWMHj2vfff4/evXsjJycHq1ev5o03AHDw4EH88ssvmDdvHgDg0KFDSElJYdXxwYMHWLlyJT7//HO6n6zhzp07OHDgAD777DPemLp161b079+fN4ZSNmDTpk3IysqyOYYycfToUZSUlGD+/PmC+hw8eBBpaWksmffv38fXX38tuU5S0NzcjBUrVuC9996jd7wA7TF6unTpglGjRgnmW7t2La9NgP/ZAKHx5+DBg6ivr6fnOlxMnToVd+7c4cWSbG1txddff806ovTVV18BaHfmTJw4EUuXLhWUefbs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(internal link)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cOXNGvO+JJ55A165dxTQ/+ugjlJeXo2PHjnjmmWfEv1++fBnvvPMO/vrXv2L79u2yM9hmzpyJsWPHip9//vln7Nq1S/x82223IT09XVbekydPYvHixQDatCZd8fXFF18gKSkJN910k6xfzLSWnp6Ov//971pdYJlffvkFO3bskP2tW7duePjhh8XPxcXFsjMK+vbti7vuukt2T1NTk6xfBB566CHLb8I2b96MVatWiZ+nTJmCa6+91lIaUn744Qfs2bMHMTExeO6555CUlKSyQVbZs2cPvv/+ewDAo48+ivz8fNn327Ztw6+//ir7W1ZWFp5++mlTo/zuu++iqqpK9rebbroJI0eOtFxOARa7tnv3bqxZswbPPvssFi5cKFt1Y2bX/vKXv6jyXLVqFbZs2SL7m9KusdDc3IxXXnkFd911F1paWvDFF1+I3w0ZMgR33HGH+Hnfvn349ttvZfenpaXhH//4h/i5srISr7/+uiqfp59+GtnZ2TIbsGPHDmzcuFG8RmkD9GhtbcXrr7+ucgDmzJmDAQMGoKmpCa+88grmzJmDPn36yK7Zv38/fv75Zzz33HNYvHix7C3/Y489hrKyMvz8888A2uxaTk4Orl69ijfeeEO8TtDaBx98gMuXL6NTp0546qmnALRp4cKFC7LxBmAfQ4G21QdffPEFnnvuOXz99deylXf33nuvrE5LlixBcXGxyq4JKLUGABMmTMD06dN181+3bh2OHDmCP//5z3j33XdlK0GfffZZpKWl4cyZM/j444/xf//3f0hJSZHd//3336Ompgb33HMPXnvtNdx6662IjY3F0qVLxWsGDBiAOXPmiJ8PHDiAb775RpZOSkoKnn32WcdvMt977z1cvXpV9jel1lasWIGdO3fKrunevTseeugh8bPWuCloTTlhY/XXjNi0aRN2796NF154QfTZpOj5euFwGG+99RamTZuGESNGyO4pKSnB4sWL8dxzzyExMREnTpzAkiVLZNfEx8fj+eefN/Vt5s6di8LCQtTU1ODVV1/Fo48+itLSUqxYsUK8xkxrUpRaA4BZs2Zh9OjRpvcKdV+2bBkOHTok/v3uu+82vVdp10aNGoXJkyfj1VdfBdC2bXHGjBni98oxFIDMBgDO/Gjgf9sepT6ngGADGhsbMW/ePNx3332oqqrCsmXLxGtGjBiBm2++GQDw4IM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UW9Kg77Bhw8SVP8J4Ysf/UiKMP0Yr1sxss/L72NhYcWXZ4cOH8cUXX2Do0KGBW83jdAzlkZcVhHbWGx+F752eWQq0jS/CPFPvez3/jeBHc3Mz6uvrDXeSSOMDbmBFu/X19aqYhlXtS7UXCoXQvXt38aD4999/H1evXtX8JW+3sVKPYFm6/8/atWuxYMECbuktW7YMy5Yt0/1emOjX19fjlVdewZkzZ7Bnzx688847tvNcv349/vvf/5rmaYUTJ07g9ddft2U4Fy9ejJUrV+p+/9///hfr16+3nO758+fx8ssvi47TmTNnxPOvAOv1PHLkCN54441AOaVnzpzBvHnzxP8+//xz22lptYf0b4sWLZLlZfdX6ezQ2tqKN954Q8ybVWtLly4VfwLYK/w+c2fhwoVYs2YNl7TC4TDeeecd7N27l0t60nT379+Pt99+23Z7bd68WabHLVu2mOZ59uxZ0QasWLEC8+bNw6uvvur40EmjPGtra/Hyyy/rLu2+evUq5s2bp/nDGyzpKyktLWWu05133om+ffuKbVhUVMT9PEVW7GrNTD929LVt2zbZtmUtysvL8fLLLxuuHA6FQnj66adFO/3666/j8ccf1/wpeqvs27dP9vzwtDvvv/++7FyLd955B3v27OGSdmNjI1599VXdX4DzkhkzZmDSpEmi/rt3747Zs2eb3tfU1ITXXnsNxcXFmt/X1dWJ/poZbujXCt988w2+/fZbV/PYuHEjPv74Y/HzunXruPrRgLydvvrqK6btm0Zs374dH3zwgdNiifzyyy9YunSp6XXNzc14/fXXVb9S/eWXX8rGO17Pz+XLlzFv3jzT8yXfffdd7N69W/O71tZWvPnmmzh06BCXMvlNSUmJq36BE+bPn48dO3ZofsfTXzt06BDefPNN3fnO7t278e677+re77ddiwbC4TCOHTuGN954w/CFjxAfkP6arV/8+OOPTMdhGGF1bhBErZmuwPrpp5/En/lsbW3F999/j8TERCQmJuLBBx8Uo/1JSUl4/PHHsX79elRUVCAlJUU81FYZUdu1axe2bdsGoG1isX37dtEo33vvvSgqKkJhYSHef/992X2hUAizZ8/W3ZceExODuXPnYseOHdiwYYP499GjR6Nbt27iYcOzZs3CyZMnVeknJCTg0UcfRefOncVliB988IGqY6677jrk5eUhJSU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52LRpk6zdBD326dNHfJm7ceNG1Zio1JrwIlgYQwcMGCDapStXrqCxsREXL17Evn37MHjwYFW/HTt2DNXV1ejTpw+zXauurhbz3bp1K5KTk5GSkoKioiJs3rxZ5a9J+2nChAmora1FWVkZVq9erZqzCf5aU1MTLl26hDVr1iAlJUWWntBmQ4cOVfUl0Pb8XLlyBc3NzQiH236U7ujRo0hMTBR9m06dOqF///7YtGmT6gysq1ev4sCBAyr7LdVaUlISrGAneGsawNq/fz/i4+NVb2nq6urw4osvim+am5ubcfDgQcyePVv2pvXdd99FU1OTZgArFArh2LFj6N+/P8aMGSP77ty5c3jvvfcwevRozQCWcP+ECROQnJyMkydPYtGiRfjnP/+JxMREHD16FEuXLsWECRNEx0f55ra+vh4vvvgievTogatXr2Lbtm14/vnnZW9ZPv74YzH6KTBq1CjNgx0bGxvF1QY1NTWyz1lZWSgsLEQoFMK4ceNw6dIlVRT36tWr2LBhA55//nlVUOOzzz7D2bNnReM3e/ZsmbCXL1+uu3feCvX19Vi3bh2eeeYZ1cD+66+/YseOHWJfnT9/HomJieIqMqAt0Paf//wHRUVFOHLkCCoqKvDwww8D+J9T39zcjI8++gjdunVDp06dRMdVa8C4cOECtm7dihdeeAExMTGyicH8+fNRV1cnGtwTJ05g9OjRssn52rVrsW3bNtlqBSknT57E8ePH8eSTT8r+3trainnz5iEjIwN5eXliPwqTCeFzt27dVMZMWEl36NAh3HjjjejWrZvs+99//x0rVqzQDCJocenSJTG/xsZGlJeXiwcDjho1CqdOncLhw4fx9NNPy+4Lh8N4+eWXkZaWhtzcXKxfvx7PP/+8+Ja/paUFL774Im688UYMGzZMNFJlZWVITU3FTTfdJKZ14MABLFu2DBMmTEBJSQmSk5PFVWQCtbW1eOmll9CvXz+cPXsWO3fuxP/93//JVsUsWLBAtrrQ6iolPfr374/hw4eLdbrpppswdOhQNDc349///jdycnLQuXNnQ62dP39epjWgzc795z//QV1dHYA2O3fgwAH88Y9/VK0sFbTGcu6VXp1ra2tx6NAhPProo+IA0NLSgn//+9/o0qULEhISsHHjRrzwwguqszAWLVqES5cuoU+fPjh48CBuueUWVZBg+/btWL1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YY2hqasKwYcNw7733+r/t3LkTzz//PBYsWIDly5dj69at/m+33347xowZ439+7rnn8NVXXxXZvuiii3DDDTf4n9etW4fXX3+9KE23bt3wwx/+ELlcDh9++CE+/fRTPPjgg/jd736HY8eO+el++MMfonv37ol1ef3117F+/fqi74YPH+5PgNICBblcDlu2bMHSpUv977/xjW/gyiuvLEp79uxZ/PKXvyyx8Z3vfAf9+vUr+i7M2re+9S1cdtllANrfTTZt2jQMGzYMf/zjH/00gwYNwr/8y7/4n/fs2YOnn366yG51dTUeeugh//qpqanBww8/jMWLF+Pzzz/HLbfcUlTnuLqzDpJJgUMK7dy5E88++2zRdzU1NViwYAFqa2vxxRdf4OWXX8aCBQtKFk7PPPMM+vTpgyuuuAK/+tWv/AG6S5cu+NGPfoQ//elPOHjwYAlrK1eu9G1cccUVGD9+PH73u98BAL797W9jxowZ/u9vvvkm1qxZg1wuhx//+Mfo3LlzYt3Dvy1evBhffvll0XcTJkzA/PnzE9vlzJkz+NWvfgUAuPTSS3H11Vf7v61evRpr167FD37wAzz55JNF71x78MEHi3ZTiOqrr74qejfemDFjcPvttwNor+MXX3yBxYsXF+Wpr6/HggULStogzNr//u//orGxsSjNzJkzMXv2bCbffv3rX2PGjBkYMmQI/vSnP/nfDx48GP/n//wf//Pu3btL3utTXV2NBQsWRD5lD/r47LPPlrxLMni/+ctf/oJBgwZh5syZeOyxx/w0QdYA4NixY/jNb37j/967d2//fpOm1157DRs2bCj6bsSIEbj77rsBAMuWLUNLSwvuuOOOojQtLS145JFHcNttt6GpqQmvvfYaHnrooZL7zf33348hQ4b4n//85z/jwIEDRbZmzJiBb3/72wCAJ598EhMmTMCkSZPw29/+1k/Tt29f/L//9//8z42NjUXjGlB6D/W+C/4//G+Wz2HJjmsA8Pe//x2rVq3yP1911VX45je/CQB4/PHHccUVV6Bv375F75aMuodGjWsPP/wwampqEst/5pln0NDQUPTd1KlTcc011/ifV69ejXfeeacoTc+ePfH9738fuVwO7733Hr744gt873vfK7H/xBNP4PLLL8fUqVNjffCUz+fx6KOP+q8h8MbmoOLafOPGjVixYkXRd+E556l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8sTLJmqxsYNWTadYoyxdlLY4lxxqNZMdoCtaoy+eZ2/CwxusHrw3KfKrssCppHm2C+3JiLS1PObHGsiahDmpR2lLthwxrVD6Ugx2ANj4gKpl+o9oNFWdL9FqU9cWUHcp7OlcAi7qhvV0QrIs03nIpgxas5ctM/Kl9MWUnHBgIL9qoOOKNLlOyxhIhT0sj275ZYY3FF6rr3jRrhUKhiDXe/FG/8/oXFSQxvStAVxA/C6xRlO+V7bGW5CevfyJ+eP9mtSFatk47aWIJIlGylvR7nD8U9/Aga7z5KdM41pJ9iBoLVJVdjqwFFfTBNGuy5YuWFyWKBzFhW2llU7IWtCnSrir6IIo1UVGypmIskY0PUJarkzUb12ymWKOID3iqEfKAUCoHZdYta7w3TCp/dJVrSt4AZEsddbMWlaYSWAuXZapcE5JduFL64FjTV65umahzkh+m20OHbGdNhT829aktzOuQKGuUi60km441fVJdvghrqnxyrNnDmmlfVPtgG2u659Gm+5dirZQzse3YycnJycnJycnJycnJycnJycmJVVxHCJ2cnJycnJycnJycnJycnJycnHTLBbCcnJycnJycnJycnJycnJycnKyWC2A5OTk5OTk5OTk5OTk5OTk5OVktF8BycnJycnJycnJycnJycnJycrJaLoDl5OTk5OTk5OTk5OTk5OTk5GS1XADLycnJycnJycnJycnJycnJyclq/f8AR/Jq/lP9PMMAAAAASUVORK5CYII=\n"},"metadata":{"needs_background":"light"}}]},{"cell_type":"code","source":["# Compute and compare embeddings\n","ztxt1, ztxt2, out = model.apply({\"params\": params}, jnp.stack(first_sentences), jnp.stack(second_sentences))"],"metadata":{"id":"QZOuehV8SoSc","executionInfo":{"status":"ok","timestamp":1678890469988,"user_tz":-60,"elapsed":9475,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":18,"outputs":[]},{"cell_type":"code","source":["probs_txt = np.array(jax.nn.softmax(out['t'] * ztxt1 @ ztxt2.T, axis=1))\n","pd.DataFrame(probs_txt).style.background_gradient('Greens', vmin=0, vmax=1).format('{:.2%}')"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":175},"id":"8VljxbXbTHf1","executionInfo":{"status":"ok","timestamp":1678890470393,"user_tz":-60,"elapsed":416,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"9ab19f19-7b8e-41ea-c595-60794e6c992c"},"execution_count":19,"outputs":[{"output_type":"execute_result","data":{"text/plain":[""],"text/html":["\n","\n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n"," \n","
 0123
098.74%0.00%1.26%0.00%
10.00%98.67%0.00%1.33%
22.96%0.01%96.99%0.05%
30.00%0.00%0.00%100.00%
\n"]},"metadata":{},"execution_count":19}]},{"cell_type":"markdown","metadata":{"id":"_w1fANZM0FZY"},"source":["## `tfds` zero-shot evaluation\n","\n","We provide two zero-shot clasification examples\n","- Image classification on CIFAR-100 (run cells after \"Dataset and preprocessing preparation CIFAR-100\")\n","- Text classification on SST-2 (run cells after \"Dataset and preprocessing preparation SST-2\")\n","\n","After that, run the cells after \"Zero-shot classification\" cells to perform the classification."]},{"cell_type":"markdown","source":["#### Dataset and preprocessing preparation CIFAR-100"],"metadata":{"id":"QCo1J3x0c2gG"}},{"cell_type":"code","execution_count":20,"metadata":{"id":"Ojt_6bny09Nt","outputId":"c03fabc7-6366-4dc2-e6a1-e81502300179","colab":{"base_uri":"https://localhost:8080/","height":321,"referenced_widgets":["664319014bea49ddb0682ca183f37d38","7b80afb39bd34ffbb6ec71999f043a0d","7f8cfe19a0774252b9a2e0acf8712b95","24291661ee6a4e1ea39fe36a2461ad03","456418b864a04211a89f7ded9893182a","4e035e47a511466aaee1f410141dc219","1fe28e969c15490b8d5deddf404bbfc8","9b493b2b10d749af94288e1da31082de","7910356c0b024c48995d1215cfc40d92","57bca4c7a4e048118328e261f2816485","1981f2194ce14b9d858b4e66ad749a74","7bf24c0b526849148f79ddbe489fecbe","d96f75be285b4ece84a79b80525e07ff","eae9d0ad46014301a4104063786c6513","33daed476d1e48aea2c175d7d165007c","25ac8ad9428446638d2e831154b67e9e","096d4f03e30642ecb75b856d32192b77","ff4aa1191d5546e29cfc0b2099deaf32","5bd7bfbeae434bd0ad6d1a004866e8bf","49a2dd8bfc2440dc87edd2a379b3b9df","117b9b987ba646239156062a37e44881","7bcffca94bd349ce8e36b41eed824e9f","14202fa017664237abee369e0b787b2a","e77432e0ad7a48cda8a84a59c7c4fcd9","81cb50b67148443a97315c6004b9bd9e","072d46906efe4a07ac2a04bf532ca56e","d4a3cb590ed2442682da8cc156ed9ad0","df162d04da2a41209af80c51808caf2b","c1ed3a33cca94d0f8e91ea8cafbeffca","621ed7453b7a48dfa3fb5d5c0da0bec3","8c3a583468f54969a8203d83a3b511ad","259cda89a60d4ae28a23d222bf17a4f4","5d02bdf68b19451dad6399a596c700f9","31d1942f4b104c048fe34b06fd5612e4","e22728de4ffb4c2ca07c8a6ac6da10de","282d4ebc49794e869d299726b52bcf21","f74afaef34b14da7a6e5f0c2f569f68d","70dfcbb20077452a95f7b585fd182be6","99cdd80186aa49b891f7a5d4de962f18","5f8b6e4a048a4fd5b944e4d7853b9a71","1418a4fa26a64e6bbdcca38fb682e39d","c1e9314ff7904b8dbc50a8708aab1eb1","0bf192b5c7034c2191c5f92c7df1c9dd","2f71d1430e9c45799e8949ec0158a222","07689a70f74446bcba36184e47560bd1","65f301b7b02d4c4dba0f233f9e9e425a","e4412596145f4c01af01d51402ed45d6","179510514bfd4e3ea9c46889b13da181","3021ffddf75e42759b0749f82cb17eb4","5c101b460ebd4a27a01a2ada97f777a7","807aa95840eb41b4859a300500d8beec","46d2eb5e2620481f9495b472a8c84775","d7cbb87d3c9245a8b339ecbcea83e8c2","6d3ac9db950848f3baae1fa93b8e68ea","dee93ec00db64a9d8f3f1edd65a90ac8","4dc9469114be4125b4d344d644f5aff9","274ee0dd728f4a33be912fcd1786f058","5708d1ee150f46029173593d2ec4c21f","33749fbfb6d9403f962568ba42c1e8ed","a586b099410a4223b0c62b21e94b6c10","22b60f7e0f234a7e83c01a0b7f5ad9b1","a39974f014e24fc5ac43e72b34342e9d","f10a575c66664f618e179e4df8dc6fa3","5c3235bc34964d51b54fa096fc92c1f9","f43832732153414a951c1810c899c531","84bce2199a5e4d1c8526b1a6d1059cf5","40ad9dad27c747f5afc95f69b5bf42fa","3978ee7fa0f444bebf6da70d16c26095","f01d3d3fc2c84fb2b2bd14560cdd7fa3","8f28e75e20b24966b582137d4dfa31ee","4aaf504fe9fc4d618d4174ae9844b347","44a3a4df887a4bebbabd04033546d7c9","7fd53f280d914a8395c6c1c3cdc677ce","b83e550fff71444480e85f9155edb0d0","3742d1e304584823a975a123bfe2ccb4","135831c870fd49aeaf21294f8256f1f9","2d8ab80c4b5742a2b8ac5154d58d52d3","4be15f66f88c4401a769ce297c325705","e09cae9d4eb44f84892d1e4f705b13f8","ad869a7a2c3948f6b2485510529df75b","8e343e5259294118806a3144ef125d44","07ed03f79ec643028f7ed7c741d284a9","bb2436bfe4b047979ad69dbf58b12836","9e683e88a8e042889c004fd9e34e5ba9","d247a7b15d814991933dcf6705f5904b","b8130ff7e9f64911b7f11dd947d9eea4","cca976a0f62f41c9a6161e60be7f757c","47fb22e77cd44a75b752f4b8ee16c026"]},"executionInfo":{"status":"ok","timestamp":1678890542909,"user_tz":-60,"elapsed":72522,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"stream","name":"stdout","text":["Downloading and preparing dataset Unknown size (download: Unknown size, generated: Unknown size, total: Unknown size) to /root/tensorflow_datasets/cifar100/3.0.2...\n"]},{"output_type":"display_data","data":{"text/plain":["Dl Completed...: 0 url [00:00, ? url/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"664319014bea49ddb0682ca183f37d38"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Dl Size...: 0 MiB [00:00, ? MiB/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"7bf24c0b526849148f79ddbe489fecbe"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Extraction completed...: 0 file [00:00, ? file/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"14202fa017664237abee369e0b787b2a"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Generating splits...: 0%| | 0/2 [00:00"],"image/png":"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\n"},"metadata":{"needs_background":"light"}}],"source":["imgs = next(iter(ds_test.batch(4)))['image']\n","\n","plt.figure(figsize=(15, 4))\n","plt.imshow(np.hstack(imgs) * .5 + .5)\n","plt.axis('off');"]},{"cell_type":"code","source":["#@markdown *Prompt engineering*\\\n","#@markdown The [official CLIP Colab](https://github.com/openai/CLIP/blob/main/notebooks/Prompt_Engineering_for_ImageNet.ipynb)\n","#@markdown lists two sets of prompts: the 80 prompts mentioned in the [CLIP paper](https://arxiv.org/abs/2103.00020)\n","#@markdown as well as a shortlist of 7 prompts. That will be used by default for speed,\n","#@markdown but using 80 prompts boosts performance a bit.\n","use_80_prompts = False #@param {\"type\": \"boolean\"}\n","if use_80_prompts:\n"," PROMPTS = [\n"," 'a bad photo of a {}.',\n"," 'a photo of many {}.',\n"," 'a sculpture of a {}.',\n"," 'a photo of the hard to see {}.',\n"," 'a low resolution photo of the {}.',\n"," 'a rendering of a {}.',\n"," 'graffiti of a {}.',\n"," 'a bad photo of the {}.',\n"," 'a cropped photo of the {}.',\n"," 'a tattoo of a {}.',\n"," 'the embroidered {}.',\n"," 'a photo of a hard to see {}.',\n"," 'a bright photo of a {}.',\n"," 'a photo of a clean {}.',\n"," 'a photo of a dirty {}.',\n"," 'a dark photo of the {}.',\n"," 'a drawing of a {}.',\n"," 'a photo of my {}.',\n"," 'the plastic {}.',\n"," 'a photo of the cool {}.',\n"," 'a close-up photo of a {}.',\n"," 'a black and white photo of the {}.',\n"," 'a painting of the {}.',\n"," 'a painting of a {}.',\n"," 'a pixelated photo of the {}.',\n"," 'a sculpture of the {}.',\n"," 'a bright photo of the {}.',\n"," 'a cropped photo of a {}.',\n"," 'a plastic {}.',\n"," 'a photo of the dirty {}.',\n"," 'a jpeg corrupted photo of a {}.',\n"," 'a blurry photo of the {}.',\n"," 'a photo of the {}.',\n"," 'a good photo of the {}.',\n"," 'a rendering of the {}.',\n"," 'a {} in a video game.',\n"," 'a photo of one {}.',\n"," 'a doodle of a {}.',\n"," 'a close-up photo of the {}.',\n"," 'a photo of a {}.',\n"," 'the origami {}.',\n"," 'the {} in a video game.',\n"," 'a sketch of a {}.',\n"," 'a doodle of the {}.',\n"," 'a origami {}.',\n"," 'a low resolution photo of a {}.',\n"," 'the toy {}.',\n"," 'a rendition of the {}.',\n"," 'a photo of the clean {}.',\n"," 'a photo of a large {}.',\n"," 'a rendition of a {}.',\n"," 'a photo of a nice {}.',\n"," 'a photo of a weird {}.',\n"," 'a blurry photo of a {}.',\n"," 'a cartoon {}.',\n"," 'art of a {}.',\n"," 'a sketch of the {}.',\n"," 'a embroidered {}.',\n"," 'a pixelated photo of a {}.',\n"," 'itap of the {}.',\n"," 'a jpeg corrupted photo of the {}.',\n"," 'a good photo of a {}.',\n"," 'a plushie {}.',\n"," 'a photo of the nice {}.',\n"," 'a photo of the small {}.',\n"," 'a photo of the weird {}.',\n"," 'the cartoon {}.',\n"," 'art of the {}.',\n"," 'a drawing of the {}.',\n"," 'a photo of the large {}.',\n"," 'a black and white photo of a {}.',\n"," 'the plushie {}.',\n"," 'a dark photo of a {}.',\n"," 'itap of a {}.',\n"," 'graffiti of the {}.',\n"," 'a toy {}.',\n"," 'itap of my {}.',\n"," 'a photo of a cool {}.',\n"," 'a photo of a small {}.',\n"," 'a tattoo of the {}.',\n"," ]\n","else:\n"," PROMPTS = [\n"," 'itap of a {}.',\n"," 'a bad photo of the {}.',\n"," 'a origami {}.',\n"," 'a photo of the large {}.',\n"," 'a {} in a video game.',\n"," 'art of the {}.',\n"," 'a photo of the small {}.',\n"," '{}',\n"," ]"],"metadata":{"cellView":"form","id":"rB9324hHgWvh","executionInfo":{"status":"ok","timestamp":1678890543807,"user_tz":-60,"elapsed":5,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":22,"outputs":[]},{"cell_type":"code","execution_count":23,"metadata":{"id":"8BQtRYhd0F94","outputId":"1878af0f-14f4-4389-97d4-5870f51fe977","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1678890543807,"user_tz":-60,"elapsed":5,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"execute_result","data":{"text/plain":["800"]},"metadata":{},"execution_count":23}],"source":["# Construct prompts for all templates\n","class_prompts = [\n"," prompt.format(classname)\n"," for classname in classnames\n"," for prompt in PROMPTS\n","]\n","len(class_prompts)"]},{"cell_type":"code","execution_count":24,"metadata":{"id":"Ye2MkSEVBcuF","outputId":"1930d7c2-4f2b-49ef-c498-ac2b9b209680","colab":{"base_uri":"https://localhost:8080/","height":241},"executionInfo":{"status":"ok","timestamp":1678890545724,"user_tz":-60,"elapsed":1920,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"display_data","data":{"text/plain":["
"],"image/png":"iVBORw0KGgoAAAANSUhEUgAABpgAAAD9CAYAAACoTnSLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAADDyUlEQVR4nO29efQdVZUvvm8GQgjzTEBABkVAkEGQIIOCIIMTKuqz2+dTezn8bFFxBl0uNNg2LR1EBUEQpdH3HvZTW6YWNJAwBCIyB0iYxwAJhJA533zv74/vqkvduudU7b3PPlPd/VmLFe6tOnvv2ufUZ59PnTr32+l2u6BQKBQKhUKhUCgUCoVCoVAoFAqFQqFQYDEudgAKhUKhUCgUCoVCoVAoFAqFQqFQKBSKvKALTAqFQqFQKBQKhUKhUCgUCoVCoVAoFAoSdIFJoVAoFAqFQqFQKBQKhUKhUCgUCoVCQYIuMCkUCoVCoVAoFAqFQqFQKBQKhUKhUChI0AUmhUKhUCgUCoVCoVAoFAqFQqFQKBQKBQm6wKRQKBQKhUKhUCgUCoVCoVAoFAqFQqEgYULdwe9+97vd6nfdbhc6nQ7ZUdGu+i+lrQSkbFXt1NnlXDPXV1Nb7DGJ87F2JGP0hVTiqIKSO1/9Jxkjxo7U/RRj3MXmMtfc+fYROz+udqXHpnK9nO/YdnzG4YvrqYgxbjB2cqiTUrak6yTFV1tAua7UuJ4bh7Qd1UR4qCaSQw5cr5rIj13VROFsYe2qJqq3o5rITxyqiert5FAnpWypJnKH7bq++93vWi+SvIOJm7CiXfVfnz592qraqbPLuWZT+263i7aDOZcaT8zcuaLIBxeUnGPgGk8BSu589Z/L+dWxjbEjdT+FGHc+7XJsuebOt4/Y+XG1Kz02levp4NTJOrjGlTPXUyGdc0pf1tnJoU5K2ZKukxRfbQHlulQTme2oJuLbUU3ERw5cr5rIj13VROFsYe2qJqq3o5qID9VE/HhyqJNStlQTuYNzXegFposuugheeOEF4zHMzSFFKDa89NJLMGPGDPjpT38aLQasj9mzZ8OMGTPg6quvJuUulgAtxyCNUGMnxM0eun9seSl/j83d6OgozJgxA30+5rxf/epX8Oyzz6LslfPx05/+FJYvXy4SA3XsUM9fs2YNzJgxg5Q7gDG+uuCCC0i+QqO4noKvrrnmmiAcmgt8jC1OfqW47cILL4QXX3wxCNeX///cc8+F1atXi/mh8vBPfvITmDFjBixZskQsBm48NsScY0nb5dTJFLieA1cfCxcuhEsuucR7DFK5uOmmm2DGjBlw1VVXkdr98Y9/hHvvvVckBgCARx55BGbMmAG//OUvxWxSEPN+ffjhh2HGjBnwq1/9KloMLj5UE43ZXb16NcyYMQPOOeecAV8c3z/5yU9g1apVvc8paKKZM2fCzTff3Hjuj3/8Y1izZk2jP0lNREXMOkmJIcU6mQtMuajLz7nnngszZsyApUuXon1QtXhMPP744zBjxgy4+OKLAaB9msglBqpdn/0ds0768lGXr/POOw+WLl2aBdePjIzAOeecAzNmzIDR0dHG84vcLV26FM4///zac1Pi+rvuugtmzJgB//f//t++7wu+mzFjBqxdu9ab/zJM1/njH/8YZsyYAa+88or1HAUN6AWml19+GdatWzfw/UMPPQRXXnllY2d0Oh3nDivaV/997rnn4KqrroKTTz4ZTjrppNoY6uxyYyq3x/jYd9994Q1veAOsWLECvSIqnTsqJHInecPG8itp19c1VPsKI0673S68/PLLaL+Ycbt06VIYGRnps4MZhy+//HJjoW3afnrZZZfB4sWLyeO2fH713q5ixYoV8B//8R9w8sknw8knn1zr429/+xvceOONvc/r1q1Diw6JhwvU9uX87rPPPrDHHnv0+CrEwzPpa5aww+H6MjC581knm1Dcdz64vvq57GPJkiW1sfrm+ve///2wevVqWLduXRK1IrU6yRWDrnOOsh2XnSQSXN8EqdxVc0apEzZ7y5Ytq11sqMsvheuvv/56WLduHZx88snwlre8pbbNRRdd1HvY3e12Yfny5b0HyNx+KLebOnUqHHnkkc65454Tk+unTp0KRxxxBOraU9FEmJiq57RdE02cOBE+8IEPoF98aPK7ZMmS3rw6hToHALBy5cq+RS9b+5dfftlpTsjRRDb44voQdZLD9SHqpKsdKS5YvXo1XHTRRdZzTLkzcVG324ULL7wQ3ve+98HJJ58MG2ywgTXGZ599Fi6//PK+7zBaPAVNtM0228Db3/72Xq1pmyYqx1BnlxKD7TPFR0g9kUKtoMZgqhdUW7/97W+tmyqkNNHKlSvhV7/6Ve8ZUp3dO+64A2644YZe+9HRUSNPYOKMwfW77bYbvPnNb4Zly5YN+Dz55JOt1xKqTn7gAx+AFStW9K5bQhO5xChhL8azwzLQC0zve9/7YLPNNhv4fuXKlbB48WKUDddVa9s2tzVr1sCLL74IU6dOhe22287Yti4x1ZuKGhP14cfGG28MG2+8MdmPC3xt26PkzjY5K8NEaph2VWAEriuk+oR7bpNoKs6hbnf1kTupLapVezY899xzvcUtTvvinLrz1q1bB8899xxMnToVpk6dWpvnpUuX9gootkAV50lvV6a232STTaLxVfUzZtJTd45LXByu5/qRaE+910466aTafu50OvDKK6/A7373OzLX23LX7Xbhwx/+MEycONEaq++377bbbjsYN24cuz0Gw1gnQ3G9RHvsvV0GNnecB6GSdXfdunWwcOFCdnvsOUuWLIFx48bB1KlTYfPNN69t8+yzz/YedktxfdnO+uuvD1tttVVj7HXAXPvVV1/Nzi3WB6Z9+d/JkyfDlltu2dg2JU3EQds10bhx46x6lsP1H/7wh2HSpEmoc7mg2j3wwANhn332YbX3oYmWLFkCv//97wfatEETSbTn1EmMDxeur7NLjWt0dBT9qxtNeOaZZ2DbbbeFqVOnwoQJE6wxrlmzBp5//nl0jNXPMTURts6mMrZ9zK8KSGqiuna277B+qRhWTfT888+L76ipjr/R0VFYuHCh9RlSGcuWLeu9bJIq19fVySlTpvR0QfW87bbbzuorVJ2cOnVq7zkApz31HEp7CU3kI64mTGg6odvtwtVXXw0jIyOw+eabw3rrrdd4046OjsIVV1zRd/zoo4+GDTbYAObMmQPbb789LFu2DBYsWAAAAFtssQUceuihPX+dTv8fkrrmmmv6tsfvu+++sNNOOzXGXY6p0+nAjTfeCC+++GLvux133BHe9KY3Ga+j2+3CFVdc0deZ06ZNgy233BLuvfdeGDduHGy00UZwxx13AADA5MmT4R3veEfv2t/1rnfBlVde2RPRRxxxBGy88ca1HbR27Vq4+uqr+7575zvfCeuttx7qWk2546Bp9baJCDDn2sCN29Tf2HOxxzhxlD+72m6CyfYNN9zQ92bALrvsAnvvvXffOVXCKo/bww47rLewfP/99/fuWQCADTfcEN7+9rf3bPzpT3/qHTv22GOtcS5cuBAWLFgA+++/P/zlL3/pxX7iiSf2XcNf//rX3tsOe++9N+yyyy4AMFb458yZ0ztv3LhxcOKJJ1r9FXjooYdg3rx5vc/FPWvrF0zuClD6tnzeyMhI388JnXDCCTB+/Hg0X5X9V/nq0EMPhS222ILFV0ceeaR1saGIn8JXpvxw+Mo2Icfe+yZI8EHdfV8GJbY77rgDnnzyyd7nLbbYAqZNm2bleludfOyxx+D555+H3XbbrbeLbvz48XDCCSf02l955ZWwbt062Gqrrfr6b/78+fDAAw8AAMCmm24Kb3zjG+GRRx7p+S7f8wCvjpvbb78dnn76aQAA2GGHHWCHHXaAOXPm9MZdkaOi/c477wzjx4+35mLRokW9n9Ip8viud70LAACuuOIKOOaYY2DWrFmwYsUKAAB405veBDvuuCM8+uijsGjRIthll13gpptuMl67DQ8++CA8+OCDvc9TpkyBo446qvdZ66R7nZSaszT5kWxXHKuz++ijj8I999zT+zxp0qReTaza/stf/gL77bcfPPTQQ73Fip122gn23Xff3jmrV6+G//7v/+59Lmplp9OBmTNn9n7WAQBg9913hze84Q2N13jVVVf1vYSx//77ww477GDMgSvXY44BADzwwAMwf/58AADYfPPN4a1vfSsAjL3MceWVV/ade+yxx/YemGPBrZNF2zLfveMd74AnnngC9thjDwAAuO++++Dhhx/uHd94443hyCOPtMZy3XXX9fgKAOANb3gD7L777vDMM8/Ao48+Cvvssw/MnDkTAPrnR1deeSUcffTRcOONN/Z+ShiriarX/ta3vrVxMRAzf/Vxn7nAhV9efPHFvt3mprlpGffccw88+uijvc+bbropHH744bBmzRr485//DCeccELfuDnmmGNg/fXXh5tvvhl22mkneOmll+CRRx4BAICtttoKDjnkkMYYsfP6ap3ecccde3V2zpw5MHXqVFi+fHlvXl++56p2AAbnGPvssw/svPPOA/GtXr0arrvuuoE6+9///d8wbdo02GijjWDu3Lnw7LPPwu677963ILp8+fKeLgCAXp0vcP311/ftztt1111hr732AoCxN9eLt7zL7W19N2/ePHjooYcAYGx+tfvuu8MjjzzSe0hTfY5x1FFHwZQpU+DWW2+F5557DgDG5i5bbbUVzJ07t1YTAQC8/e1vhw033NAYiwnSdRJ7/3LnvdhjmLg48/pqnQQAOP7442HChAkwc+ZM2GeffeDRRx+FZ555BgAAXvOa18B+++1ntVeOoey/juuxC/I2TVRGMS8H6Nfi5ToJ0K/FTbZnzpzZt3Pgda97Heyxxx6wcOFCmD9/PhxwwAFGLX7VVVfB29/+drjlllt6c4yyFq/DFVdc0fdLJIcccohxMcqV/0M+/6pDtU662vGFumdSMZ8d1unJKl544QWYN28evPnNb4brrruu9321VpTr5J577gm77bYbANTrSal5PcDYnxd46aWXep+r8/oyuOO4Ojc+7rjjei9s3nzzzbBo0aLesR122AH2228/q93qPfuWt7wFtt5664E6+cY3vhGuv/56mDBhAhx//PF9zxHK7Q8++GDYZpttBvxwx9jChQvhtttu630ua/krrrgCjjvuOPjLX/7S+8n9Aw88EKZOnQoLFiyAZcuWwfbbb997djhx4kQ47rjjjH4oXJ9jnfQRmwmNC0ydTgc23nhjuPHGG+Gggw6CTTbZBADGHtY++eST8Pzzz8OSJUtg5syZMGnSJDjooINg9uzZvfMK3HrrrXDggQfCQw89BI899hhsu+22vXPWrl0Lt912Gxx00EEDxfyGG26AyZMnw+TJk3u2Hn30Ueh2uzBx4kSYP38+LF26FFauXAkzZ86E8ePHw+GHHz6QlFtvvRVGRkb64lqyZAncfffdfW9QAYwl9frrrx8gtnvvvRf23HNPePLJJ+HZZ5+FXXbZpc/e7NmzYdq0aXDHHXfARhttBBtttFHv2B133AH77ruvcRcYwNjE6Oabbx7I20033QRvectb+q6/imrOXAuU1GDzXSi5vurOlc6dy43viltuuQW63W7fmFq0aBHcd999PUFWjev666/vG7d33XUXvPGNb4QXXngBnnvuOev4nDVrVt+xm2++GVauXGmM6+WXX4a5c+cO3I/XX399j7hvvvlmWH/99XvHn3jiCQAYe+Dz4IMPDsQxc+ZMOPLII+GGG26AbrcLq1evhrlz58KUKVNgzz33hFdeeQWeeuqpgXazZ8+Gww47jJS77bffHu644w5Ys2YNrFu3rvcQ6Mgjjxzo47vvvhsWL14Mjz/+OKxduxZmzpwJm2yyCey4446wZs2aAa6cNWsWHHrooXDHHXc489U999zTyFeHHHKIka9uv/12eNOb3mTlq1WrVsEtt9xiHA+HHHIIrL/++n3f1y22xOYrCT6ou+85uOuuu+Dll1/uy29RJw8++GBSnVy0aBHcdtttsGLFioH77YgjjgCAsUlTUb822mgj6Ha7sGDBAnjmmWd6bUZHR+HOO++EQw89FEZHR+GGG24Y6P/bb78d9ttvP3j00Udh9erVsMMOO8DSpUth3rx5vXPL99wmm2wCN9xwAxxzzDG9SXEVzz33HNx///3We/7OO++ESZMmwXrrrdc7p3hI9MILL8DcuXNh+fLlVq4x4cEHH4Rnn312wOeNN97YewAnVUeGuU5KcUCTH8l2TTYfffRRePzxxwfGzqxZs4xz03nz5sGyZctgk0026bV56aWX4J577oHNN98cVq5cOTA3vOGGG+Dwww+HOXPmwLhx4/qOFQ8+t956a7j77rth1apVMDIy0lenbrjhBpgyZUpfHA899BB0u114zWte03edTVx/7733wiuvvAILFy6EpUuXwtq1a2Hrrbfum2MAvFqnAMa45KabboIJEybAAQccAABj99x2223X8zMyMgK33nor7LfffnDTTTcN+L/55pvh4IMPhg022MDaFyb/nDp56KGHDvDdnDlzYI899oDNNtsM5s2bBy+88IIxxmnTpg3EMnv2bJg4cWLf+cWC/Jo1a2Du3LmwZs0aI2fdeeedMHHiRJg0aVLveMH1pof9ddd+9913w1577eX8FrqP+8wFXH5ZtGgR3HvvvQP9WOS+aufee++FxYsX950/OjoKc+bMgX322Qf+/ve/w5QpU/qO33LLLXDQQQfBggUL4KmnnoKtt966d7yYt775zW+2xsiZ1xd19uijj+4tCj/88MPw6KOPGu+5gw8+eMDOrFmzBuYYjz32GHS7XXjta1/bF+PIyAjceeedcMIJJ/TFcc8998D+++8PG220EWywwQbw/PPPw4YbbthbEH/llVfgb3/728C4Lx6833zzzdDpdPqOFw8bt912W7j77ruNfXfEEUcMvJ18//33w/PPP9/3HOK+++6DadOmwbp164zPMW677TY44IAD4OGHH+7tMHvxxRfhxRdf7J1r00QAAHPnzoX9999/4HsbYtVJlwdeVBsSL2MBjP2azpw5c4wab9q0aTBv3jx4+eWXYdNNN+2d8/LLL8Ndd90Fu+yyC/ztb3+DkZERGB0d7dXKI444wlivbVxfPP8qMGvWLOh0OkZN9PTTT8OCBQvg5ZdfhhUrVvSeXxXzy+uvv75vMfKuu+6CvffeGxYtWgQLFy401uPiRe1yzDfddBOMHz++7/ziRZZx48bB3LlzYd26dcZac9ddd8H48eMHtHi324Vdd93V2A9FrSnrSYCxB7VveMMbjA+cXZDa8y/f96or6p5J+cpd07E6PWl6DrF06VKYO3cujI6OWjXtLbfc0jdun3rqKeh2u7DxxhvX6slZs2bB6OgorFq1Cm6//XZ48MEHYY899oCVK1fCE088gZ7X33rrrQP3VTGv32mnneD222+HtWvX9vFNeY5R/HvvvffCCy+8AE888UTvOfdGG20Eu+22G4yMjAzUmRtvvBGmTZsGd91118AccunSpXDXXXcNvKhsu2fnzZsHo6Oj8PTTT8Pzzz8Pu+++O6xduxZuv/32nt0i56b2999/P4yOjlp3YlPwzDPPwPz58631/Y477oDJkyfD+uuv3+Pa+fPn93aJ3XfffbDHHnsM6KhivJhg4vput9ub14eskzHau9pG7WB661vf2rdqCDAmgpYvX9772wXLly/v/Q2DFStWwAknnNB3/owZM+D1r389AIxNorfffnvYfffdAWDsDwVeccUVcNBBB/X5BRi7Wb70pS/1icg//elP8Pjjj8POO+8My5cvh5UrV8Lo6CgsX76893ZWtRisWLEC9t1337639f7+97/DvffeO/DAFmDsLarjjz++b1J6ySWX9IRYceMWb78sWbIEzj///J6YrLY///zzYccdd6xdYJozZw5885vf7Pv+hz/8Iey33361C0xVNBVC7nFfBdPnCqpkLBLHXUC1vWLFCjjwwAMHHorcf//9Aw9/CixbtgyOP/743n104YUXwtSpUwfuWYAxUXvJJZfAm9/8Zpg9ezZ8+9vf7sX35z//ufbvKI2OjkK32+2Re7fbhTPOOKP3ecWKFb2H3QBjP0Xz6KOPwuabbw5PPfUU/MM//EPP1sjICEyfPh2OPPJIWL58ee/tw2KBa2RkpCcIi7eRAcbExXnnnQdvfetbjZODD33oQ32F8dZbb+0J2eXLl8PatWuh2+323iI2YdWqVb2/L1HwZCHwu90urFq1Co477rhe306fPh0OOuggmDt3LrznPe/pPewDoPPVL3/5yz6+2nTTTXsTiyVLlsDPf/7z3tuy1fbnnXce7LzzzrDZZpsZ38hbs2YN3HrrrfD1r3+9L3cFX1XFVB0K+9z7xoWvpO5Xmx3uta1atQp23XXXvv4v6mT54U/ht65OTpkyBUZGRmDChAk98bp27Vr4wQ9+0LvfDjvsMLj11lt7bTudDixYsAAmT57cezPy6aefht/97ndwyimn9MZy9QHSz372s97Dpl133RXe8pa3wF133QV33303/OM//mPvnjvssMOg0+nAEUccAbNmzbLmodvtwsjICEyaNKnvze5169bB97///d4i0fLly+HQQw/tXf8f//hHeOKJJ2DSpEkwMjIC6623Xq82r1mzBv7lX/7F+PCgwIIFC2DDDTfsm3wuXrwYLr744l4OU6pNKcUicdwFVNvSsaxZs6b3ll+BZcuWwY9//GM4/PDDjf5WrlwJBx98MGy77bYAMCas77333t75a9asgWOPPbbHJ9/73vfg0EMPhTlz5sDHPvaxvgWC2bNnw4MPPghbbLFFb45erVOzZ8+Gr371q308/Z//+Z+w3nrrwQ477NAXXxPXr1ixApYvXw4jIyM9XVD+uyoFqjEsX74cJkyY0HtDe/Xq1TB16lR43eteBwAATz75JPzxj3+EvfbaC26++WY47bTT+nL3b//2b/DGN74RvcBU+OTWyVmzZsF3vvMdABjjx7PPPhv+x//4H7DZZpvBzJkzYdttt4VDDjmkF99zzz0Hl112mXGBacWKFXDYYYf1xf6Xv/wFHnroIdhxxx1h3bp1PX4EGJsvfe973+vx3YoVK+DQQw/tLRKWNZEJ3W4XZs2aBd/+9rf7rv3iiy+GbbbZxvnnBCUQkxOK49U6CfBq7k31YtWqVbDzzjv35f2ZZ56Byy+/vDdPq9bJH/3oR73596pVq2DHHXfs7Qh4+OGH4dprr4UDDzzQGu+cOXPgIx/5SI8riu9s8/piHJUffBcwafE//elPxjnGsmXL4KijjurbNXjFFVfA448/PrDAVIYt93vttRc8/vjjfd+98sorcPfdd8Mpp5zSa1v+G89z5syBj370o72H1MXDngceeAAmTZoEDzzwAHzmM5/p833GGWfA4YcfPhDLAw88AFtvvXXvofxjjz0GV111FXzuc5+DkZER43OMc845p8dPr3/962G//faDuXPnwiOPPAIf+tCHajURAMC5554Lu+22G3qBKdY94XteXIZUDKOjowN1stPpwJlnngkHHnggAIzV2YMOOgimTp0KAAB33nkn3HPPPb1nScWO3jpN98ADD1i5/s1vfnNf22XLlsG4ceOMWnjt2rXW51dFW5MWLx6yluemixYtgl/+8pcDvwRUXMu0adP6FqtmzZoF8+fPhz322IOtxW0LTIX9008/ve96fv3rX8Pmm28OW2+9NQDEeZ7k0j6VeFOok9zjde1MerL8HKKK0dFRGB0dhbe97W09G2WuX7FiBRxyyCE9rr322mvh4Ycfhh122AEef/xx+J//83/2bJX15PLly3u2i787vXbtWnjsscdg1apVcPzxx/fa1c3rb7vtNnjve9/b0/Ddbrf3DGeHHXZgPUMaGRnpzZsLmytXruyL6Qc/+AEccMABcPvtt8M73/lOeO1rX9uL7e6774Y777zT+Es45Xu2OH/OnDk9Ttx+++3hiCOO6KuTq1evhh/+8IdwxBFHwLJly+C4447ru+d/85vfwCabbCKywLR27VqYMmVK38s3a9euhTPPPLPHVcuXL4djjz22p2suv/zy3rPzkZERmDx5cm9us3LlSvjRj35Uu8B0//33w3bbbdfH9QsXLoTf/OY3MG3aNJF70dfzrxC+m9C4wGTDnnvuCXvuuSfcc8898Pe//73vJ6qOP/74vp+WAYC+n+LYd999+x5Um4C54J122gl22mknePLJJ+GFF17oi6Ha/m1vexs88cQTfX+0zfbb6Z3O2Bbh+fPn9z1cLf+cxa677tpbXDI9gC2IktL5o6OjA3mrTkxM9pq2yFG30IV+8yKVh2amWFxzi+kvbmxN9o466ih47LHH+sZ50289234KZN9994VFixb1jc/qH5EutzvmmGMGxnIZm266ad+W/iqOPvpo0k9JFCgE4fz58+Hwww/vCdF7772XbMuGLbbYAk488UR45ZVX4IEHHujjnWpfFIvmf/3rX2HlypW9+BYtWgSTJk3qbdE15fyJJ57o4xwTXxX+THxV3kG22267WX9eDwAGhHQZtnG3bt26vp9pKL6jorAvfV8UqFv8ofoLtYvz4IMPhoULF/bdQ6Y/OIq1v+222/Y9NOt0Or0HJRwUW9Or/V9sTZdCp9OB7bffHjbbbLO+XFRr7rHHHmt9CWO77bYzTgabcrd48eI+n+WfIcO0l0bdfCJ2nWw6nnKdpM7TmvC6170OXn755b6xU15wMfk59NBD+x4Yl7HBBhv0foaDEuPWW28NJ554IixZsgQeeugh1M/IUn0AQG+x+A9/+ANsueWWAz+xVaD8U7Z33nknHHPMMX0LLPvss49VF3S73VpNUT6vjuspdbLbHfuVhOJnVZrwwgsv9PFh8Xv5Jhx77LHwyCOP9P3G/4svvtib82y++ea9xSQTqrnDYv78+X35KV+75D3AeXgtKbC58/bilzXqak0ZBx54IDz33HN955d/1rjp55v3339/489N1eUOYGwxpPwTeZi/4WICRYsff/zx8NBDD/XN8ZYsWTIwT+92uzB+/HjYbbfd4MEHH+zNMxYsWAA777wz6Wc1i3u2+MlKgLFFsPK9Vf4JIIw9DIqf/qlyDuVvcnS73YH5UfnnBTGIUSfrwJn/cuff1PimTJkCRx999ECfle/fadOm9RaXAMZ29u2www6wySabwIknnggrV66Ee++9t3fP1t37pvgmTpzY01K333577+f5THjta18Lr33ta+Hxxx+HF198seezeOZTxxtVLV43Nz3mmGPg0Ucf7e2QLdoXcZW1uOl6jz766IGdCRjMnz+/72WGQstKL/5Q5/VNSGUunXKdxB7HgqMnN954YzjqqKNqn39hF/LLKBZrHn74YTjssMN6fFH8RJwNFE202WabwQknnAArVqyAefPmGe/14rqKxfHZs2f38cSSJUtg4sSJfYtLVTz55JN9Naf69+XKuXvd614HCxYsgE6nA7vssgtMmDAB3vKWtwDA2EJL07WZ5tbFz3JKzC132mkn2GKLLfp4r/q86bjjjrP+SZkddtih78UZLCjz+gKU6216/uVaJ2M+/0L9RB7FSbGdrvpWsumNRipcBulzzz0Ht912W9/vYa5YsQK22mqrPrvFzfHMM8/A7Nmz+xZ4yhN6LCjxrlu3biBvW265ZW9FuWqviBtDbFIrrXUFRnrAStmty10T6s6hvJ1VPrfojyb7TWjq/4ULF8Ktt97atxC0fPnyvr+zUGe3jGJrb7nImh7wSMI199h2khPKpjFGifvOO+8cKJY77rijcSw988wzMGvWrL7ibuMrm2iicr2Jr7baaqu+N2BsaOoHSb6y2ZUqutK2ut0uLF68GO644w546qmnet9jH05g/E6YMAE+/OEP19rYdNNNYc2aNT1humTJkt5D8G63a6zz5QVRk00OVqxYAQ899FDfDiuOD0xflHNXCP8yMG9iVfNf5XqXeibNiU3129e8oQqfdbLqQwq23L3y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s5y7X//613DWWWfBPffc0xdzjMFazZXLz7BgbRTHqb5ssRa5842iUNji514PJl/VMX/DDTfAzJkza9vZxm0xuSy+f/LJJ3tv/pru+fPOO6/v5yirePbZZ+Giiy7qa1PlgCJ3ZYwbN66Pb4r7p9q2fO3lh0BN7Uzti/+KnGJQ7acij+X7vQ4m/3fddRf84Q9/sJ5f5avyTrXbbrsNrr76auu1n3322bB27dpaviriaeKrV155pcdX995770AfUgqxJLD3ThEfl3MkUPRNkdf7778f/s//+T+153PrpK3GF/08b948OPvss+Hss8+GX/ziF71zyw8mR0ZG4Oc//zksXrwYdX1F7av6NXFlcR3F+StXroR///d/77P3s5/9DF588cU+m8U9v2DBAvjf//t/93yUx3753i5fu4kvFi9eDOeddx6MjIzAWWedBWeddZaRz2NwPdeHrV3osV+tk67A2qk7j2ujzPUjIyOwaNEiOP/882tt/Pa3v4UHH3ywbzxiHuiY7vsihup51Tpm4gubTxPXjx8/3sovpp9lKaPMHRg+njBhwoB/2xyjWqfKeWmqkzYU/DB+/HiYMWNGjw8XLlzY11dl28899xxccMEFRnvV/F999dV9PwH61FNPwSWXXGKdH5133nmwePHiAa4vz3lM117t89/+9rewYMECuPbaa+Gss86Ca665pu+aMfBxD3Hhqomqenb16tUDtaaMar8/9NBD8B//8R+94+vWrYOzzz67L+/l++bKK6+EuXPnDtybdfXRNDecO3fuwG6iIh82O+Vzyv/aUB0/f/jDH+Duu++2nj9x4kT46le/2quVn//852HKlCm180qTljj77LN7sZmuvZjbVvuuWuur11f4u/POO3v3c3HPFb6qc9vqz2nacma6jpGREbj44ovh2Wefhd///vdw1llnDfyMWBNMmsgFLraocwRfc4qyvWrO//3f/936UqUtjmqtrMLE9XXPsDC+yz6bYPK/aNEi626P6j1z3XXXwU033dQ7vnDhwt683lRrfv7zn8Pzzz8/ME8o37dVnjHVmssvvxweeOABmDlzJpx11lnwpz/9CZ2vOuT6/ItiQ+r5V/n7UM8OsZqoqicvuOCCgb+vVz7/+eefhwsuuMDI9bY2AIN60ta2+gypyFVTO1N76ry+iLOcu/J9j3mGVL5Hi//uu+8++N3vfmc8/0c/+lHfs7+RkVefAxWoG3/V/rvsssvg0UcfBQDoy2OZK6rPwJt4t9xvy5YtG9hRfO6558LSpUt7cZQ1zbx58+A///M/B/quGk95jmTi2oULF8IFF1wAq1ev7s1t6mB6nkE5Xwoxnn81vjb1gx/8oCdWfvGLXwDA2Bb1N77xjQAw9vbPlClTYPr06bDxxhvDl770Jfj617/e9/M1X/va1+CCCy7oBf6ud70LFi9eDNOnTweAsT/e9ZnPfMbo/xvf+AacddZZfb9VfuKJJ8IBBxzQd15Tp332s5+Fiy++uLdIdPjhh8MJJ5wADzzwwED7TqcDp59+Onzve9/rXftnP/tZuOqqq3rnHHLIIbD11lv3rmGTTTaBr3zlK70b5bTTToPp06f3Pn/605+GbbfdFn7/+9/3Tcrvu+8+2HrrreGzn/0snHLKKT175dyVV0ddt+RKoGnLJsdOdcW+WvzqtvtyYg2Zu7o+415P0zbZTqcDX/jCF+DnP/85LFy4EADG/ljnMcccU/tA5bTTToMzzzyzN1H81Kc+Bdtvvz3svvvu8Ne//rU3Pl/zmtfAZz/7WfjVr34FEydOhG9+85t9PyPyla98xfrTMwBjvwN70kkn9ex1Oh349re/3Sv+tuvcd999e3xTYPz48XD66af3nXfKKafA+eefD8899xycdNJJ8I53vANuvvnmvnYbb7wxfPWrXzXG98///M99uQMY2yp70kknWa+pjGr8Rx55JMydOxemT58Or3nNa+Dd7353bbsqXwEA7LfffvChD33I2KbKV5/73Ofgyiuv7NmbNm0abLnllgN8VfTzaaedBt///vd77T/zmc/ANttsA//v//2/vkXt++67D7bZZhv4zGc+A//8z/9s5SvTNVEhfY9S7/0YXFHgIx/5CPzxj3+E//qv/wIAgD333BM+9rGPGR8iAdTXyb/97W+1vs4888zeJKJ4EH7yySfDI488ApMnT+67t1566SU4++yz4Wtf+xp861vfgjPOOKN37Mtf/jJceumlRh9VbhodHe0bO//2b/8GAK/WyXKbvfbaCzbYYIPe+ZMmTYJvfvObfXOMU089FS644ILez3685z3vgTe96U1w2223wR577AEHH3xwr/3EiRPhtNNOg26323fthUD/8Ic/DMcffzxcf/31fTFuvvnm8OUvf7l2F2horrfVSSpSq5O+bFHyReWJAmWuBxj7W0Ff/OIXa38i7+Mf/zjcfPPNvZ/W2n///eFDH/pQ3x/mNvn84he/CD/72c/ghRde6B1761vf2ldfNt10U/j//r//rxfPd77zHfjWt74F//Iv/9K326I8ry9j4403RnN9NT4bvvGNb8C//uu/wsqVK+FTn/pU7flTpkyBL3/5ywP+Tz311N7fcynjzjvv7Ht4NX36dJgwYQKcdtpp1jrZFPO6detg+vTpAz/hcv7558MxxxwD733ve+HPf/5zX4zbbLMNnHLKKUZ7X/nKV+Ccc87p46sNN9yw96sKO+64I5x44ok9e+PGjev7ua4vf/nL8Itf/KK3wFbm+uJ6imufOHEifOtb34LTTz8dzjjjjD7B+L/+1/+CHXfc0fjzYtzxX3fM9lBL6t53tfP617++r9YUubP9RN4HP/hBuOqqq3rn77777vDJT36ydx+PHz/eWCeLn1Z83/veB0899VSv/S677AL/9E//BC+//HLfz8oXx7/+9a8PzOsB7HPTap09++yzAQD6fmamyvW2HFbnGB/60If6fi6Pgl//+tfwxBNP9D7fdNNNsNtuu8FHP/pR+OhHPzqgC4paf8opp8B5553X9zcRpk2bBu95z3sAYOxnpas8Ub1nC/zXf/0XbL311n3zm+effx7OOeccOPXUU43PMS688MI+G7axbNJEAACf//znYYsttqhPTgNC1ElK29jz6cLe5ptvDp/97Gf7+v/b3/42/PCHPzT6NcUxefJk+OpXv9qzcfrppw88QKZyfV3MAGPPv97//vfD9OnTYb311oOvf/3rtW1OPPFEmDlzZp//LbbYAr785S8b233pS1+Cn/zkJ72Xv44//ng48sgje7Vn6tSp8IEPfMCoxQHG7rlLL720N8d45zvfCQcffPDAC4/Tp0/vafFqnQUA+Md//EfYZZdd4JlnnkHnKUVIPf+qs+Hr+VfT9z6A0UQTJkwYqJNf+tKX4LLLLrPa2mabbeAjH/lI331Q/L2/utxV9STA4PwKYIyrL7zwQnjmmWfg3e9+N7ztbW+D2267ra/dhhtuaL1fP/e5z8FFF13U97ytmNdjUb6OQw89FO68806YPn06TJ06FT74wQ/WzqU+/elPw69//eu+F1722Wcf+OhHP9p3fjHWTj/9dPj+97/ft3j1D//wD7DrrrvCdddd1/dcruqv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\n"},"metadata":{"needs_background":"light"}}],"source":["# Render prompt images for all classes and visualize them\n","text_images = preprocess_texts(class_prompts [:7])\n","\n","plt.figure(figsize=(30, 6))\n","plt.imshow(np.hstack(text_images) * .5 + .5)\n","plt.axis('off');"]},{"cell_type":"markdown","source":["#### Dataset and preprocessing preparation SST-2\n","\n","The goal of SST-2 is sentiment classification (`positive`, `negative`) of movie\n","reviews. To this end we embed the reviews and a few prompts for positive/negative sentiment using CLIPPO and compare the embeddings to perform zero-shot\n","classification."],"metadata":{"id":"n4vkj4zejNR4"}},{"cell_type":"code","execution_count":30,"metadata":{"outputId":"64b6f081-453e-40fd-990e-037b212d5c99","colab":{"base_uri":"https://localhost:8080/","height":165,"referenced_widgets":["bb1c1dafddfc4ab1bf0ea96916d088dc","412d72f0cab84cdc9ccb202015b8f09b","321f978c7d154fa9a793612bd45e34fb","c98c2b0e046e4d11bdb41466c8b9bbed","3d795eb785364e04b3458db07a689231","47defbe6eded463aa913f6c94eba650c","90a7c3c7f67c4b9dabae3124d4f4807a","f9c8321259e64b5da15a5780e48387cc","7d9c02eab6b14e64b0d54535a51d3f26","7261da104bf4488489651c59b57c810e","292cacad99804debab40eaff1f7241f0","16c4f6ca9f104713aaed23017b953711","c9382c9cc43144ccbc94ff9c126c75cb","5d35963be08f4f5e855c86ad7360b061","9ba65fbf4ff54df8832d0790b326bd74","26e7683fe07e46f2b6e42ab2cf525d1c","2e2f1aca25d64fc6a5eb6c5984ea1884","4b045a43035c4f4aa8f72acfc44361a0","999d70d6ea1c43f0a1b7005999dc2d9d","4e45f77fa3fe477eaca52d987b4d0217","86979c617e50477db737303ad9042a7c","23c0c48a8eb04a6aa12a39e102a71322","60999d5a3045449b87f83734c86adcce","7e297ca7eaf64ce1b3bdf8685acbf74f","e9b3045f23b24882b847fcf7c821f3bd","a6322a45f87d43dd8cb4edc4f366742e","56a2a0d2d23243cf8f5c6424c6a704bd","bb36af1417914c8aa0728cf0294be51c","9cd358bace4a46288e5a11e03256eee1","71ea73a230af420c8c49d9cfaa5ab007","1199e807dbea492486a7faf9aeb2e8d2","b0d8ca926f9e469ebaaf8216ad9ede8c","c2867984ecb1465f95a65d126c939991","523eaf4ccda54016b3fbbc7bcba69d09","e4a736a0c2134a51bf605daa8882db31","f288cf7496284b1bb1ee1b8c5dbf9afe","ed84f6bbac894e62ad0599dae35a9b8d","0ca6bb008cdf457fa868d567e4c5cbc9","652284bf59e846cd980fef70006cbbd7","5842f56fc48440169916b714284e80fe","aa19d3fac97b48678fbf88350a462765","0afb2cb83138438daa94145fafa706cc","af89e62625854f85bce7d651bfc4acfe","7ab055add3f54e939b70b9937ed100cd","959675b37c284184a97ddfe7fed30088","f82ad804566d4c018941d9efe57aa753","6e5a097140b14e0b9d18bf49ad747ea1","29f7931b409b48d7b23ca62965c11447","a00b6e479fed421eb64cc26089d127ec","e6b51524f5f34035a302c1e25c905f6f","2b7e38d56e6c406d85d4c2f8dbd14381","9da221b94d634814a2b2f5b2598adfbf","fe302d219a9d48f386835333902f745c","7a64baf19db946708ac3cfe9bba0ecff","9f6f17c6ffb44fa09431423d27740cdf","e5ad5de128d4472dbe7c8c722685dd63","6baf1d03147a4d509ffeca5b46bfaef8","bd1b3ebfa0f040b6a3409f83f0689612","a14de20b05b74107912737505564a9b6","ea8f4fe35c91417ea8b9f5411ff3609e","9c09190e2fdd4f97affc2234e86c5bee","864e3bd5051a47a8901a28f7aaab9ccd","1afb48bfcdc64803b2e63dac7380ffcc","a437ae7a7d67409eb066f470b01896e8","246027ed95ea40a9a037bb1218a5bab6","e02e735df1f24875ae30e06c37434a96","50f38b8c1de741eb895e3de8d10a6e97","0727fde380a54a26b84117fb58b34814","b3806705ac714b7caa823d184d09dd0a","df6c3e7278a0402cbdcf56861181d1a0","dac1bd568a134f1ab6752ccea84d2cd7","9b4029630887481f895100433ea29ca5","48e4c9c0a9ad45f488833b9533db4a67","225d0c8c8ab54188bb8823f2618a7abb","79bde2b3bba444ff83109656e67760d1","ac5c7cce9d7641d89ac2921a6ced5480","463efc760f3c424c944084d4853ccb81","de8dc6464f554376834078c8fbeb4e9b","2c301232781c44c5a9883bb0e3d6d5a8","3e2396bc62d74a5ba3dd9a11731de392","4062069fd10849e48aa2f42f0ccad406","d058e12b7c9a47b6957916f0a5735a51","a50bd51c0e774c63a9a0c23de522c9e7","13a1ce102a2045b69e4565e7a63449ae","5176c6d1c19c434aa88256aa83bccd57","274282e991c142e2a4f62410e9fd29d3","d48e121c27884d7eadfda819c78e9617","56546bf856364f65b289cdf7ef5895fa","98133af35daf47b38b785da163078430","e99cda177fcf455281a2a2b606d0d8ef","87591ac7a7e247c88cd2149b259802b9","9dba966e37cd44398d55e7d4e191f1f0","ae3d380509cf4aba8e3a7dbe3b54000a","691c71d141d34e79ab4becc4f26252df","ab344b76b2e84abb8bebacd105941428","993f4775c78243ca8241ce3de9111b7f","4dbba02a274a488e9e4874752a0ded05","6552a8e5158644e099d74dd9e1f561df","2e8ff466b6b94290aef03bb2a39389c7","009034a493bc4bc7b3658f1381bce4cc","9d9eef4bb31f4183aa14346b7aa5cb20","6fadc7132d0d42ef9c3fc83dc8ee1564","a0cb3869861e41f88d8ae0b7e9627122","1776a1f9c103417bab4873d44ab31202","fff0dce5a48745978722580454349072","891a5a8d28f5434bada9cdaea176aea7","07b3506aa2e444379a0e41486d8154c4","fcbfa3898e0a4d01962c7502974db9d5","4303aa6e9a424f46b1291aac04db28f4","8635d996805741ce87e22fac012a9501"]},"executionInfo":{"status":"ok","timestamp":1678890966974,"user_tz":-60,"elapsed":13213,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"id":"7DEsxcvtRsEX"},"outputs":[{"output_type":"stream","name":"stdout","text":["Downloading and preparing dataset Unknown size (download: Unknown size, generated: Unknown size, total: Unknown size) to /root/tensorflow_datasets/glue/sst2/2.0.0...\n"]},{"output_type":"display_data","data":{"text/plain":["Dl Completed...: 0 url [00:00, ? url/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"bb1c1dafddfc4ab1bf0ea96916d088dc"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Dl Size...: 0 MiB [00:00, ? MiB/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"16c4f6ca9f104713aaed23017b953711"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Extraction completed...: 0 file [00:00, ? file/s]"],"application/vnd.jupyter.widget-view+json":{"version_major":2,"version_minor":0,"model_id":"60999d5a3045449b87f83734c86adcce"}},"metadata":{}},{"output_type":"display_data","data":{"text/plain":["Generating splits...: 0%| | 0/3 [00:00"]},"metadata":{},"execution_count":31}]},{"cell_type":"code","source":["PROMPTS = (\n"," 'a {} review',\n"," 'a {} movie review',\n"," 'a {} sentiment',\n"," 'the movie was received {}',\n"," 'the reception was {}',\n"," )\n","class_prompts = [p.format(c) for c in classnames for p in PROMPTS]\n","len(class_prompts)"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"a9-hXwnGSGMN","executionInfo":{"status":"ok","timestamp":1678890967745,"user_tz":-60,"elapsed":3,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"b04b954c-1bf0-4c4d-ef0f-9c91a89a951d"},"execution_count":32,"outputs":[{"output_type":"execute_result","data":{"text/plain":["10"]},"metadata":{},"execution_count":32}]},{"cell_type":"code","source":["# Render prompt images for all classes and visualize them\n","text_images = preprocess_texts(class_prompts [:5])\n","\n","plt.figure(figsize=(20, 5))\n","plt.imshow(np.hstack(text_images) * .5 + .5)\n","plt.axis('off');"],"metadata":{"colab":{"base_uri":"https://localhost:8080/","height":254},"id":"bHPRePTVkn4k","executionInfo":{"status":"ok","timestamp":1678890969903,"user_tz":-60,"elapsed":2160,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"359dcb12-823d-400a-92c8-c31de895ad79"},"execution_count":33,"outputs":[{"output_type":"display_data","data":{"text/plain":["
"],"image/png":"iVBORw0KGgoAAAANSUhEUgAABGoAAADtCAYAAADnTZN1AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAAsTAAALEwEAmpwYAAAid0lEQVR4nO3de7QV5X0/4O8R8I7itQjEgom4rIk1uogpJiZab6G6RBMJNVQRREERBRHhCMFsjoAEFDXeEAkSrBEaIimS1BhtS2q0VoNdatXGeolYb6mAifnJxf37gzWTvc/Z+1w5hxd5nrWyIntm3nn3Zd7zzmfmfaemWCwGAAAAANveTtu6AgAAAABsIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIROfGFl577bWe3d1KxWIxampq2q3c9ip/e9ben43PnBRoWzqetoUdgbal42lb2BFoWzqetmX7ce2111b9ID/Rd9QUi9tfzrQ91hnq215/x6nWO9V6kZ5P0m/lk/ReaLvt4fewPdRxW2nPz2Z7+dy3l3rWt73Wuy12xPfcXnyWrfeJCGoq/QA+/PDD+O53v7sNatN6r7zySvzgBz/Y1tWAZqvW+BYKhe2uYf7oo49i5syZ27oaDbz99ttx5513butqkJhqx9fcuXNj3bp1HVybtkmlHdne2qxPutLv46mnnooVK1Zsw9o0z/Lly2P16tXbuhpJmjVrVvzxj39sl7Lr6upi8+bN7VJ2a1RqSz7++OOYNm3aNqhNy5XWf+3atXHzzTdvw9q0r0rf1b//+7/HT3/6021Qm+1btb+ht956a/zud7/r4Np8MiQb1DS3w/T//t//i+uvv76dawM7jpacrGyPgQykqiXH0i233BL/93//14616TjTpk2Ljz/+eJvWYXs6idoRrF69OpYvX76tqwG55rbPxWIxCoVCO9em/axbty7mzp27ravRIZ5++untIgDellrSL7n99tvjnXfeacfa7HiaFdQUi8Wy/zW2Xv31m1tO6esbN26M6dOnt3j/pXbbbbe46qqrKtav2mtb832WLm/u/nv37h1/93d/V3GdxvbTWJls3zr62IsovzLVmmOvWCzGt7/97bKxq1vz2CtdpyXvtak67LzzzjFx4sRG99ke30FT9TrwwAPj4osvbna9tAtbR3N/91vj2Mvceeed8dZbbzW5XWN1ueKKK2LvvfduUL9KdW7pfprbjlT6PW6tdqStx39L60F6WnOsVVqv/n83VU5zjqUzzzwzjjrqqFbXq6nXW3MMViuvLW1Wa5ZNmDAhdtttt0bfb2vea2O25nstDV9a0z7X1NTElClTqr7fSq+1Zj/tKdXfXrUyW9vu9+vXL772ta81uo/G9lPNtjjGK+23JcfWggUL4o033qi4rDmKxWJceumlsd9++1V8v5VeS+13vy01OplwROVkuH7nqXS9b3/72/n6WaNUU1PT4GrVTjvtFJMnT46amprYtGlTXHfddRER0alTp7j66qvz9TZs2BAzZszI/52dTGUTJJXuP9tnNuxpt912iwkTJkRExO9///u44447Yvz48fk269evj/nz58e4ceMaJMjdunWLMWPGNHif7733Xtx///0xaNCguO222yIi4oADDohRo0ZFTU1NvPXWW2XDFHr06BEXXnhhrFmzJn72s5/FhRdemC977bXX4pFHHokLLrggXnnllVi0aFH06dMnzjvvvHyd3/zmN3Hvvffm/+7bt28MHjw4XnzxxXjmmWfim9/8Zv7+a2pq4tlnn40XX3wxvv71rwfbt2KxGLNnz44PP/wwf+2qq66K3XffvcG6119/fYwdOzZuuOGG2LBhQ0RETJo0KXbeeecoFosxffr02LRpU77+NddcE507d45isRh1dXX51ezJkyeX7X/atGllx9mUKVNip512qtiwZ8dKdvyXthNZ25D9O2svpk6d2uI2JmLLcT558uSYNm1aWTuzefPmqKury7fp1KlTXHPNNbF58+aYOXNm2fvbsGFDzJ49O2pra/N2Zuedd45Jkybl69S/Yy8LgOvXLWtfLr744rjhhhsiIqJr164xduzYqKmpqdq+vP/++3HvvffGZZddli979913Y8mSJXHppZfGO++8E7fffnv82Z/9WYwcOTJf580334y77ror/3fPnj1j+PDh8cYbb8TPf/7zGDZsWNn38sorr8SqVavK2haqKxaLsXTp0viv//qv/LXBgwdH3759G6y7ePHi6N+/fzz22GPxP//zPxERcd5550WfPn2iWCzGwoUL4/XXX8/XHz58ePTq1SuKxWLMnz8/3nzzzYiIuOiii8r2f/vtt8e7776bv3bppZfG/vvv3+ixd9NNN8W6devKwpobb7wxRowYEV27ds23mTNnTowaNSp23333+O53v1s2HKH+yVTpfmbOnJm3LxHNa2Pq6uqitrY2pk+fnrczbWlHMqV9jWydam1Jtm3pNvW3K61DRJgkcRuq9H1k/uM//iMefPDBiPjTyVRNTU08/vjj8U//9E/5ev3794+TTjqpwff4L//yL/HP//zPERHx1a9+NTZv3hyrVq2KE088Mb785S9HsViMhx9+OB577LF8m1NPPTW++MUvxk9/+tPYf//94wtf+EK+bMWKFdGjR4845phj4ic/+UmsXr26LKwpFouxbNmyePbZZ/NtzjnnnPiLv/iLBu/7vvvui379+sWhhx5adiwsXrw4jjvuuOjTp08sWrQoXn311XybCy64IA4++OAoFotx9913x5o1a/JlI0aMiIMOOqjBfm6//fb4xje+EUuXLo333nsvIsrbl1tuuSXef//9fP3LL788unXrFsViMebOnRvr16/Pl40bNy66du0axWIx5syZE3/4wx/yZaX9laydKW1fsn7LLrvskm8zY8aMGD9+fHTp0qVV/Zb6pk2blvcVMk21F5WWZe1IROPnRdXakuwcqKamJi9r8+bNMX369LLwZvPmzTFjxoyYPHly2blRRETnzp2jtra2Xdumxo69999/Px8Cte+++8bo0aOjpqYm3n333fx8KKL8nKjUm2++GQ8++GAMGDAg5s+fHxERvXr1imHDhkVNTU289tprsXDhwnz93r17x3nnnRc1NTXx8ssvx+LFi/Nln/nMZ+Lcc8+NiIgXX3wxfvjDH+bLDj/88DjnnHPiueeeixdeeCEOO+ywWLZsWUREHHnkkTFw4MBGv6snn3wyVq5cGf369YsBAwbk6/zqV7+Khx56KP/3l770pTjxxBPjl7/8ZWzcuDE6depU1rZ85StfafD5Pvzww7HHHntE//79y47xrG3p169fPPjgg/HUU0/l25xxxhlx9NFHR7FYjOXLl8czzzyTLzv77LPjc5/7XIP9LFmyJP7yL/8yfv3rX8dLL70UERHnnntu3rYsXrw4769EVO+zZH3J7DMq7bNEbOm3HHTQQY3+Lc+GPY0ePToPa26++eY477zzYp999sm3mTt3bgwfPjy6du0aN954Y3zwwQf5siuvvDL23HPPBu9zR9BoUFN6MtXYa6VKl2UN0+TJk6Ourq5sm+yEqra2NmbOnJkv27RpU1x//fVxzTXXxMaNG2P27Nll22XzSIwbN65sPolCoRC77rprXH311bH77rvHVVddFd/73vea9SGsW7cuFixYULafrEG6/PLLG6z/3nvvxZIlS/L133nnnbjjjjvirLPOigceeCA/+aypqYk1a9bE/PnzKyazpbKAZtWqVflrv/nNb+Lxxx8vq1fWIH3+85/PX3v++efjueeei3POOadZ75ftw5w5c+KSSy6JPfbYI39t1qxZMXr06IphzcyZM2PixIl5p2f69Okxfvz4mD17dkyYMCG6dOmSr1tXVxcTJ06MmTNnRm1tbXTq1CkitnRqsn8XCoWYPHly7LTTn268KxQKMWXKlLJOSv2TpalTp8Z3vvOdZr3HlrYxWSfn448/ztuU7L+zk8HS7bKOTqU7ZUplAXAWskRsCWnmzp1bVl4WAmcBcKk//OEPMW/evHz99evXx9y5c2PYsGEV25dbbrklhgwZ0mi9DjzwwBg5cmT8+Mc/zl976623YsWKFWXlvfHGG7FgwYI45ZRT8tdee+21ePTRR2Po0KGN7oOGli5dGp/97Gdj0KBBEbHlN5l1BA877LAG6y9evDiGDBmS3xF5zz33RE1NTTz66KNx4oknxp//+Z/n62Z/D1auXBkDBgyInj17RrFYjHnz5sXAgQOje/fu+YnUgQcemG/3ve99LwYPHhw//OEP87Het9xyS0REjBkzJvbZZ5+44oor4sYbb2z2+5w9e3aD9uT666+PMWPGNAhrsr+7pSdV06dPj6uuuipmzZpVsY3JQs+6urqYPHly3s5kbUtdXV3ewctOpJpqR7Ll3/nOdxr8d7VAuDSsyY6bSheXsnVKT6boWP/5n/9Z1t4988wz8fnPfz569uwZTz/9dBxzzDH5d5jNI7HffvvF2rVry9rEf/u3f4tf/OIXcdJJJzXYx1e+8pX46le/Go8++mh07tw5pk6dGr/4xS/il7/8Zfzxj3+M3Xffvaysn/3sZ/HEE080WfczzzyzwQnLsmXLom/fvmUXz5YsWRIRUTGsydx7773xV3/1V/HpT386f23RokVx/PHHx/nnn5+/tmDBgjj55JPjoYceitNOOy169eqVL5s3b16cccYZVcOaSy65JA444ICI+FP7ct9998WQIUNi3333zdedO3duDB06NL7//e/HsGHDyu7YmzNnTlx00UVx5513xsiRI8tOpkr7KxMmTGjRNAUzZsxoUb+lsaGT06ZNK2sXqrUX1ZaVthWN9VkqtSUREVOnTo2ddtoppkyZUnYhqTGbNm0qOzeKiNi4cWPMmDEjamtrm1VGS61fv77s70ehUIi99947hg4dGmvXro1Fixbl9fnd734Xt956awwePLjsfCjiT+dEo0aNarCPN998M1auXJmv/9vf/jb/DT/yyCNl5WTzdh533HHx2GOPlS377//+77jvvvvimGOOiaeffrrse3z++efjH/7hH+Lwww+P5557Lv97ErGlPVm+fHn07t27bGhl1racfvrp0a9fvygWi3mIGbElpFm/fn1ZHVatWhWPPPJI7LzzzrFq1ao4/vjj8+WPPvpo/Ou//mscf/zxVT/vhx56KPbee+/44he/mL+2cuXK6N69e9l+fvKTn+RBVu/evWPgwIH5sh/96EdRU1MTn/3sZxuUf//998fgwYNj8ODBUVNTk1/0f+KJJ6J///5lIzjuueeeiNgSZJf2We6+++445ZRTolevXnHXXXflfZaIyPstZ555ZjzwwAPx9ttvR8SWtiUi8vZl9OjRzT4fj9hycenCCy+MvfbaK39t9uzZDdqXHUWjQU32427JbUfVApyIlt9636VLl6itra243S677BJTp07NT6aaOhErrUO1NLq59dt///3j0ksvbXQf1cprbP9NlceOZfz48S267a80pKmkpb+jrFNSabvsOC89WWqLjvqNt+fxt8cee8SVV17ZqnK0C2kZNGhQi469IUOGlJ1Q1dfS72rUqFFV9z969OiI2HJFqv4JVXM0dctxc7bPfqv1TxiqlVMa0pQqDVDqh8JtUa0e1cLf0sBXQLNtHXnkkXHkkUfG6tWr49VXX81PSp566qk4+uij4/TTT6+6bUe3iS09btrq/PPPb9aQh+z4vOiii6quO2rUqDykqVZOc9T/m9eW99natmnKlCmNzi+V3XHbkv1W09h5Uemyxi5oV9p/W89Ltoa99torpk6dml+8Hjt2bERsmUy4W7duFS9cZ5pbzx49esSIESPaXM6hhx4ahx56aLz44ouNbnvEEUdUHGFw1FFHxVFHHRVPP/10rFmzJs4444xm7beaL3/5y3HCCSe0qYyIiL/5m79p0THe2OiJb37zm3HYYYdVLaulv60RI0ZUrVt2x/dtt93W4CJTc7S1X/JJ1qqhT61RqZzS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//Zvy/5AHH744XHEEUdEoVCII444Ir7xjW+0qN6kafz48XlHJyJa9CSzUrW1tQ06OPUfnVs60Wf25KfsWM8a1kpPLCi9PbjaEw8am2y4UhuTldtSpR2e0teaunW4/jxYhUIhH/KUtS2Z3XbbreITn7L3Uklr25fs0dyl9erevXtcdNFFcfrpp5eV17Nnz/y295NOOikKhUIcfPDBccIJJ0ShUIjevXuX3S5P4wYNGhRLliyJpUuXRsSWSfla0ym64IILym4jjtgyl0TPnj3LnqBQLG65WnTHHXfEwIEDY9SoUXHbbbflc9Rccsklcf/995eVPWbMmLj55pvj/fffjzFjxsQ999wT69ati4jIHwXf1O3D2RObmvN47tI5akpf69KlS1xzzTUV25hKEwhXUtrWNNaONLecUs19IEJpP8VTn7a97PHWhUIhf+pTNccee2w8/vjjZd99//794+STT27xfk8++eT4+c9/XlbWqaeeGscee2yj2/3jP/5jfkKVPVnmrLPOiq9//evxox/9qGyOmmqP5z733HPj3nvvjb//+7+Pb33rW/HYY4/lT5Q75JBD4pBDDol77rknFi1aFBFbrnpnYVTpHDWZESNG5MMpm+uyyy7L25VM9njusWPHxo033ljx8dxXXnllzJ49u+LjuUvbmGz49tVXXx277rpr1Xo01m+pNGS7NfPZVHqyXKWnykVE3t+eOnVqk+dF9fs19Sc7bk4bk82rV1pu586d85EH7am0z5I99ama/fffPwYNGlRWzwMOOCAuueSSFu3z4IMPjhNPPLGsnPr9ltJln/nMZ+Jb3/pWWaCWOfzww2PQoEHx7LPPxhFHHBF9+/bNlx955JFx1lln5etmj70uFApxzDHHRPfu3ePBBx/Mlz/55JP5sMpf/epXZfv50pe+FH/9139d9rTepmRPaCsUCnHKKafE2rVro1AoxNe+9rX4whe+EBERK1asyPdz+umn5++xdI6azNlnn93iu2mGDBkSP/jBD8rmqckez71w4cIGT6q8++6749RTT604XLt0WFbElr5K1nfJ+i3ZRabs/KWpoZXjxo3LA+DMjvx47prGOjDXXnttu19W2rx5c8yYMaOs8dm0aVPMmjWrQxqk9pJ1vNur3PYqf3vW3p/NJ/Ezzx6hWzrsqVJ4Qzq0LR2vPT6bLKjp0aNH/tqdd96ZP6IbOpq2pePpt6Sp9OEHmZY8yYly26JtefbZZ+OFF17YYS9et6ZtWbhwYZxwwgllw54WLFgQp556aoMHVmhbtp5rr7226ge5zYOaiC3BzHXXXZf/u6NS4/akw9PxdHhap/TKVET1R+mSBm1Lx2uvz6b06lRExMUXXyykYZvRtnQ8/ZZ0VbvbhpYT1HS81rYt9e8CLp1QuFL5tF3yQc0nkQ5Px9PhYUegbel42hZ2BNqWjqdtYUegbel42pbtR2NBTbMmEwYAAACg/QlqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIhKAGAAAAIBGCGgAAAIBECGoAAAAAEiGoAQAAAEiEoAYAAAAgEYIaAAAAgEQIagAAAAASIagBAAAASISgBgAAACARghoAAACARAhqAAAAABIhqAEAAABIRE2xWNzWdQAAAAAg3FEDAAAAkAxBDQAAAEAiBDUAAAAAiRDUAAAAACRCUAMAAACQCEENAAAAQCL+P6dNZUhqv6c7AAAAAElFTkSuQmCC\n"},"metadata":{"needs_background":"light"}}]},{"cell_type":"markdown","source":["### Zero-shot classification"],"metadata":{"id":"5J7DEWgQk3y7"}},{"cell_type":"code","execution_count":25,"metadata":{"id":"iO85T44aEmMs","executionInfo":{"status":"ok","timestamp":1678890545725,"user_tz":-60,"elapsed":5,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[],"source":["# JIT-compile image embedding function to speed up the processing\n","@jax.jit\n","def embed_images(params, images):\n"," zimg, _, _ = model.apply({\"params\": params}, image=images)\n"," return zimg"]},{"cell_type":"code","source":["# Compute class embeddings\n","zclass = []\n","for i in range(0, len(class_prompts), 100):\n"," batch = class_prompts[i : i + 100]\n"," batch = np.stack(preprocess_texts(batch))\n"," zbatch = embed_images(params, batch)\n"," zclass.append(zbatch)\n","\n","zclass = np.concatenate(zclass)\n","zclass.shape\n"],"metadata":{"colab":{"base_uri":"https://localhost:8080/"},"id":"nM_1-0rj2ksS","executionInfo":{"status":"ok","timestamp":1678890755919,"user_tz":-60,"elapsed":210198,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}},"outputId":"03e58c30-614d-4aea-b360-14c0bb90e263"},"execution_count":26,"outputs":[{"output_type":"execute_result","data":{"text/plain":["(800, 768)"]},"metadata":{},"execution_count":26}]},{"cell_type":"code","execution_count":27,"metadata":{"id":"8Drfz7TACX2P","outputId":"b7d05c68-ebec-49d2-9ad4-bf8ebb4a3298","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1678890953378,"user_tz":-60,"elapsed":197470,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"stream","name":"stderr","text":["100%|██████████| 20/20 [03:17<00:00, 9.87s/it]\n"]},{"output_type":"execute_result","data":{"text/plain":["(10000, 768)"]},"metadata":{},"execution_count":27}],"source":["# Compute all image/sentence embeddings and collect the correct labels\n","zimgs = []\n","labels = []\n","\n","for batch in tqdm.tqdm(ds_test.batch(500)):\n"," labels += list(batch['label'].numpy())\n"," zimg = embed_images(params, batch['image'].numpy())\n"," zimgs.append(np.array(zimg))\n","zimgs = np.concatenate(zimgs)\n","zimgs.shape"]},{"cell_type":"code","execution_count":28,"metadata":{"id":"uU_8LyMcE7zp","outputId":"183dad76-b4dd-42d9-fe0a-5d286e1bc5b0","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1678890953379,"user_tz":-60,"elapsed":21,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"execute_result","data":{"text/plain":["(10000, 100)"]},"metadata":{},"execution_count":28}],"source":["# Compute similarities ...\n","sims = zimgs @ zclass.reshape([len(classnames), len(PROMPTS), -1]).mean(axis=1).T\n","sims.shape"]},{"cell_type":"code","execution_count":29,"metadata":{"id":"0mKZW4gNFl_6","outputId":"8eb4b896-dde1-4f72-8808-f219210f7006","colab":{"base_uri":"https://localhost:8080/"},"executionInfo":{"status":"ok","timestamp":1678890953379,"user_tz":-60,"elapsed":15,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"outputs":[{"output_type":"execute_result","data":{"text/plain":["0.5092"]},"metadata":{},"execution_count":29}],"source":["# ... and use most similar embedding to predict label.\n","(sims.argmax(axis=1) == np.array(labels)).mean()"]},{"cell_type":"code","source":["# CIFAR-100 expected results (7 prompts)\n","#\n","# clippo_b16_yfcc100m: 0.4535\n","# clippo_b16_yfcc100m_i21k_init: 0.5183\n","# clippo_b16_yfcc100m_i21k_init_25c4: 0.5092\n","# clippo_b16_yfcc100m_i21k_init_50c4: 0.4862\n","# clippo_b16_yfcc100m_i21k_init_50c4: 0.4204\n","#\n","# SST-2 expected results\n","#\n","# clippo_b16_yfcc100m_i21k_init_25c4: 0.6754\n","# clippo_b16_yfcc100m_i21k_init_75c4: 0.7006"],"metadata":{"id":"f-N90sgl6aKN","executionInfo":{"status":"ok","timestamp":1678889595136,"user_tz":-60,"elapsed":17,"user":{"displayName":"Michael Tschannen","userId":"02619997334944082183"}}},"execution_count":30,"outputs":[]}],"metadata":{"accelerator":"GPU","colab":{"provenance":[{"file_id":"https://github.com/google-research/vision_transformer/blob/main/lit.ipynb","timestamp":1678125470076}],"toc_visible":true},"kernelspec":{"display_name":"Python 3","name":"python3"},"language_info":{"name":"python"},"widgets":{"application/vnd.jupyter.widget-state+json":{"dccdc3dac09849d89bb4e33cd84f342e":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_5375b788c4434c70ab1174814f8f8c01","IPY_MODEL_d2ed5d9e693649dbbce268b311751c42","IPY_MODEL_3bb94f09d06b4f21a36d797603167e0e"],"layout":"IPY_MODEL_daecd79e3f4d4f8ea5bdfaddfa979047"}},"5375b788c4434c70ab1174814f8f8c01":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_8fdbc2c453284869844620f0b3d48118","placeholder":"​","style":"IPY_MODEL_0b6bae24732c4f4e8c6533057db1c320","value":"Dl Completed...: 100%"}},"d2ed5d9e693649dbbce268b311751c42":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_eeca15fa7845458aac9d0ac4564eb683","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_181c3157bb5c4d0682092d4362c71e38","value":1}},"3bb94f09d06b4f21a36d797603167e0e":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_0dae83dd44984685b6b547ea563f2ac5","placeholder":"​","style":"IPY_MODEL_499675f6f43449e1b457a943bb89f5e6","value":" 1/1 [01:42<00:00, 48.00s/ url]"}},"daecd79e3f4d4f8ea5bdfaddfa979047":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8fdbc2c453284869844620f0b3d48118":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"0b6bae24732c4f4e8c6533057db1c320":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"eeca15fa7845458aac9d0ac4564eb683":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"181c3157bb5c4d0682092d4362c71e38":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"0dae83dd44984685b6b547ea563f2ac5":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"499675f6f43449e1b457a943bb89f5e6":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"ad89503ecf0a4ce09e5d817b6aeca758":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_774eb9564b324fb398ec9f4257f67673","IPY_MODEL_1b3a0f324a4f477eade4332e2f40b115","IPY_MODEL_1986deffd06e4ed3bf882576f218ecb8"],"layout":"IPY_MODEL_93da5eeb980f416da18ed48ebd3d025d"}},"774eb9564b324fb398ec9f4257f67673":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_2a068826d3d94f9199c42bdc4381de7c","placeholder":"​","style":"IPY_MODEL_4a14f7780cae4c9ba6dd0b3588e713ea","value":"Dl Size...: 100%"}},"1b3a0f324a4f477eade4332e2f40b115":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_ecfea453250a49caadb5162d3b02ac40","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_9879ce5353e04f738660e92d78a3abcd","value":1}},"1986deffd06e4ed3bf882576f218ecb8":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_e3bc1b53a7f94ed3bd409c309ca7fa3a","placeholder":"​","style":"IPY_MODEL_f0122c02070d44e9ab72d6189e65cfb5","value":" 1485/1485 [01:42<00:00, 32.32 MiB/s]"}},"93da5eeb980f416da18ed48ebd3d025d":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2a068826d3d94f9199c42bdc4381de7c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4a14f7780cae4c9ba6dd0b3588e713ea":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"ecfea453250a49caadb5162d3b02ac40":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"9879ce5353e04f738660e92d78a3abcd":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"e3bc1b53a7f94ed3bd409c309ca7fa3a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"f0122c02070d44e9ab72d6189e65cfb5":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"1756f9ca57194e05a21e86be3390c160":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_353e41bf89064820b79b2d0c0ea7df31","IPY_MODEL_bb8c6693952a4dba95e0d16305b6bd3f","IPY_MODEL_7d00a1fa21354a51805a415db2410a7c"],"layout":"IPY_MODEL_5c4eafb7fe01476e8306e62dda1d46f6"}},"353e41bf89064820b79b2d0c0ea7df31":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_e53866d027f64bd4b299ecfdcf4f0a1c","placeholder":"​","style":"IPY_MODEL_7f7af62a6dd14868a0b35e520fec9d6a","value":"Extraction completed...: 100%"}},"bb8c6693952a4dba95e0d16305b6bd3f":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_e1e62a44c0b74e9781e93342838a744e","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_a84b52168d624ca1b442e989950b3b97","value":0}},"7d00a1fa21354a51805a415db2410a7c":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_c5497a566d504a6d926c4a74d24fea75","placeholder":"​","style":"IPY_MODEL_c7cf618be98a4ef4954e83ca3b49874d","value":" 13395/13395 [01:43<00:00, 664.73 file/s]"}},"5c4eafb7fe01476e8306e62dda1d46f6":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e53866d027f64bd4b299ecfdcf4f0a1c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7f7af62a6dd14868a0b35e520fec9d6a":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"e1e62a44c0b74e9781e93342838a744e":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"a84b52168d624ca1b442e989950b3b97":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"c5497a566d504a6d926c4a74d24fea75":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"c7cf618be98a4ef4954e83ca3b49874d":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"b157a99f67604979b1cd918b68fca6ac":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_6c00492230ed4b93af80dfc8e92b029b","IPY_MODEL_b86e74d1b6ff42129cffb98dec1f3f7b","IPY_MODEL_651f7d28f1a24bb3b7e48b3692a220e2"],"layout":"IPY_MODEL_3498b3fee9b54ee0889f21bb893c78be"}},"6c00492230ed4b93af80dfc8e92b029b":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_a781b7bb93a7451ba0c80d0f29986898","placeholder":"​","style":"IPY_MODEL_0d321d1d25fd40ef83074eba17b66080","value":"Generating splits...: 100%"}},"b86e74d1b6ff42129cffb98dec1f3f7b":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_4c0e7be5d0a94baaaadc5a6500dd9b6c","max":2,"min":0,"orientation":"horizontal","style":"IPY_MODEL_d34fe0909ab249058650c2cf8ceb35c4","value":2}},"651f7d28f1a24bb3b7e48b3692a220e2":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_7669118fa50947c5b9ae662fb6267f1d","placeholder":"​","style":"IPY_MODEL_6c6e27d70026499abaee7600b26fde29","value":" 2/2 [00:17<00:00, 7.75s/ splits]"}},"3498b3fee9b54ee0889f21bb893c78be":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"a781b7bb93a7451ba0c80d0f29986898":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"0d321d1d25fd40ef83074eba17b66080":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"4c0e7be5d0a94baaaadc5a6500dd9b6c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"d34fe0909ab249058650c2cf8ceb35c4":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"7669118fa50947c5b9ae662fb6267f1d":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"6c6e27d70026499abaee7600b26fde29":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"ff8597efa78344a09d83e4a6db42abfd":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_0725627ce3414d1c92b340d3d3f8f281","IPY_MODEL_de52e4ca1d6f4e0092d6c68274d3af8f","IPY_MODEL_78fa9b95d9c649d9be746399446eec77"],"layout":"IPY_MODEL_cdf7a866e43a4bdaa0c809b78741f757"}},"0725627ce3414d1c92b340d3d3f8f281":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_4a10c07fa0e1434984871acb73d5f46a","placeholder":"​","style":"IPY_MODEL_53893397fee74fd5b6a34dada619b6d1","value":"Generating train examples...: "}},"de52e4ca1d6f4e0092d6c68274d3af8f":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_3af6c8755e7c49f6aa01f45364a7e77e","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_7c4f5b790f1d4c00aa975141a8ede738","value":1}},"78fa9b95d9c649d9be746399446eec77":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_b5d6c41c9a9141dc94963faa2d4a676a","placeholder":"​","style":"IPY_MODEL_8e2d89124d264321ab04e8b7716c7d33","value":" 9408/? [00:10<00:00, 272.24 examples/s]"}},"cdf7a866e43a4bdaa0c809b78741f757":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"4a10c07fa0e1434984871acb73d5f46a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"53893397fee74fd5b6a34dada619b6d1":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"3af6c8755e7c49f6aa01f45364a7e77e":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"7c4f5b790f1d4c00aa975141a8ede738":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"b5d6c41c9a9141dc94963faa2d4a676a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8e2d89124d264321ab04e8b7716c7d33":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"5c711a79f5bc4b08b3dd2015e948adad":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_d78bc7be8f3847569d3134d5db2b5c8f","IPY_MODEL_55ca92dd00c74bff961c46f74aa95558","IPY_MODEL_c0cd6e7644ce4769b1cf9a15ee503d0d"],"layout":"IPY_MODEL_9b2109cef3404964b4b3e0e299369ca3"}},"d78bc7be8f3847569d3134d5db2b5c8f":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_c7a7c829ed2e40d6b9aeba7cd2792361","placeholder":"​","style":"IPY_MODEL_d103db0b223d4b6a927321aaee909f1c","value":"Shuffling /root/tensorflow_datasets/imagenette/full-size-v2/1.0.0.incompleteZXF06J/imagenette-train.tfrecord*...: 100%"}},"55ca92dd00c74bff961c46f74aa95558":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_df02c258cb1f47a48a5bda4dbd1cd01a","max":9469,"min":0,"orientation":"horizontal","style":"IPY_MODEL_1c2f0535a5494ae1b557dd8426fd69c1","value":9469}},"c0cd6e7644ce4769b1cf9a15ee503d0d":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_1ea41bb66d8c41639cf8d9273fa8c6d3","placeholder":"​","style":"IPY_MODEL_07a7298243ea44e5aa95adb338c2b5fa","value":" 9454/9469 [00:04<00:00, 2234.99 examples/s]"}},"9b2109cef3404964b4b3e0e299369ca3":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"c7a7c829ed2e40d6b9aeba7cd2792361":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"d103db0b223d4b6a927321aaee909f1c":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"df02c258cb1f47a48a5bda4dbd1cd01a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"1c2f0535a5494ae1b557dd8426fd69c1":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"1ea41bb66d8c41639cf8d9273fa8c6d3":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"07a7298243ea44e5aa95adb338c2b5fa":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"4e71c9ec77e04e0fb85f83f739f48949":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_0d2f9fb372734a2cb58c1027afd2ab64","IPY_MODEL_8ffb631b45d7432193da5c756028a061","IPY_MODEL_9e1179561ab6434eabea9eb1238dae43"],"layout":"IPY_MODEL_d41263cf6e4845c29612dc1f8a113a64"}},"0d2f9fb372734a2cb58c1027afd2ab64":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_e0cb8d6dfb7349d7b7cfe1698140b29b","placeholder":"​","style":"IPY_MODEL_5e4a96dea2154789be89dc50523167d1","value":"Generating validation examples...: "}},"8ffb631b45d7432193da5c756028a061":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_fa2922365ee043908b03abf0cc8b3d46","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_6c7f68bc82da44c89df1fe69883af09b","value":1}},"9e1179561ab6434eabea9eb1238dae43":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_c9c3213ba5184b9ca4f46601d372a291","placeholder":"​","style":"IPY_MODEL_825b35a2d7cf4606ad7056d60f59e907","value":" 3925/? [00:01<00:00, 2737.37 examples/s]"}},"d41263cf6e4845c29612dc1f8a113a64":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"e0cb8d6dfb7349d7b7cfe1698140b29b":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5e4a96dea2154789be89dc50523167d1":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"fa2922365ee043908b03abf0cc8b3d46":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"6c7f68bc82da44c89df1fe69883af09b":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"c9c3213ba5184b9ca4f46601d372a291":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"825b35a2d7cf4606ad7056d60f59e907":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"4464bdf63c684c748e530de69bdb8da3":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_b4a8f48c427642b7a5e574794c67c450","IPY_MODEL_353d24473b444907b18c2d8eb1853d1b","IPY_MODEL_785f446240fb476a8aa5cb1b8fb38573"],"layout":"IPY_MODEL_8a813d3ace9e4c9f8defa24c29ee96ac"}},"b4a8f48c427642b7a5e574794c67c450":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_cf0747e7cf8a4fb6af9a8a2d2ff4885f","placeholder":"​","style":"IPY_MODEL_4cf0aab25b1c43338c8761e1ff492631","value":"Shuffling /root/tensorflow_datasets/imagenette/full-size-v2/1.0.0.incompleteZXF06J/imagenette-validation.tfrecord*...: 97%"}},"353d24473b444907b18c2d8eb1853d1b":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_3993f8faf3794a9dae2c55e13b8938e4","max":3925,"min":0,"orientation":"horizontal","style":"IPY_MODEL_33a70dcd558646f09e73060acdb80a98","value":3925}},"785f446240fb476a8aa5cb1b8fb38573":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_af99dc606a7c4da0931365780439f632","placeholder":"​","style":"IPY_MODEL_e4047f4ac6824cacb4a0f2f1c5da6b48","value":" 3825/3925 [00:01<00:00, 2164.36 examples/s]"}},"8a813d3ace9e4c9f8defa24c29ee96ac":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"cf0747e7cf8a4fb6af9a8a2d2ff4885f":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4cf0aab25b1c43338c8761e1ff492631":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"3993f8faf3794a9dae2c55e13b8938e4":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"33a70dcd558646f09e73060acdb80a98":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"af99dc606a7c4da0931365780439f632":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e4047f4ac6824cacb4a0f2f1c5da6b48":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"664319014bea49ddb0682ca183f37d38":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_7b80afb39bd34ffbb6ec71999f043a0d","IPY_MODEL_7f8cfe19a0774252b9a2e0acf8712b95","IPY_MODEL_24291661ee6a4e1ea39fe36a2461ad03"],"layout":"IPY_MODEL_456418b864a04211a89f7ded9893182a"}},"7b80afb39bd34ffbb6ec71999f043a0d":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_4e035e47a511466aaee1f410141dc219","placeholder":"​","style":"IPY_MODEL_1fe28e969c15490b8d5deddf404bbfc8","value":"Dl Completed...: 100%"}},"7f8cfe19a0774252b9a2e0acf8712b95":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_9b493b2b10d749af94288e1da31082de","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_7910356c0b024c48995d1215cfc40d92","value":1}},"24291661ee6a4e1ea39fe36a2461ad03":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_57bca4c7a4e048118328e261f2816485","placeholder":"​","style":"IPY_MODEL_1981f2194ce14b9d858b4e66ad749a74","value":" 1/1 [00:08<00:00, 6.55s/ url]"}},"456418b864a04211a89f7ded9893182a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4e035e47a511466aaee1f410141dc219":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"1fe28e969c15490b8d5deddf404bbfc8":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"9b493b2b10d749af94288e1da31082de":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"7910356c0b024c48995d1215cfc40d92":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"57bca4c7a4e048118328e261f2816485":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"1981f2194ce14b9d858b4e66ad749a74":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"7bf24c0b526849148f79ddbe489fecbe":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_d96f75be285b4ece84a79b80525e07ff","IPY_MODEL_eae9d0ad46014301a4104063786c6513","IPY_MODEL_33daed476d1e48aea2c175d7d165007c"],"layout":"IPY_MODEL_25ac8ad9428446638d2e831154b67e9e"}},"d96f75be285b4ece84a79b80525e07ff":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_096d4f03e30642ecb75b856d32192b77","placeholder":"​","style":"IPY_MODEL_ff4aa1191d5546e29cfc0b2099deaf32","value":"Dl Size...: 100%"}},"eae9d0ad46014301a4104063786c6513":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_5bd7bfbeae434bd0ad6d1a004866e8bf","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_49a2dd8bfc2440dc87edd2a379b3b9df","value":1}},"33daed476d1e48aea2c175d7d165007c":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_117b9b987ba646239156062a37e44881","placeholder":"​","style":"IPY_MODEL_7bcffca94bd349ce8e36b41eed824e9f","value":" 160/160 [00:08<00:00, 27.88 MiB/s]"}},"25ac8ad9428446638d2e831154b67e9e":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"096d4f03e30642ecb75b856d32192b77":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"ff4aa1191d5546e29cfc0b2099deaf32":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"5bd7bfbeae434bd0ad6d1a004866e8bf":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"49a2dd8bfc2440dc87edd2a379b3b9df":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"117b9b987ba646239156062a37e44881":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7bcffca94bd349ce8e36b41eed824e9f":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"14202fa017664237abee369e0b787b2a":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e77432e0ad7a48cda8a84a59c7c4fcd9","IPY_MODEL_81cb50b67148443a97315c6004b9bd9e","IPY_MODEL_072d46906efe4a07ac2a04bf532ca56e"],"layout":"IPY_MODEL_d4a3cb590ed2442682da8cc156ed9ad0"}},"e77432e0ad7a48cda8a84a59c7c4fcd9":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_df162d04da2a41209af80c51808caf2b","placeholder":"​","style":"IPY_MODEL_c1ed3a33cca94d0f8e91ea8cafbeffca","value":"Extraction completed...: 100%"}},"81cb50b67148443a97315c6004b9bd9e":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_621ed7453b7a48dfa3fb5d5c0da0bec3","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_8c3a583468f54969a8203d83a3b511ad","value":1}},"072d46906efe4a07ac2a04bf532ca56e":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_259cda89a60d4ae28a23d222bf17a4f4","placeholder":"​","style":"IPY_MODEL_5d02bdf68b19451dad6399a596c700f9","value":" 4/4 [00:08<00:00, 8.23s/ file]"}},"d4a3cb590ed2442682da8cc156ed9ad0":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"df162d04da2a41209af80c51808caf2b":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"c1ed3a33cca94d0f8e91ea8cafbeffca":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"621ed7453b7a48dfa3fb5d5c0da0bec3":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"8c3a583468f54969a8203d83a3b511ad":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"259cda89a60d4ae28a23d222bf17a4f4":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5d02bdf68b19451dad6399a596c700f9":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"31d1942f4b104c048fe34b06fd5612e4":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e22728de4ffb4c2ca07c8a6ac6da10de","IPY_MODEL_282d4ebc49794e869d299726b52bcf21","IPY_MODEL_f74afaef34b14da7a6e5f0c2f569f68d"],"layout":"IPY_MODEL_70dfcbb20077452a95f7b585fd182be6"}},"e22728de4ffb4c2ca07c8a6ac6da10de":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_99cdd80186aa49b891f7a5d4de962f18","placeholder":"​","style":"IPY_MODEL_5f8b6e4a048a4fd5b944e4d7853b9a71","value":"Generating splits...: 100%"}},"282d4ebc49794e869d299726b52bcf21":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_1418a4fa26a64e6bbdcca38fb682e39d","max":2,"min":0,"orientation":"horizontal","style":"IPY_MODEL_c1e9314ff7904b8dbc50a8708aab1eb1","value":2}},"f74afaef34b14da7a6e5f0c2f569f68d":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_0bf192b5c7034c2191c5f92c7df1c9dd","placeholder":"​","style":"IPY_MODEL_2f71d1430e9c45799e8949ec0158a222","value":" 2/2 [01:03<00:00, 28.13s/ splits]"}},"70dfcbb20077452a95f7b585fd182be6":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"99cdd80186aa49b891f7a5d4de962f18":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5f8b6e4a048a4fd5b944e4d7853b9a71":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"1418a4fa26a64e6bbdcca38fb682e39d":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"c1e9314ff7904b8dbc50a8708aab1eb1":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"0bf192b5c7034c2191c5f92c7df1c9dd":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2f71d1430e9c45799e8949ec0158a222":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"07689a70f74446bcba36184e47560bd1":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_65f301b7b02d4c4dba0f233f9e9e425a","IPY_MODEL_e4412596145f4c01af01d51402ed45d6","IPY_MODEL_179510514bfd4e3ea9c46889b13da181"],"layout":"IPY_MODEL_3021ffddf75e42759b0749f82cb17eb4"}},"65f301b7b02d4c4dba0f233f9e9e425a":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_5c101b460ebd4a27a01a2ada97f777a7","placeholder":"​","style":"IPY_MODEL_807aa95840eb41b4859a300500d8beec","value":"Generating train examples...: "}},"e4412596145f4c01af01d51402ed45d6":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_46d2eb5e2620481f9495b472a8c84775","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_d7cbb87d3c9245a8b339ecbcea83e8c2","value":1}},"179510514bfd4e3ea9c46889b13da181":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_6d3ac9db950848f3baae1fa93b8e68ea","placeholder":"​","style":"IPY_MODEL_dee93ec00db64a9d8f3f1edd65a90ac8","value":" 49940/? [00:52<00:00, 1017.08 examples/s]"}},"3021ffddf75e42759b0749f82cb17eb4":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"5c101b460ebd4a27a01a2ada97f777a7":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"807aa95840eb41b4859a300500d8beec":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"46d2eb5e2620481f9495b472a8c84775":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"d7cbb87d3c9245a8b339ecbcea83e8c2":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"6d3ac9db950848f3baae1fa93b8e68ea":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"dee93ec00db64a9d8f3f1edd65a90ac8":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"4dc9469114be4125b4d344d644f5aff9":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_274ee0dd728f4a33be912fcd1786f058","IPY_MODEL_5708d1ee150f46029173593d2ec4c21f","IPY_MODEL_33749fbfb6d9403f962568ba42c1e8ed"],"layout":"IPY_MODEL_a586b099410a4223b0c62b21e94b6c10"}},"274ee0dd728f4a33be912fcd1786f058":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_22b60f7e0f234a7e83c01a0b7f5ad9b1","placeholder":"​","style":"IPY_MODEL_a39974f014e24fc5ac43e72b34342e9d","value":"Shuffling /root/tensorflow_datasets/cifar100/3.0.2.incompleteW3MSAW/cifar100-train.tfrecord*...: 80%"}},"5708d1ee150f46029173593d2ec4c21f":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_f10a575c66664f618e179e4df8dc6fa3","max":50000,"min":0,"orientation":"horizontal","style":"IPY_MODEL_5c3235bc34964d51b54fa096fc92c1f9","value":50000}},"33749fbfb6d9403f962568ba42c1e8ed":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_f43832732153414a951c1810c899c531","placeholder":"​","style":"IPY_MODEL_84bce2199a5e4d1c8526b1a6d1059cf5","value":" 40132/50000 [00:00<00:00, 140860.69 examples/s]"}},"a586b099410a4223b0c62b21e94b6c10":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"22b60f7e0f234a7e83c01a0b7f5ad9b1":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"a39974f014e24fc5ac43e72b34342e9d":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"f10a575c66664f618e179e4df8dc6fa3":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5c3235bc34964d51b54fa096fc92c1f9":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"f43832732153414a951c1810c899c531":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"84bce2199a5e4d1c8526b1a6d1059cf5":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"40ad9dad27c747f5afc95f69b5bf42fa":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_3978ee7fa0f444bebf6da70d16c26095","IPY_MODEL_f01d3d3fc2c84fb2b2bd14560cdd7fa3","IPY_MODEL_8f28e75e20b24966b582137d4dfa31ee"],"layout":"IPY_MODEL_4aaf504fe9fc4d618d4174ae9844b347"}},"3978ee7fa0f444bebf6da70d16c26095":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_44a3a4df887a4bebbabd04033546d7c9","placeholder":"​","style":"IPY_MODEL_7fd53f280d914a8395c6c1c3cdc677ce","value":"Generating test examples...: "}},"f01d3d3fc2c84fb2b2bd14560cdd7fa3":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_b83e550fff71444480e85f9155edb0d0","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_3742d1e304584823a975a123bfe2ccb4","value":1}},"8f28e75e20b24966b582137d4dfa31ee":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_135831c870fd49aeaf21294f8256f1f9","placeholder":"​","style":"IPY_MODEL_2d8ab80c4b5742a2b8ac5154d58d52d3","value":" 9955/? [00:10<00:00, 837.05 examples/s]"}},"4aaf504fe9fc4d618d4174ae9844b347":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"44a3a4df887a4bebbabd04033546d7c9":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7fd53f280d914a8395c6c1c3cdc677ce":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"b83e550fff71444480e85f9155edb0d0":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"3742d1e304584823a975a123bfe2ccb4":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"135831c870fd49aeaf21294f8256f1f9":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2d8ab80c4b5742a2b8ac5154d58d52d3":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"4be15f66f88c4401a769ce297c325705":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e09cae9d4eb44f84892d1e4f705b13f8","IPY_MODEL_ad869a7a2c3948f6b2485510529df75b","IPY_MODEL_8e343e5259294118806a3144ef125d44"],"layout":"IPY_MODEL_07ed03f79ec643028f7ed7c741d284a9"}},"e09cae9d4eb44f84892d1e4f705b13f8":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_bb2436bfe4b047979ad69dbf58b12836","placeholder":"​","style":"IPY_MODEL_9e683e88a8e042889c004fd9e34e5ba9","value":"Shuffling /root/tensorflow_datasets/cifar100/3.0.2.incompleteW3MSAW/cifar100-test.tfrecord*...: 75%"}},"ad869a7a2c3948f6b2485510529df75b":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_d247a7b15d814991933dcf6705f5904b","max":10000,"min":0,"orientation":"horizontal","style":"IPY_MODEL_b8130ff7e9f64911b7f11dd947d9eea4","value":10000}},"8e343e5259294118806a3144ef125d44":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_cca976a0f62f41c9a6161e60be7f757c","placeholder":"​","style":"IPY_MODEL_47fb22e77cd44a75b752f4b8ee16c026","value":" 7540/10000 [00:00<00:00, 75389.29 examples/s]"}},"07ed03f79ec643028f7ed7c741d284a9":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"bb2436bfe4b047979ad69dbf58b12836":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9e683e88a8e042889c004fd9e34e5ba9":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"d247a7b15d814991933dcf6705f5904b":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"b8130ff7e9f64911b7f11dd947d9eea4":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"cca976a0f62f41c9a6161e60be7f757c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"47fb22e77cd44a75b752f4b8ee16c026":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"bb1c1dafddfc4ab1bf0ea96916d088dc":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_412d72f0cab84cdc9ccb202015b8f09b","IPY_MODEL_321f978c7d154fa9a793612bd45e34fb","IPY_MODEL_c98c2b0e046e4d11bdb41466c8b9bbed"],"layout":"IPY_MODEL_3d795eb785364e04b3458db07a689231"}},"412d72f0cab84cdc9ccb202015b8f09b":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_47defbe6eded463aa913f6c94eba650c","placeholder":"​","style":"IPY_MODEL_90a7c3c7f67c4b9dabae3124d4f4807a","value":"Dl Completed...: 100%"}},"321f978c7d154fa9a793612bd45e34fb":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_f9c8321259e64b5da15a5780e48387cc","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_7d9c02eab6b14e64b0d54535a51d3f26","value":1}},"c98c2b0e046e4d11bdb41466c8b9bbed":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_7261da104bf4488489651c59b57c810e","placeholder":"​","style":"IPY_MODEL_292cacad99804debab40eaff1f7241f0","value":" 1/1 [00:02<00:00, 2.14s/ url]"}},"3d795eb785364e04b3458db07a689231":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"47defbe6eded463aa913f6c94eba650c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"90a7c3c7f67c4b9dabae3124d4f4807a":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"f9c8321259e64b5da15a5780e48387cc":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"7d9c02eab6b14e64b0d54535a51d3f26":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"7261da104bf4488489651c59b57c810e":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"292cacad99804debab40eaff1f7241f0":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"16c4f6ca9f104713aaed23017b953711":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_c9382c9cc43144ccbc94ff9c126c75cb","IPY_MODEL_5d35963be08f4f5e855c86ad7360b061","IPY_MODEL_9ba65fbf4ff54df8832d0790b326bd74"],"layout":"IPY_MODEL_26e7683fe07e46f2b6e42ab2cf525d1c"}},"c9382c9cc43144ccbc94ff9c126c75cb":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_2e2f1aca25d64fc6a5eb6c5984ea1884","placeholder":"​","style":"IPY_MODEL_4b045a43035c4f4aa8f72acfc44361a0","value":"Dl Size...: 100%"}},"5d35963be08f4f5e855c86ad7360b061":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_999d70d6ea1c43f0a1b7005999dc2d9d","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_4e45f77fa3fe477eaca52d987b4d0217","value":1}},"9ba65fbf4ff54df8832d0790b326bd74":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_86979c617e50477db737303ad9042a7c","placeholder":"​","style":"IPY_MODEL_23c0c48a8eb04a6aa12a39e102a71322","value":" 7/7 [00:02<00:00, 3.97 MiB/s]"}},"26e7683fe07e46f2b6e42ab2cf525d1c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2e2f1aca25d64fc6a5eb6c5984ea1884":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"4b045a43035c4f4aa8f72acfc44361a0":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"999d70d6ea1c43f0a1b7005999dc2d9d":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"4e45f77fa3fe477eaca52d987b4d0217":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"86979c617e50477db737303ad9042a7c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"23c0c48a8eb04a6aa12a39e102a71322":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"60999d5a3045449b87f83734c86adcce":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_7e297ca7eaf64ce1b3bdf8685acbf74f","IPY_MODEL_e9b3045f23b24882b847fcf7c821f3bd","IPY_MODEL_a6322a45f87d43dd8cb4edc4f366742e"],"layout":"IPY_MODEL_56a2a0d2d23243cf8f5c6424c6a704bd"}},"7e297ca7eaf64ce1b3bdf8685acbf74f":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_bb36af1417914c8aa0728cf0294be51c","placeholder":"​","style":"IPY_MODEL_9cd358bace4a46288e5a11e03256eee1","value":"Extraction completed...: 100%"}},"e9b3045f23b24882b847fcf7c821f3bd":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"success","description":"","description_tooltip":null,"layout":"IPY_MODEL_71ea73a230af420c8c49d9cfaa5ab007","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_1199e807dbea492486a7faf9aeb2e8d2","value":1}},"a6322a45f87d43dd8cb4edc4f366742e":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_b0d8ca926f9e469ebaaf8216ad9ede8c","placeholder":"​","style":"IPY_MODEL_c2867984ecb1465f95a65d126c939991","value":" 11/11 [00:02<00:00, 2.25s/ file]"}},"56a2a0d2d23243cf8f5c6424c6a704bd":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"bb36af1417914c8aa0728cf0294be51c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9cd358bace4a46288e5a11e03256eee1":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"71ea73a230af420c8c49d9cfaa5ab007":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"1199e807dbea492486a7faf9aeb2e8d2":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"b0d8ca926f9e469ebaaf8216ad9ede8c":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"c2867984ecb1465f95a65d126c939991":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"523eaf4ccda54016b3fbbc7bcba69d09":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e4a736a0c2134a51bf605daa8882db31","IPY_MODEL_f288cf7496284b1bb1ee1b8c5dbf9afe","IPY_MODEL_ed84f6bbac894e62ad0599dae35a9b8d"],"layout":"IPY_MODEL_0ca6bb008cdf457fa868d567e4c5cbc9"}},"e4a736a0c2134a51bf605daa8882db31":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_652284bf59e846cd980fef70006cbbd7","placeholder":"​","style":"IPY_MODEL_5842f56fc48440169916b714284e80fe","value":"Generating splits...: 100%"}},"f288cf7496284b1bb1ee1b8c5dbf9afe":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_aa19d3fac97b48678fbf88350a462765","max":3,"min":0,"orientation":"horizontal","style":"IPY_MODEL_0afb2cb83138438daa94145fafa706cc","value":3}},"ed84f6bbac894e62ad0599dae35a9b8d":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_af89e62625854f85bce7d651bfc4acfe","placeholder":"​","style":"IPY_MODEL_7ab055add3f54e939b70b9937ed100cd","value":" 3/3 [00:07<00:00, 1.85s/ splits]"}},"0ca6bb008cdf457fa868d567e4c5cbc9":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"652284bf59e846cd980fef70006cbbd7":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"5842f56fc48440169916b714284e80fe":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"aa19d3fac97b48678fbf88350a462765":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"0afb2cb83138438daa94145fafa706cc":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"af89e62625854f85bce7d651bfc4acfe":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"7ab055add3f54e939b70b9937ed100cd":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"959675b37c284184a97ddfe7fed30088":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_f82ad804566d4c018941d9efe57aa753","IPY_MODEL_6e5a097140b14e0b9d18bf49ad747ea1","IPY_MODEL_29f7931b409b48d7b23ca62965c11447"],"layout":"IPY_MODEL_a00b6e479fed421eb64cc26089d127ec"}},"f82ad804566d4c018941d9efe57aa753":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_e6b51524f5f34035a302c1e25c905f6f","placeholder":"​","style":"IPY_MODEL_2b7e38d56e6c406d85d4c2f8dbd14381","value":"Generating train examples...: "}},"6e5a097140b14e0b9d18bf49ad747ea1":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_9da221b94d634814a2b2f5b2598adfbf","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_fe302d219a9d48f386835333902f745c","value":1}},"29f7931b409b48d7b23ca62965c11447":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_7a64baf19db946708ac3cfe9bba0ecff","placeholder":"​","style":"IPY_MODEL_9f6f17c6ffb44fa09431423d27740cdf","value":" 67299/? [00:07<00:00, 10076.71 examples/s]"}},"a00b6e479fed421eb64cc26089d127ec":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"e6b51524f5f34035a302c1e25c905f6f":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2b7e38d56e6c406d85d4c2f8dbd14381":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"9da221b94d634814a2b2f5b2598adfbf":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"fe302d219a9d48f386835333902f745c":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"7a64baf19db946708ac3cfe9bba0ecff":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"9f6f17c6ffb44fa09431423d27740cdf":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"e5ad5de128d4472dbe7c8c722685dd63":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_6baf1d03147a4d509ffeca5b46bfaef8","IPY_MODEL_bd1b3ebfa0f040b6a3409f83f0689612","IPY_MODEL_a14de20b05b74107912737505564a9b6"],"layout":"IPY_MODEL_ea8f4fe35c91417ea8b9f5411ff3609e"}},"6baf1d03147a4d509ffeca5b46bfaef8":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_9c09190e2fdd4f97affc2234e86c5bee","placeholder":"​","style":"IPY_MODEL_864e3bd5051a47a8901a28f7aaab9ccd","value":"Shuffling /root/tensorflow_datasets/glue/sst2/2.0.0.incompleteEYA068/glue-train.tfrecord*...: 87%"}},"bd1b3ebfa0f040b6a3409f83f0689612":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_1afb48bfcdc64803b2e63dac7380ffcc","max":67349,"min":0,"orientation":"horizontal","style":"IPY_MODEL_a437ae7a7d67409eb066f470b01896e8","value":67349}},"a14de20b05b74107912737505564a9b6":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_246027ed95ea40a9a037bb1218a5bab6","placeholder":"​","style":"IPY_MODEL_e02e735df1f24875ae30e06c37434a96","value":" 58529/67349 [00:00<00:00, 318506.08 examples/s]"}},"ea8f4fe35c91417ea8b9f5411ff3609e":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"9c09190e2fdd4f97affc2234e86c5bee":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"864e3bd5051a47a8901a28f7aaab9ccd":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"1afb48bfcdc64803b2e63dac7380ffcc":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"a437ae7a7d67409eb066f470b01896e8":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"246027ed95ea40a9a037bb1218a5bab6":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"e02e735df1f24875ae30e06c37434a96":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"50f38b8c1de741eb895e3de8d10a6e97":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_0727fde380a54a26b84117fb58b34814","IPY_MODEL_b3806705ac714b7caa823d184d09dd0a","IPY_MODEL_df6c3e7278a0402cbdcf56861181d1a0"],"layout":"IPY_MODEL_dac1bd568a134f1ab6752ccea84d2cd7"}},"0727fde380a54a26b84117fb58b34814":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_9b4029630887481f895100433ea29ca5","placeholder":"​","style":"IPY_MODEL_48e4c9c0a9ad45f488833b9533db4a67","value":"Generating validation examples...: "}},"b3806705ac714b7caa823d184d09dd0a":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_225d0c8c8ab54188bb8823f2618a7abb","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_79bde2b3bba444ff83109656e67760d1","value":1}},"df6c3e7278a0402cbdcf56861181d1a0":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_ac5c7cce9d7641d89ac2921a6ced5480","placeholder":"​","style":"IPY_MODEL_463efc760f3c424c944084d4853ccb81","value":" 348/? [00:00<00:00, 3478.61 examples/s]"}},"dac1bd568a134f1ab6752ccea84d2cd7":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"9b4029630887481f895100433ea29ca5":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"48e4c9c0a9ad45f488833b9533db4a67":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"225d0c8c8ab54188bb8823f2618a7abb":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"79bde2b3bba444ff83109656e67760d1":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"ac5c7cce9d7641d89ac2921a6ced5480":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"463efc760f3c424c944084d4853ccb81":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"de8dc6464f554376834078c8fbeb4e9b":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_2c301232781c44c5a9883bb0e3d6d5a8","IPY_MODEL_3e2396bc62d74a5ba3dd9a11731de392","IPY_MODEL_4062069fd10849e48aa2f42f0ccad406"],"layout":"IPY_MODEL_d058e12b7c9a47b6957916f0a5735a51"}},"2c301232781c44c5a9883bb0e3d6d5a8":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_a50bd51c0e774c63a9a0c23de522c9e7","placeholder":"​","style":"IPY_MODEL_13a1ce102a2045b69e4565e7a63449ae","value":"Shuffling /root/tensorflow_datasets/glue/sst2/2.0.0.incompleteEYA068/glue-validation.tfrecord*...: 0%"}},"3e2396bc62d74a5ba3dd9a11731de392":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_5176c6d1c19c434aa88256aa83bccd57","max":872,"min":0,"orientation":"horizontal","style":"IPY_MODEL_274282e991c142e2a4f62410e9fd29d3","value":872}},"4062069fd10849e48aa2f42f0ccad406":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_d48e121c27884d7eadfda819c78e9617","placeholder":"​","style":"IPY_MODEL_56546bf856364f65b289cdf7ef5895fa","value":" 0/872 [00:00<?, ? examples/s]"}},"d058e12b7c9a47b6957916f0a5735a51":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"a50bd51c0e774c63a9a0c23de522c9e7":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"13a1ce102a2045b69e4565e7a63449ae":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"5176c6d1c19c434aa88256aa83bccd57":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"274282e991c142e2a4f62410e9fd29d3":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"d48e121c27884d7eadfda819c78e9617":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"56546bf856364f65b289cdf7ef5895fa":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"98133af35daf47b38b785da163078430":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_e99cda177fcf455281a2a2b606d0d8ef","IPY_MODEL_87591ac7a7e247c88cd2149b259802b9","IPY_MODEL_9dba966e37cd44398d55e7d4e191f1f0"],"layout":"IPY_MODEL_ae3d380509cf4aba8e3a7dbe3b54000a"}},"e99cda177fcf455281a2a2b606d0d8ef":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_691c71d141d34e79ab4becc4f26252df","placeholder":"​","style":"IPY_MODEL_ab344b76b2e84abb8bebacd105941428","value":"Generating test examples...: "}},"87591ac7a7e247c88cd2149b259802b9":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"info","description":"","description_tooltip":null,"layout":"IPY_MODEL_993f4775c78243ca8241ce3de9111b7f","max":1,"min":0,"orientation":"horizontal","style":"IPY_MODEL_4dbba02a274a488e9e4874752a0ded05","value":1}},"9dba966e37cd44398d55e7d4e191f1f0":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_6552a8e5158644e099d74dd9e1f561df","placeholder":"​","style":"IPY_MODEL_2e8ff466b6b94290aef03bb2a39389c7","value":" 1220/? [00:00<00:00, 6451.57 examples/s]"}},"ae3d380509cf4aba8e3a7dbe3b54000a":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"691c71d141d34e79ab4becc4f26252df":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"ab344b76b2e84abb8bebacd105941428":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"993f4775c78243ca8241ce3de9111b7f":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":"20px"}},"4dbba02a274a488e9e4874752a0ded05":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"6552a8e5158644e099d74dd9e1f561df":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"2e8ff466b6b94290aef03bb2a39389c7":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"009034a493bc4bc7b3658f1381bce4cc":{"model_module":"@jupyter-widgets/controls","model_name":"HBoxModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HBoxModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HBoxView","box_style":"","children":["IPY_MODEL_9d9eef4bb31f4183aa14346b7aa5cb20","IPY_MODEL_6fadc7132d0d42ef9c3fc83dc8ee1564","IPY_MODEL_a0cb3869861e41f88d8ae0b7e9627122"],"layout":"IPY_MODEL_1776a1f9c103417bab4873d44ab31202"}},"9d9eef4bb31f4183aa14346b7aa5cb20":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_fff0dce5a48745978722580454349072","placeholder":"​","style":"IPY_MODEL_891a5a8d28f5434bada9cdaea176aea7","value":"Shuffling /root/tensorflow_datasets/glue/sst2/2.0.0.incompleteEYA068/glue-test.tfrecord*...: 0%"}},"6fadc7132d0d42ef9c3fc83dc8ee1564":{"model_module":"@jupyter-widgets/controls","model_name":"FloatProgressModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"FloatProgressModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"ProgressView","bar_style":"","description":"","description_tooltip":null,"layout":"IPY_MODEL_07b3506aa2e444379a0e41486d8154c4","max":1821,"min":0,"orientation":"horizontal","style":"IPY_MODEL_fcbfa3898e0a4d01962c7502974db9d5","value":1821}},"a0cb3869861e41f88d8ae0b7e9627122":{"model_module":"@jupyter-widgets/controls","model_name":"HTMLModel","model_module_version":"1.5.0","state":{"_dom_classes":[],"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"HTMLModel","_view_count":null,"_view_module":"@jupyter-widgets/controls","_view_module_version":"1.5.0","_view_name":"HTMLView","description":"","description_tooltip":null,"layout":"IPY_MODEL_4303aa6e9a424f46b1291aac04db28f4","placeholder":"​","style":"IPY_MODEL_8635d996805741ce87e22fac012a9501","value":" 0/1821 [00:00<?, ? examples/s]"}},"1776a1f9c103417bab4873d44ab31202":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":"hidden","width":null}},"fff0dce5a48745978722580454349072":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"891a5a8d28f5434bada9cdaea176aea7":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}},"07b3506aa2e444379a0e41486d8154c4":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"fcbfa3898e0a4d01962c7502974db9d5":{"model_module":"@jupyter-widgets/controls","model_name":"ProgressStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"ProgressStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","bar_color":null,"description_width":""}},"4303aa6e9a424f46b1291aac04db28f4":{"model_module":"@jupyter-widgets/base","model_name":"LayoutModel","model_module_version":"1.2.0","state":{"_model_module":"@jupyter-widgets/base","_model_module_version":"1.2.0","_model_name":"LayoutModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"LayoutView","align_content":null,"align_items":null,"align_self":null,"border":null,"bottom":null,"display":null,"flex":null,"flex_flow":null,"grid_area":null,"grid_auto_columns":null,"grid_auto_flow":null,"grid_auto_rows":null,"grid_column":null,"grid_gap":null,"grid_row":null,"grid_template_areas":null,"grid_template_columns":null,"grid_template_rows":null,"height":null,"justify_content":null,"justify_items":null,"left":null,"margin":null,"max_height":null,"max_width":null,"min_height":null,"min_width":null,"object_fit":null,"object_position":null,"order":null,"overflow":null,"overflow_x":null,"overflow_y":null,"padding":null,"right":null,"top":null,"visibility":null,"width":null}},"8635d996805741ce87e22fac012a9501":{"model_module":"@jupyter-widgets/controls","model_name":"DescriptionStyleModel","model_module_version":"1.5.0","state":{"_model_module":"@jupyter-widgets/controls","_model_module_version":"1.5.0","_model_name":"DescriptionStyleModel","_view_count":null,"_view_module":"@jupyter-widgets/base","_view_module_version":"1.2.0","_view_name":"StyleView","description_width":""}}}}},"nbformat":4,"nbformat_minor":0} \ No newline at end of file diff --git a/big_vision/configs/proj/clippo/train_clippo.py b/big_vision/configs/proj/clippo/train_clippo.py new file mode 100644 index 0000000000000000000000000000000000000000..8fc683b985d096a010744bb565c1d3b47d03c96e --- /dev/null +++ b/big_vision/configs/proj/clippo/train_clippo.py @@ -0,0 +1,199 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""Trains CLIP with Pixels Only (CLIPPO), https://arxiv.org/abs/2212.08045 + +IMPORTANT NOTE: This config uses coco_captions by default for demonstration +purposes since the TFDS catalog does not provide any large image/alt-text data +set; the training will not produce a model with useful accuracy. Please +replace the data set below (marked by a comment) with an appropriate image/ +alt-text data set wrapped in TFDS (for example LAION-400M) and run the config +with the suffix `:test_with_coco=False` to train on your data set. Refer to +the following guide to build a TFDS wrapper for your favorite image/alt-text +data set: +https://www.tensorflow.org/datasets/add_dataset + +Also note that evaluation on ImageNet requires manual TFDS setup, see +https://github.com/google-research/big_vision#preparing-tfds-data + + +Example training: + +big_vision.trainers.proj.image_text.contrastive \ + --config big_vision/configs/proj/clippo/train_clippo.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%Y-%m-%d_%H%M'` + +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr +from big_vision.configs.proj.image_text import common +from ml_collections import ConfigDict + + +def get_config(arg=None): + """The base configuration.""" + arg = bvcc.parse_arg( + arg, res=224, runlocal=False, variant='B/16', + test_with_coco=True, i1k_eval=False) + config = ConfigDict() + + config.input = {} + if arg.test_with_coco: + # Use COCO Captions for sanity-checking + config.input.data = dict(name='coco_captions', split='train') + val_data = dict(config.input.data) + val_data['split'] = 'val' + config.input.batch_size = 4000 if not arg.runlocal else 32 + config.input.shuffle_buffer_size = 50_000 if not arg.runlocal else 50 + config.total_steps = 400 if not arg.runlocal else 10 + else: + # Please add your favorite image/alt-text dataset here + config.input.data = None + val_data = None + assert config.input.data is not None and val_data is not None, ( + config.input.data, val_data) + + # The value in the paper is 10 * 1024, which requires 128 TPUv3 cores or a + # memory optimized ViT implementation when running on 128 TPUv2 cores. + config.input.batch_size = 8 * 1024 if not arg.runlocal else 32 + config.input.shuffle_buffer_size = 250_000 if not arg.runlocal else 50 + config.total_steps = 100_000 if not arg.runlocal else 10 + + def tokenizer(inkey, outkey='labels'): + return (f'render_unifont(' + f'inkey="{inkey}", ' + f'outkey="{outkey}", ' + f'image_size={arg.res}, ' + f'lower=True, ' + f'font_size=16, ' + f'text_brightness=0, ' + f'background_brightness=127)|' + f'value_range(-1, 1, inkey="{outkey}", outkey="{outkey}")') + + pp_image = f'decode|resize({arg.res})|value_range(-1,1)' + if arg.test_with_coco: + # Train with augmentation when sanity-checking + pp_image_aug = ( + f'decode|resize({arg.res})|flip_lr|randaug(2,10)|value_range(-1,1)') + config.input.pp = pp_eval = ( + f'{pp_image_aug}|flatten|{tokenizer("captions/text")}|' + f'keep("image", "labels")') + else: + config.input.pp = pp_eval = ( + f'{pp_image}|flatten|{tokenizer("text")}|keep("image", "labels")') + + config.pp_modules = [ + 'ops_general', 'ops_image', 'ops_text', 'proj.clippo.pp_ops'] + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 5000 + + config.loss_use_global_batch = True + + # Define the model + config.model_name = 'proj.clippo.one_tower' + + config.model = ConfigDict() + config.model.image_model = 'vit' + config.model.image = ConfigDict({ + 'variant': arg.variant, + 'pool_type': 'map', + 'head_zeroinit': False, + }) + + if arg.test_with_coco: + # Initialize with ImageNet21k pretrained checkpoint for sanity-checking + assert arg.variant == 'B/16', arg.variant + config.model_init = {'image': 'howto-i21k-B/16'} + config.model_load = {} + config.model_load['img_load_kw'] = { + 'dont_load': ['^head/.*', '^MAPHead_0/.*', 'cls']} + + config.model.temperature_init = 10.0 + config.model.out_dim = 768 + + # Define the optimizer + config.optax_name = 'big_vision.scale_by_adafactor' + config.grad_clip_norm = 1.0 + + if arg.test_with_coco: + # Short schedule for sanity-checking + config.lr = 0.0001 + config.wd = 0.0003 + config.schedule = dict(decay_type='rsqrt', + timescale=100, + warmup_steps=100 if not arg.runlocal else 5, + cooldown_steps=100 if not arg.runlocal else 5) + else: + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = dict(decay_type='rsqrt', + timescale=10_000, + warmup_steps=10_000 if not arg.runlocal else 5, + cooldown_steps=10_000 if not arg.runlocal else 5) + + # Eval section (Both few-shot and zero-shot) + eval_common = dict( + type='proj.image_text.contrastive', + use_global_batch=config.loss_use_global_batch, + log_steps=1000 if not arg.runlocal else 5, + ) + config.evals = {} + sub = '[:4]' if arg.runlocal else '' + config.evals.val = { + **eval_common, + 'data': val_data, + 'pp_fn': pp_eval, + } + config.evals.coco = { + **eval_common, + 'data': dict(name='coco_captions', split=f'val{sub}'), + 'pp_fn': ( + f'{pp_image}|flatten|{tokenizer("captions/text")}|' + f'keep("image", "labels")'), + } + + if arg.i1k_eval: + # Requires manual download, see + # https://github.com/google-research/big_vision#preparing-tfds-data + config.evals.imagenet = { + **eval_common, + 'data': dict(name='imagenet2012', split=f'validation{sub}'), + 'pp_fn': ( + f'{pp_image}|clip_i1k_label_names|' + f'{tokenizer("labels")}|keep("image", "labels")'), + } + config.evals.disclf = dict( + type='proj.image_text.discriminative_classifier', + pp_txt=tokenizer('texts', 'labels'), + prefix='z/0shot/', + log_steps=5_000 if not arg.runlocal else 5) + + config.evals.retrieval_coco = common.get_coco( + pp_img=f'resize({arg.res})|value_range(-1, 1)', + pp_txt=tokenizer('texts'), + log_steps=5_000 if not arg.runlocal else 5, + ) + + # Few-shot metrics + config.evals.fewshot = get_fewshot_lsr() + config.evals.fewshot.log_steps = 5_000 if not arg.runlocal else 5 + config.evals.fewshot.representation_layer = 'img/pre_logits' + + config.seed = 0 + + return config diff --git a/big_vision/configs/proj/distill/README.md b/big_vision/configs/proj/distill/README.md new file mode 100644 index 0000000000000000000000000000000000000000..44e10c3670d40307b74e367a9e6725ca1f848c61 --- /dev/null +++ b/big_vision/configs/proj/distill/README.md @@ -0,0 +1,43 @@ +# Knowledge distillation: A good teacher is patient and consistent +*by Lucas Beyer, Xiaohua Zhai, Amélie Royer, Larisa Markeeva, Rohan Anil, Alexander Kolesnikov* + +## Introduction +We publish all teacher models, and configurations for the main experiments of +the paper, as well as training logs and student models. + +Please read the main [big_vision README](/README.md) to learn how to run +configs, and remember that each config file contains an example invocation in +the top-level comment. + +## Results + +We provide the following [colab to read and plot the logfiles](https://colab.research.google.com/drive/1nMykzUzsfQ_uAxfj3k35DYsATnG_knPl?usp=sharing) +of a few runs that we reproduced on Cloud. + +### ImageNet-1k + +The file [bit_i1k.py](bit_i1k.py) is the configuration which reproduces our +distillation runs on ImageNet-1k reported in Figures 1 and 5(left) and the first +row of Table1. + +We release both student and teacher models: + +| Model | Download link | Resolution | ImageNet top-1 acc. (paper) | +| :--- | :---: | :---: | :---: | +| BiT-R50x1 | [link](https://storage.googleapis.com/bit_models/distill/R50x1_160.npz) | 160 | 80.5 | +| BiT-R50x1 | [link](https://storage.googleapis.com/bit_models/distill/R50x1_224.npz) | 224 | 82.8 | +| BiT-R152x2 | [link](https://storage.googleapis.com/bit_models/distill/R152x2_T_224.npz) | 224 | 83.0 | +| BiT-R152x2 | [link](https://storage.googleapis.com/bit_models/distill/R152x2_T_384.npz) | 384 | 84.3 | + +### Flowers/Pet/Food/Sun + +The files [bigsweep_flowers_pet.py](bigsweep_flowers_pet.py) and +[bigsweep_food_sun.py](bigsweep_food_sun.py) can be used to reproduce the +distillation runs on these datasets and shown in Figures 3,4,9-12, and Table4. + +While our open-source release does not currently support doing hyper-parameter +sweeps, we still provide an example of the sweeps at the end of the configs +for reference. + +### Teacher models +Links to all teacher models we used can be found in [common.py](common.py). diff --git a/big_vision/configs/proj/distill/bigsweep_flowers_pet.py b/big_vision/configs/proj/distill/bigsweep_flowers_pet.py new file mode 100644 index 0000000000000000000000000000000000000000..977f7d2e5cf672bae6032b108148d123ecca4265 --- /dev/null +++ b/big_vision/configs/proj/distill/bigsweep_flowers_pet.py @@ -0,0 +1,164 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Distilling BiT-R152x2 into BiT-R50x1 on Flowers/Pet as in https://arxiv.org/abs/2106.05237 + +While many epochs are required, this is a small dataset, and thus overall it +is still fast and possible to run on the relatively small v3-8TPUs (or GPUs). + +This configuration contains the recommended settings from Fig3/Tab4 of the +paper, which can be selected via the fast/medium/long config argument. +(best settings were selected on a 10% minival) + +For Flowers: +- The `fast` variant takes ~1h10m on a v2-8 TPU. + Example logs at gs://big_vision/distill/bit_flowers_fast_06-18_2008/big_vision_metrics.txt +- The `long` variant takes ~25h on a v3-32 TPU. + Example logs at gs://big_vision/distill/bit_flowers_long_06-19_0524/big_vision_metrics.txt +For Pet: +- The `fast` variant takes ~28min on a v2-8 TPU. + Example logs at gs://big_vision/distill/bit_pet_fast_06-16_2338/big_vision_metrics.txt +- The `long` variant takes ~11h on a v2-8 and ~8h on a v3-32. + Example logs at gs://big_vision/distill/bit_pet_long_06-17_0050/big_vision_metrics.txt + +big_vision.trainers.proj.distill.distill \ + --config big_vision/configs/proj/distill/bigsweep_flowers_pet.py:data=flowers,variant=fast \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ +""" + +import big_vision.configs.common as bvcc +import big_vision.configs.proj.distill.common as cd +import ml_collections as mlc + +NCLS = dict(flowers=102, pet=37) + + +def get_config(arg=None): + """Config for massive hypothesis-test on pet.""" + arg = bvcc.parse_arg(arg, runlocal=False, data='flowers', variant='medium', crop='inception_crop(128)') + config = mlc.ConfigDict() + + config.input = {} + config.input.data = dict( + name=dict(flowers='oxford_flowers102', pet='oxford_iiit_pet')[arg.data], + split=dict(flowers='train', pet='train[:90%]')[arg.data], + ) + config.input.batch_size = 512 + config.input.cache_raw = True + config.input.shuffle_buffer_size = 50_000 + config.prefetch_to_device = 4 + + config.num_classes = NCLS[arg.data] + config.total_epochs = { + 'flowers': {'fast': 10_000, 'medium': 100_000, 'long': 1_000_000}, + 'pet': {'fast': 1000, 'medium': 3000, 'long': 30_000}, + }[arg.data][arg.variant] + + config.log_training_steps = 100 + config.ckpt_steps = 2500 + + # Model section + config.student_name = 'bit_paper' + config.student = dict(depth=50, width=1) + + config.teachers = ['prof_m'] + config.prof_m_name = 'bit_paper' + config.prof_m_init = cd.inits[f'BiT-M R152x2 {arg.data} rc128'] + config.prof_m = dict(depth=152, width=2) + + # Preprocessing pipeline for student & tacher. + pp_common = ( + '|value_range(-1, 1)' + f'|onehot({config.num_classes}, key="label", key_result="labels")' + '|keep("image", "labels")' + ) + config.input.pp = f'decode|{arg.crop}|flip_lr' + pp_common + ppv = 'decode|resize_small(160)|central_crop(128)' + pp_common + + config.mixup = dict(p=1.0) + + # Distillation settings + config.distance = 'kl' + config.distance_kw = dict(t={ + 'flowers': {'fast': 10., 'medium': 1., 'long': 1.}, + 'pet': {'fast': 5., 'medium': 10., 'long': 2.}, + }[arg.data][arg.variant]) + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + + config.lr = { + 'flowers': {'fast': 0.003, 'medium': 0.001, 'long': 0.0003}, + 'pet': {'fast': 0.01, 'medium': 0.003, 'long': 0.003}, + }[arg.data][arg.variant] + config.wd = { + 'flowers': {'fast': 3e-4, 'medium': 1e-4, 'long': 1e-5}, + 'pet': {'fast': 1e-3, 'medium': 3e-4, 'long': 1e-5}, + }[arg.data][arg.variant] + config.schedule = dict(warmup_steps=1500, decay_type='cosine') + config.optim_name = 'adam_hp' + + # Eval section + minitrain_split = 'train[:512]' if not arg.runlocal else 'train[:16]' + if arg.data == 'flowers': + val_split = 'validation' if not arg.runlocal else 'validation[:16]' + test_split = 'test' if not arg.runlocal else 'test[:16]' + elif arg.data == 'pet': + val_split = 'train[90%:]' if not arg.runlocal else 'train[:16]' + test_split = 'test' if not arg.runlocal else 'test[:16]' + + def get_eval(split): + return dict( + type='classification', + pred='student_fwd', + data=dict(name=config.input.data.name, split=split), + pp_fn=ppv, + loss_name='softmax_xent', + log_steps=500, + ) + config.evals = {} + config.evals.student_train = get_eval(minitrain_split) + config.evals.student_val = get_eval(val_split) + config.evals.student_test = get_eval(test_split) + + # Teacher is fixed, so rare evals. + teacher = dict(log_steps=100_000, pred='prof_m_fwd') + config.evals.teacher_train = {**config.evals.student_train, **teacher} + config.evals.teacher_val = {**config.evals.student_val, **teacher} + config.evals.teacher_test = {**config.evals.student_test, **teacher} + + # Could in principle also look at agreement on other datasets! + def get_dist(split): + return dict( + type='proj.distill.distance', + pred='student_prof_m_fwd', + data=dict(name=config.input.data.name, split=split), + pp_fn=ppv + '|keep("image")', + log_steps=1000, + distances=({'kind': 'kl'}, {'kind': 'euclidean'}, + {'kind': 'agree', 'k': 1}, {'kind': 'agree', 'k': 5}), + ) + config.evals.dist_train = get_dist(minitrain_split) + config.evals.dist_val = get_dist(val_split) + config.evals.dist_test = get_dist(test_split) + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 8 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/distill/bigsweep_food_sun.py b/big_vision/configs/proj/distill/bigsweep_food_sun.py new file mode 100644 index 0000000000000000000000000000000000000000..36362e658cc0b0cec13f3b33d2f31cd7cd45ff37 --- /dev/null +++ b/big_vision/configs/proj/distill/bigsweep_food_sun.py @@ -0,0 +1,213 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Distilling BiT-R152x2 into BiT-R50x1 on Food101/Sun397 as in https://arxiv.org/abs/2106.05237 + +While many epochs are required, this is a small dataset, and thus overall it +is still fast and possible to run on the relatively small v3-8TPUs (or GPUs). + +This configuration contains the recommended settings from Fig3/Tab4 of the +paper, which can be selected via the fast/medium/long config argument. +(best settings were selected on a 10% minival) + +For Food101: +- The `fast` variant takes ~45min on a v2-8 TPU. + Example logs at gs://big_vision/distill/bit_food_fast_06-19_0547/big_vision_metrics.txt + Example logs at gs://big_vision/distill/bit_sun_fast_06-20_1839/big_vision_metrics.txt +- The `long` variant takes ~14h on a v3-8 TPU. + Example logs at gs://big_vision/distill/bit_food_long_06-19_0614/big_vision_metrics.txt + Example logs at gs://big_vision/distill/bit_sun_long_06-20_1912/big_vision_metrics.txt + +big_vision.trainers.proj.distill.distill \ + --config big_vision/configs/proj/distill/bigsweep_food_sun.py:data=food,variant=fast \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ +""" + +import big_vision.configs.common as bvcc +import big_vision.configs.proj.distill.common as cd +import ml_collections as mlc + +H, L = 160, 128 +NCLS = dict(food=101, sun=397) + + +def get_config(arg=None): + """Config for massive hypothesis-test on pet.""" + arg = bvcc.parse_arg(arg, runlocal=False, data='food', variant='medium', crop='inception_crop(128)') + config = mlc.ConfigDict() + + config.input = {} + config.input.data = dict( + name=dict(food='food101', sun='sun397')[arg.data], + split=dict(food='train[:90%]', sun='train')[arg.data], + ) + config.input.batch_size = 512 + config.input.cache_raw = True + config.input.shuffle_buffer_size = 50_000 + config.prefetch_to_device = 4 + + config.num_classes = NCLS[arg.data] + config.total_epochs = {'fast': 100, 'medium': 1000, 'long': 3000}[arg.variant] + + config.log_training_steps = 50 + config.ckpt_steps = 2500 + + # Model section + config.student_name = 'bit_paper' + config.student = dict(depth=50, width=1) + + config.teachers = ['prof_m'] + config.prof_m_name = 'bit_paper' + config.prof_m_init = cd.inits[f'BiT-M R152x2 {arg.data} rc128'] + config.prof_m = dict(depth=152, width=2) + + # Preprocessing pipeline for student & tacher. + pp_common = ( + '|value_range(-1, 1)' + f'|onehot({config.num_classes}, key="label", key_result="labels")' + '|keep("image", "labels")' + ) + config.input.pp = f'decode|{arg.crop}|flip_lr' + pp_common + ppv = 'decode|resize_small(160)|central_crop(128)' + pp_common + + config.mixup = dict(p=1.0) + + # Distillation settings + config.distance = 'kl' + config.distance_kw = dict(t={ + 'food': {'fast': 10., 'medium': 10., 'long': 5.}, + 'sun': {'fast': 10., 'medium': 10., 'long': 10.}, + }[arg.data][arg.variant]) + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + + config.lr = { + 'food': {'fast': 0.01, 'medium': 0.001, 'long': 0.01}, + 'sun': {'fast': 0.01, 'medium': 0.001, 'long': 0.01}, + }[arg.data][arg.variant] + config.wd = { + 'food': {'fast': 1e-3, 'medium': 3e-4, 'long': 1e-4}, + 'sun': {'fast': 1e-3, 'medium': 1e-4, 'long': 3e-5}, + }[arg.data][arg.variant] + config.schedule = dict(warmup_steps=1500, decay_type='cosine') + config.optim_name = 'adam_hp' + + # Eval section + minitrain_split = 'train[:1024]' if not arg.runlocal else 'train[:16]' + if arg.data == 'food': + val_split = 'train[90%:]' if not arg.runlocal else 'train[:16]' + test_split = 'validation' if not arg.runlocal else 'test[:16]' + elif arg.data == 'sun': + val_split = 'validation' if not arg.runlocal else 'validation[:16]' + test_split = 'test' if not arg.runlocal else 'test[:16]' + + def get_eval(split): + return dict( + type='classification', + pred='student_fwd', + data=dict(name=config.input.data.name, split=split), + pp_fn=ppv, + loss_name='softmax_xent', + log_steps=500, + ) + config.evals = {} + config.evals.student_train = get_eval(minitrain_split) + config.evals.student_val = get_eval(val_split) + config.evals.student_test = get_eval(test_split) + + # Teacher is fixed, so rare evals. + teacher = dict(log_steps=100_000, pred='prof_m_fwd') + config.evals.teacher_train = {**config.evals.student_train, **teacher} + config.evals.teacher_val = {**config.evals.student_val, **teacher} + config.evals.teacher_test = {**config.evals.student_test, **teacher} + + # Could in principle also look at agreement on other datasets! + def get_dist(split): + return dict( + type='proj.distill.distance', + pred='student_prof_m_fwd', + data=dict(name=config.input.data.name, split=split), + pp_fn=ppv + '|keep("image")', + log_steps=1000, + distances=({'kind': 'kl'}, {'kind': 'euclidean'}, + {'kind': 'agree', 'k': 1}, {'kind': 'agree', 'k': 5}), + ) + config.evals.dist_train = get_dist(minitrain_split) + config.evals.dist_val = get_dist(val_split) + config.evals.dist_test = get_dist(test_split) + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 8 + + return config + + +def get_hyper(hyper): + """Hyper sweep.""" + # TODO: update, similar to flowers_pet sweep. + # By default, not running the MASSIVE sweep, just the recommended setting + # across durations. However, code for sweep is left for reference/convenience. + return hyper.zipit([ + hyper.sweep('config.total_epochs', [100, 1_000]), + hyper.sweep('config.mixup.p', [0.0, 1.0]), + hyper.sweep('config.weight_decay', [1e-3, 1e-5]), + ]) + + # pylint: disable=unreachable + + def fix(**kw): + return hyper.product([hyper.fixed(f'config.{k}', v, length=1) + for k, v in kw.items()]) + + def setting(p, l, m, crop, pp_end=None, **extra): + pp_end = pp_end or ( + f'|value_range(-1, 1, key="image")' + f'|onehot({NCLS}, key="label", key_result="labels")' + f'|keep("image", "labels")' + ) + return hyper.product([ + fix(**{'mixup.p': p}), + fix(l=l, m=m, crop=crop), + fix(pp_train=f'decode|{crop}|flip_lr|randaug({l},{m})' + pp_end), + fix(**extra) + ]) + + # Mixup, Layers and Mag in randaug. + plm = [(0.0, 0, 0), (0.1, 0, 0), (0.5, 0, 0), (1.0, 0, 0)] + return hyper.product([ + hyper.sweep('config.total_epochs', [100, 1000, 3000]), + hyper.sweep('config.lr.base', [0.001, 0.003, 0.01]), + hyper.sweep('config.distance_kw.t', [1.0, 2.0, 5.0, 10.0]), + hyper.sweep('config.weight_decay', [1e-5, 3e-5, 1e-4, 3e-4, 1e-3]), + hyper.chainit( + [setting(p=p, l=l, m=m, + crop=(f'resize({H})' + f'|inception_crop({L}, outkey="student")' + f'|central_crop({L}, outkey="teacher")'), + pp_end=( + f'|value_range(-1, 1, key="student")' + f'|value_range(-1, 1, key="teacher")' + f'|onehot({NCLS}, key="label", key_result="labels")' + f'|keep("student", "teacher", "labels")')) + for p, l, m in plm] + + [setting(p=p, l=l, m=m, crop=f'inception_crop({L})') for + p, l, m in plm], + ) + ]) diff --git a/big_vision/configs/proj/distill/bit_i1k.py b/big_vision/configs/proj/distill/bit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..24c2a0388109de1e3de6504422c54f0317cbed8a --- /dev/null +++ b/big_vision/configs/proj/distill/bit_i1k.py @@ -0,0 +1,167 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Distilling BiT-R152x2 into BiT-R50x1 on ILSVRC-2012 as in https://arxiv.org/abs/2106.05237 + +Note that as per paper title, good results require many epochs and thus +a lot of _patience_. For experimentation/exploration, consider +using the smaller datasets. + +300ep take about 15h on a v3-32 TPU, an example log is available at: + Example logs at gs://big_vision/distill/bit_i1k_300ep_06-16/big_vision_metrics.txt + +big_vision.trainers.proj.distill.distill \ + --config big_vision/configs/proj/distill/bit_i1k.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 1200 +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr +import big_vision.configs.proj.distill.common as cd +import ml_collections as mlc + + +def get_config(arg=None): + """Config for distilling on ImageNet.""" + arg = bvcc.parse_arg(arg, runlocal=False) + config = mlc.ConfigDict() + + config.input = {} + config.input.data = dict(name='imagenet2012', split='train[:98%]') + config.input.batch_size = 4096 + config.input.shuffle_buffer_size = 250_000 + + config.num_classes = 1000 + config.total_epochs = 1200 # A good middle-ground + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 20000 + + # Model section + config.student_name = 'bit_paper' + config.student = dict(depth=50, width=1) + + config.teachers = ['prof_m'] # You could even add multiple. + + # TODO: use public checkpoint name. + config.prof_m_name = 'bit_paper' + config.prof_m_init = cd.inits['BiT-M R152x2 imagenet2012 ic224'] + config.prof_m = dict(depth=152, width=2) + + pp_common = ( + '|value_range(-1, 1)' + '|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ) + config.input.pp = ( + 'decode_jpeg_and_inception_crop(224)|flip_lr' + + pp_common.format(lbl='label') + ) + ppv = 'decode|resize_small(256)|central_crop(224)' + pp_common + + config.mixup = dict(p=1.0) + + # Distillation settings + config.distance = 'kl' + config.distance_kw = dict(t=1.0) + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + + config.lr = 0.03 + config.wd = 0.0003 + config.schedule = dict(warmup_steps=5000, decay_type='cosine') + + # Eval section + minitrain_split = 'train[:2%]' if not arg.runlocal else 'train[:16]' + minival_split = 'train[99%:]' if not arg.runlocal else 'train[:16]' + val_split = 'validation' if not arg.runlocal else 'validation[:16]' + real_split = 'validation' if not arg.runlocal else 'validation[:16]' + v2_split = 'test' if not arg.runlocal else 'test[:16]' + + def get_eval(split, dataset='imagenet2012'): + return dict( + type='classification', + pred='student_fwd', + data=dict(name=dataset, split=split), + pp_fn=ppv.format(lbl='label'), + loss_name='softmax_xent', + log_steps=1000, + ) + + config.evals = {} + config.evals.student_train = get_eval(minitrain_split) + config.evals.student_minival = get_eval(minival_split) + config.evals.student_val = get_eval(val_split) + config.evals.student_v2 = get_eval(v2_split, dataset='imagenet_v2') + config.evals.student_real = get_eval(real_split, dataset='imagenet2012_real') + config.evals.student_real.pp_fn = ppv.format(lbl='real_label') + + config.evals.student_fewshot = get_fewshot_lsr(runlocal=arg.runlocal) + config.evals.student_fewshot.pred = 'student_fwd' + config.evals.student_fewshot.log_steps = 10_000 + + teacher_eval = dict( + log_steps=100_000, # Teacher is fixed, so rare evals. + pred='prof_m_fwd', + ) + config.evals.teacher_train = {**config.evals.student_train, **teacher_eval} + config.evals.teacher_minival = {**config.evals.student_minival, **teacher_eval} + config.evals.teacher_val = {**config.evals.student_val, **teacher_eval} + config.evals.teacher_v2 = {**config.evals.student_v2, **teacher_eval} + config.evals.teacher_real = {**config.evals.student_real, **teacher_eval} + config.evals.teacher_fewshot = {**config.evals.student_fewshot, **teacher_eval} + config.evals.teacher_fewshot.prefix = 'z_teacher/' + + # Could in principle also look at agreement on other datasets! + def get_dist(split, dataset='imagenet2012'): + return dict( + type='proj.distill.distance', + pred='student_prof_m_fwd', + data=dict(name=dataset, split=split), + pp_fn=ppv.format(lbl='label') + '|keep("image")', + log_steps=1000, + distances=({'kind': 'kl'}, {'kind': 'euclidean'}, + {'kind': 'agree', 'k': 1}, {'kind': 'agree', 'k': 5}), + ) + config.evals.dist_train = get_dist(minitrain_split) + config.evals.dist_minival = get_dist(minival_split) + config.evals.dist_val = get_dist(val_split) + config.evals.dist_v2 = get_dist(v2_split, dataset='imagenet_v2') + + # NOTE: CKA evaluator does not work with batch padding, so the size of the + # split must be a multiple of the batch size. + def get_cka(split): + return dict( + type='proj.distill.cka', + pred='student_prof_m_fwd', + data=dict(name='imagenet2012', split=split), + pp_fn=ppv.format(lbl='label') + '|keep("image")', + log_steps=1000, + ) + config.evals.cka_train = get_cka('train[:24576]' if not arg.runlocal else 'train[:16]') + config.evals.cka_minival = get_cka('train[-24576:]' if not arg.runlocal else 'train[:16]') + config.evals.cka_val = get_cka('validation[:49152]' if not arg.runlocal else 'validation[:16]') + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 8 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/distill/common.py b/big_vision/configs/proj/distill/common.py new file mode 100644 index 0000000000000000000000000000000000000000..f4bb89ab540878672424e95856194ff98f3616f5 --- /dev/null +++ b/big_vision/configs/proj/distill/common.py @@ -0,0 +1,27 @@ +# Copyright 2024 Big Vision 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. + +"""Most common teachers for distillation.""" + +# pylint: disable=line-too-long +inits = { # pylint: disable=duplicate-key Internally, we override some paths for convenience. + 'BiT-M R152x2 imagenet2012 ic224': 'gs://bit_models/distill/R152x2_T_224.npz', + 'BiT-M R152x2 imagenet2012 rc384': 'gs://bit_models/distill/R152x2_T_384.npz', + 'BiT-M R152x2 flowers rc128': 'gs://bit_models/distill/R152x2_T_flowers128.npz', + 'BiT-M R152x2 pet rc128': 'gs://bit_models/distill/R152x2_T_pet128.npz', + 'BiT-M R152x2 food rc128': 'gs://bit_models/distill/R152x2_T_food128.npz', + 'BiT-M R152x2 sun rc128': 'gs://bit_models/distill/R152x2_T_sun128.npz', + +} +# pylint: enable=line-too-long diff --git a/big_vision/configs/proj/flexivit/README.md b/big_vision/configs/proj/flexivit/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2d41d8a954cb199a5b481e30eb32a1095a8523dc --- /dev/null +++ b/big_vision/configs/proj/flexivit/README.md @@ -0,0 +1,64 @@ +# FlexiViT: One Model for All Patch Sizes +*by Lucas Beyer, Pavel Izmailov, Alexander Kolesnikov, Mathilde Caron, Simon Kornblith, Xiaohua Zhai, Matthias Minderer, Michael Tschannen, Ibrahim Alabdulmohsin, Filip Pavetic* + +## Introduction +We publish all pre-trained FlexiViT models, and configurations for training +those, as well as training logs for one run. + +Please read the main [big_vision README](/README.md) to learn how to run +configs, and remember that each config file contains an example invocation in +the top-level comment. + +## Pre-trained paper models + +Here are the models that we used as backbones in the paper. See Tables in the +appendix of the paper for expected scores at various patch-sizes and on various +datasets. + +First, the recommended models we used for all experiments. +Remember that the input is 240px, not 224px: + +| Dataset | Model | Download link | Notes | +| :--- | :---: | :---: | :---: | +| ImageNet-1k | FlexiViT-L | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_l_i1k.npz) | 1200ep version | +| ImageNet-1k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i1k.npz) | 1200ep version | +| ImageNet-1k | FlexiViT-S | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_s_i1k.npz) | 1200ep version | +| ImageNet-21k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i21k_300ep.npz) | 300ep version. 1000ep version below is better but was not used in the paper for fair comparison to baselines. | +| ImageNet-21k | ViT-B/16 | [link](https://storage.googleapis.com/big_vision/flexivit/vit_b16_i21k_300ep.npz) | Apples-to-apples non-flexi baseline used throughout the paper. | +| ImageNet-21k | ViT-B/30 | [link](https://storage.googleapis.com/big_vision/flexivit/vit_b30_i21k_300ep.npz) | Apples-to-apples non-flexi baseline used throughout the paper. | + +These models can be used directly in our codebase by specifying +`model_name = "proj.flexi.vit"` and `model_init = "FlexiViT-L i1k"` for example. +See the file `models/proj/flexi/vit.py` for more names. + +*Important detail:* When further re-using these models with a flexible patch +size, it is recommended to keep the patch-embedding parameter buffer at its +original size, and change patch-size on the fly using pi-resize, as opposed to +changing the parameter buffer's size at load-time. +For re-using the models with a fixed patch size, either way is fine. +(The reason is that it is impossible to chain multiple resizes without loss, +eg doing 32->8->32 does not result in the original weights.) + +Second, the list of all released models for completeness: + +| Dataset | Model | Download link | Notes | +| :--- | :---: | :---: | :---: | +| ImageNet-21k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i21k_1000ep.npz) | 1000ep version. Should be the best available -B model. | +| ImageNet-21k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i21k_90ep.npz) | 90ep version | +| ImageNet-1k | FlexiViT-L | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_l_i1k_600ep.npz) | 600ep version | +| ImageNet-1k | FlexiViT-L | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_l_i1k_300ep.npz) | 300ep version | +| ImageNet-1k | FlexiViT-L | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_l_i1k_90ep.npz) | 90ep version | +| ImageNet-1k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i1k_600ep.npz) | 600ep version | +| ImageNet-1k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i1k_300ep.npz) | 300ep version | +| ImageNet-1k | FlexiViT-B | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_b_i1k_90ep.npz) | 90ep version | +| ImageNet-1k | FlexiViT-S | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_s_i1k_600ep.npz) | 600ep version | +| ImageNet-1k | FlexiViT-S | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_s_i1k_300ep.npz) | 300ep version | +| ImageNet-1k | FlexiViT-S | [link](https://storage.googleapis.com/big_vision/flexivit/flexivit_s_i1k_90ep.npz) | 90ep version | + +## Results + +We provide full training logs for a run with this public code on Cloud that +reproduces the FlexiViT-S 90ep on i1k results: + - [metrics](https://storage.googleapis.com/big_vision/flexivit/deit3_i1k_s_90ep_12-15_2254/big_vision_metrics.txt) + - [config](https://storage.googleapis.com/big_vision/flexivit/deit3_i1k_s_90ep_12-15_2254/config.json) + - or `gs://big_vision/flexivit/deit3_i1k_s_90ep_12-15_2254`. diff --git a/big_vision/configs/proj/flexivit/i1k_deit3_distill.py b/big_vision/configs/proj/flexivit/i1k_deit3_distill.py new file mode 100644 index 0000000000000000000000000000000000000000..fd7bf3c14b7ef48279fe75ff653fb4f25434762a --- /dev/null +++ b/big_vision/configs/proj/flexivit/i1k_deit3_distill.py @@ -0,0 +1,187 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Distillation of ViT models into FlexiViT on ImageNet1k. + +Run training of the -S variant for 90ep: + +big_vision.trainers.proj.flexi.distill \ + --config big_vision/configs/proj/flexivit/i1k_deit3_distill.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 90 --config.variant S + +Logdir for one reproduction run: + - gs://big_vision/flexivit/deit3_i1k_s_90ep_12-15_2254 + +Timing on Cloud: + - S on v3-32: Walltime:10h16m (4h39m eval) + +Note that we did not optimize the input for Cloud, +with tuned caching and prefetching, we should be able to get: + - S on v3-32: Walltime: ~6h30m (~1h30m eval) + - B on v3-32: Walltime: ~16h00m (~2h30m eval) +""" + +import big_vision.configs.common as bvcc + + +def get_config(arg=None): + """Config for distilling ViT on ImageNet1k.""" + c = bvcc.parse_arg(arg, runlocal=False, res=240) + + c.seed = 0 + c.total_epochs = 90 + c.num_classes = 1000 + c.loss = 'softmax_xent' + + c.input = {} + c.input.data = dict( + name='imagenet2012', + split='train[:99%]', + ) + c.input.batch_size = 1024 if not c.runlocal else 8 + c.input.cache_raw = False # Needs up to 120GB of RAM! + c.input.shuffle_buffer_size = 250_000 if not c.runlocal else 10 + + c.log_training_steps = 50 + c.ckpt_steps = 1000 + + # Model section + c.variant = 'B' + init = bvcc.format_str('deit3_{variant}_384_1k', c) + c.student_name = 'proj.flexi.vit' + c.student_init = init + c.student = dict(variant=c.get_ref('variant'), pool_type='tok', patch_size=(16, 16)) + + c.teachers = ['prof'] # You could even add multiple. + c.prof_name = 'vit' + c.prof_init = init + c.prof = dict(variant=c.get_ref('variant'), pool_type='tok', patch_size=(16, 16)) + + pp_label = '|onehot(1000, key="{lbl}", key_result="labels")|keep("image", "prof", "labels")' + c.input.pp = ( + f'decode|inception_crop|flip_lr' + '|copy("image", "prof")' + f'|resize({c.res})|value_range' + '|resize(384, key="prof")|value_range(key="prof")' + + pp_label.format(lbl='label') + ) + pp_eval_both = ( + 'decode|copy("image", "prof")|' + f'|resize({c.res//7*8})|central_crop({c.res})|value_range' + f'|resize({384//7*8}, key="prof")|central_crop(384, key="prof")|value_range(key="prof")|' + ) + pp_eval_student = ( + f'decode|resize({c.res//7*8})|central_crop({c.res})|value_range(-1, 1)' + ) + pp_eval_prof = ( + f'decode|resize({384//7*8})|central_crop(384)|value_range(outkey="prof")' + ) + + c.mixup = dict(p=1.0, n=2) + + # Distillation settings + c.distance = 'kl' + c.distance_kw = dict(t=1.0) + + # Optimizer section + c.grad_clip_norm = 1.0 + c.optax_name = 'scale_by_adam' + c.optax = dict(mu_dtype='bfloat16') + + c.lr = 1e-4 + c.wd = 1e-5 + c.schedule = dict(warmup_steps=5000, decay_type='cosine') + + # Define the model parameters which are flexible: + c.flexi = dict() + c.flexi.seqhw = dict( + # The settings to sample from. Corresponding patch-sizes at 240px: + # 48, 40, 30, 24, 20, 16, 15, 12, 10, 8 + v=(5, 6, 8, 10, 12, 15, 16, 20, 24, 30), + # The probabilities/weights of them. Default uniform. + p=(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + ) + + # Eval section + def mksplit(split): + if c.runlocal: + return split.split('[')[0] + '[:16]' + return split + + minitrain_split = mksplit('train[:2%]') + minival_split = mksplit('train[99%:]') + val_split = mksplit('validation') + test_split = mksplit('test') + c.aggressive_cache = False + + def get_eval(s, split, dataset='imagenet2012'): + return dict( + type='classification', + pred=f'student_seqhw={s}', + data=dict(name=dataset, split=split), + pp_fn=pp_eval_student + pp_label.format(lbl='label'), + loss_name='sigmoid_xent', + log_percent=0.05, + cache_final=False, + ) + + c.evals = {} + for s in c.flexi.seqhw.v: + c.evals[f'student_minitrain_{s:02d}'] = get_eval(s, minitrain_split) + c.evals[f'student_minival_{s:02d}'] = get_eval(s, minival_split) + c.evals[f'student_val_{s:02d}'] = get_eval(s, val_split) + c.evals[f'student_v2_{s:02d}'] = get_eval(s, test_split, 'imagenet_v2') + c.evals[f'student_a_{s:02d}'] = get_eval(s, test_split, 'imagenet_a') + c.evals[f'student_r_{s:02d}'] = get_eval(s, test_split, 'imagenet_r') + c.evals[f'student_real_{s:02d}'] = get_eval(s, val_split, 'imagenet2012_real') + c.evals[f'student_real_{s:02d}'].pp_fn = pp_eval_student + pp_label.format(lbl='real_label') + + def get_eval_t(split, dataset='imagenet2012'): + return dict( + type='classification', + pred='prof', + data=dict(name=dataset, split=split), + pp_fn=pp_eval_prof + pp_label.format(lbl='label'), + loss_name='sigmoid_xent', + log_percent=0.5, # Teacher is fixed, so eval just for plots. + cache_final=False, + ) + c.evals.teacher_minitrain = get_eval_t(minitrain_split) + c.evals.teacher_minival = get_eval_t(minival_split) + c.evals.teacher_val = get_eval_t(val_split) + c.evals.teacher_v2 = get_eval_t(test_split, 'imagenet_v2') + c.evals.teacher_a = get_eval_t(test_split, 'imagenet_a') + c.evals.teacher_r = get_eval_t(test_split, 'imagenet_r') + c.evals.teacher_real = get_eval_t(val_split, 'imagenet2012_real') + c.evals.teacher_real.pp_fn = pp_eval_prof + pp_label.format(lbl='real_label') + + # Distance evaluators + def get_dist(split, s): + return dict( + type='proj.distill.distance', + pred=f'student_seqhw={s}_prof', + data=dict(name='imagenet2012', split=split), + pp_fn=pp_eval_both + '|keep("image", "prof")', + log_percent=0.05, + distances=({'kind': 'kl'}, {'kind': 'logsoftmax_euclidean'}, + {'kind': 'agree', 'k': 1}, {'kind': 'agree', 'k': 5}), + cache_final=False, + ) + for s in c.flexi.seqhw.v: + c.evals[f'dist_minitrain_{s:02d}'] = get_dist(minitrain_split, s) + c.evals[f'dist_val_{s:02d}'] = get_dist(val_split, s) + + return c \ No newline at end of file diff --git a/big_vision/configs/proj/flexivit/i21k_distill.py b/big_vision/configs/proj/flexivit/i21k_distill.py new file mode 100644 index 0000000000000000000000000000000000000000..30eddec5785e9a551fa5caf825820a43dfdf76ae --- /dev/null +++ b/big_vision/configs/proj/flexivit/i21k_distill.py @@ -0,0 +1,216 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Distill flexible-seqlen ViT on ImageNet-21k from (internal link) B/8. + +This config is for reference, we never ran it on public infrastructure. + +big_vision.trainers.proj.flexi.distill \ + --config big_vision/configs/proj/flexivit/i21k_distill.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 90 +""" + +import big_vision.configs.common as bvcc + + +def get_config(arg=None): + """Config for training.""" + # 240px is nice because it's divisible by + # [240, 120, 80, 60, 48, 40, 30, 24, 20, 16, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1] + c = bvcc.parse_arg(arg, runlocal=False, res=240) + + c.seed = 0 + c.total_epochs = 90 + c.num_classes = 21843 + c.init_head_bias = -10.0 + c.loss = 'sigmoid_xent' + + c.input = dict() + c.input.data = dict( + name='imagenet21k', + split='full[51200:]', + ) + c.input.batch_size = 4096 if not c.runlocal else 8 + c.input.shuffle_buffer_size = 250_000 if not c.runlocal else 25 + + pp_label_i21k = f'|onehot({c.num_classes})|keep("image", "prof", "labels")' + pp_label_i1k = '|onehot(1000, key="{lbl}", key_result="labels")|keep("image", "prof", "labels")' + c.input.pp = ( + f'decode|inception_crop|flip_lr|copy("image", "prof")' + f'|resize({c.res})|value_range(-1, 1)' + f'|resize(224, outkey="prof")|value_range(-1, 1, key="prof")' + + pp_label_i21k + ) + pp_eval_both = ( + 'decode|copy("image", "prof")|' + f'|resize_small({c.res//7*8})|central_crop({c.res})|value_range(-1, 1)' + f'|resize_small(256, key="prof")|central_crop(224, key="prof")|value_range(-1, 1, key="prof")|' + ) + pp_eval_student = ( + f'decode|resize({c.res//7*8})|central_crop({c.res})|value_range(-1, 1)' + ) + pp_eval_prof = ( + 'decode|resize(256)|central_crop(224)|value_range(-1, 1, outkey="prof")' + ) + + # Aggressive pre-fetching because our models here are small, so we not only + # can afford it, but we also need it for the smallest models to not be + # bottle-necked by the input pipeline. Play around with it for -L models tho. + c.input.prefetch = 8 + c.prefetch_to_device = 4 + + c.log_training_steps = 50 + c.ckpt_steps = 1000 + + # Model section + init = 'howto-i21k-B/8' + c.student_name = 'proj.flexi.vit' + c.student_init = init + c.student = dict(variant='B', pool_type='tok', patch_size=(8, 8)) + + c.teachers = ['prof'] # You could even add multiple. + c.prof_name = 'vit' + c.prof_init = init + c.prof = dict(variant='B/8', pool_type='tok') + + # Define the model parameters which are flexible: + c.flexi = dict() + c.flexi.seqhw = dict( + # The settings to sample from. Corresponding patch-sizes at 240px: + # 48, 40, 30, 24, 20, 16, 15, 12, 10, 8 + v=(5, 6, 8, 10, 12, 15, 16, 20, 24, 30), + # The probabilities/weights of them. Default uniform. + p=(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + ) + + # Distillation settings + c.distance = 'kl' + c.distance_kw = dict(t=1.0) + + # Optimizer section + c.optax_name = 'scale_by_adam' + c.optax = dict(mu_dtype='bfloat16') + c.grad_clip_norm = 1.0 + + c.lr = 1e-4 + c.wd = 1e-5 + c.schedule = dict(warmup_steps=5000, decay_type='cosine') + + c.mixup = dict(p=1.0) + + #### + # Preparing for evals + c.evals = {} + def mksplit(split): + if c.runlocal: + return split.split('[')[0] + '[:16]' + return split + + #### + # Student evals + + # Evaluations on i21k itself. + def eval_i21k(s, split): + return dict( + type='classification', + pred=f'student_seqhw={s}', + data={**c.input.data, 'split': mksplit(split)}, + pp_fn=pp_eval_student + pp_label_i21k, + loss_name=c.loss, + log_steps=5000, # Very fast O(seconds) so it's fine to run it often. + ) + + for s in c.flexi.seqhw.v: + c.evals[f'student_test{s:02d}'] = eval_i21k(s, 'full[:25_600]') + c.evals[f'student_val{s:02d}'] = eval_i21k(s, 'full[25_600:51_200]') + c.evals[f'student_minitrain{s:02d}'] = eval_i21k(s, 'full[51_200:76_800]') + + # Evaluations on ImageNet1k variants by label-mapping. + def eval_i1k(s, dataset, split, lblmap): + return dict( + type='classification_with_labelmap', + pred=f'student_seqhw={s}', + data=dict(name=dataset, split=mksplit(split)), + pp_fn=pp_eval_student + pp_label_i1k.format(lbl='label'), + loss_name=c.loss, + log_steps=5000, # Very fast O(seconds) so it's fine to run it often. + label_mapping=lblmap, + ) + for s in c.flexi.seqhw.v: + c.evals[f'student_i1k_val{s:02d}'] = eval_i1k(s, 'imagenet2012', 'validation', 'i1k_i21k') + c.evals[f'student_i1k_v2{s:02d}'] = eval_i1k(s, 'imagenet_v2', 'test', 'i1k_i21k') + c.evals[f'student_i1k_a{s:02d}'] = eval_i1k(s, 'imagenet_a', 'test', 'i1ka_i21k') + c.evals[f'student_i1k_r{s:02d}'] = eval_i1k(s, 'imagenet_r', 'test', 'i1kr_i21k') + c.evals[f'student_i1k_real{s:02d}'] = eval_i1k(s, 'imagenet2012_real', 'validation', 'i1k_i21k') + c.evals[f'student_i1k_real{s:02d}'].pp_fn = pp_eval_student + pp_label_i1k.format(lbl='real_label') + # TODO: add objectnet. + + #### + # Teacher evals + + # Evaluations on i21k itself. + def eval_i21k_t(split): + return dict( + type='classification', + pred='prof', + data={**c.input.data, 'split': mksplit(split)}, + pp_fn=pp_eval_prof + pp_label_i21k, + loss_name=c.loss, + log_steps=5000, # Very fast O(seconds) so it's fine to run it often. + ) + + c.evals.teacher_test = eval_i21k_t('full[:25_600]') + c.evals.teacher_val = eval_i21k_t('full[25_600:51_200]') + c.evals.teacher_minitrain = eval_i21k_t('full[51_200:76_800]') + + # Evaluations on ImageNet1k variants by label-mapping. + def eval_i1k_t(dataset, split, lblmap): + return dict( + type='classification_with_labelmap', + pred='prof', + data=dict(name=dataset, split=mksplit(split)), + pp_fn=pp_eval_prof + pp_label_i1k.format(lbl='label'), + loss_name=c.loss, + log_percent=0.5, # Teacher is fixed, so eval just for plots. + label_mapping=lblmap, + ) + c.evals.teacher_i1k_val = eval_i1k_t('imagenet2012', 'validation', 'i1k_i21k') + c.evals.teacher_i1k_v2 = eval_i1k_t('imagenet_v2', 'test', 'i1k_i21k') + c.evals.teacher_i1k_a = eval_i1k_t('imagenet_a', 'test', 'i1ka_i21k') + c.evals.teacher_i1k_r = eval_i1k_t('imagenet_r', 'test', 'i1kr_i21k') + c.evals.teacher_i1k_real = eval_i1k_t('imagenet2012_real', 'validation', 'i1k_i21k') + c.evals.teacher_i1k_real.pp_fn = pp_eval_prof + pp_label_i1k.format(lbl='real_label') + # TODO: add objectnet. + + #### + # Combined evals + + def get_dist(split, s): + return dict( + type='proj.distill.distance', + pred=f'student_seqhw={s}_prof', + data=dict(name='imagenet2012', split=mksplit(split)), + pp_fn=pp_eval_both + '|keep("image", "prof")', + log_percent=0.05, + distances=({'kind': 'kl'}, {'kind': 'logsoftmax_euclidean'}, + {'kind': 'agree', 'k': 1}, {'kind': 'agree', 'k': 5}), + ) + for s in c.flexi.seqhw.v: + c.evals[f'dist_minitrain_{s:02d}'] = get_dist('full[51_200:76_800]', s) + c.evals[f'dist_val_{s:02d}'] = get_dist('full[25_600:51_200]', s) + + # Few-shot evaluators not added for overkill reasons for now. + return c diff --git a/big_vision/configs/proj/flexivit/i21k_sup.py b/big_vision/configs/proj/flexivit/i21k_sup.py new file mode 100644 index 0000000000000000000000000000000000000000..cca2443024bc4a4fe9c9474a2e34f9d53d4e52a9 --- /dev/null +++ b/big_vision/configs/proj/flexivit/i21k_sup.py @@ -0,0 +1,144 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training flexible-seqlen ViT on ImageNet-21k following (internal link). + +This config is for reference, we never ran it on public infrastructure. + +big_vision.trainers.proj.flexi.train \ + --config big_vision/configs/proj/flexivit/i21k_sup.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 90 +""" + +import big_vision.configs.common as bvcc + + +def get_config(arg=None): + """Config for training.""" + # 240px is nice because it's divisible by + # [240, 120, 80, 60, 48, 40, 30, 24, 20, 16, 15, 12, 10, 8, 6, 5, 4, 3, 2, 1] + c = bvcc.parse_arg(arg, runlocal=False, res=240) + + c.seed = 0 + c.total_epochs = 90 + c.num_classes = 21843 + c.init_head_bias = -10.0 + c.loss = 'sigmoid_xent' + + c.input = dict() + c.input.data = dict( + name='imagenet21k', + split='full[51200:]', + ) + c.input.batch_size = 4096 if not c.runlocal else 8 + c.input.shuffle_buffer_size = 250_000 if not c.runlocal else 25 + + pp_common = '|value_range(-1, 1)|onehot({onehot_args})|keep("image", "labels")' + pp_common_i21k = pp_common.format(onehot_args=f'{c.num_classes}') + pp_common_i1k = pp_common.format(onehot_args='1000, key="{lbl}", key_result="labels"') + c.input.pp = f'decode_jpeg_and_inception_crop({c.res})|flip_lr|randaug(2,10)' + pp_common_i21k + def pp_eval(res=c.res): + return f'decode|resize_small({res//7*8})|central_crop({res})' + + # To continue using the near-defunct randaug op. + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'archive.randaug'] + + # Aggressive pre-fetching because our models here are small, so we not only + # can afford it, but we also need it for the smallest models to not be + # bottle-necked by the input pipeline. Play around with it for -L models tho. + c.input.prefetch = 8 + c.prefetch_to_device = 4 + + c.log_training_steps = 50 + c.ckpt_steps = 1000 + + # Model section + c.model_name = 'proj.flexi.vit' + c.model = dict( + variant='B', + pool_type='tok', + posemb='learn', + # patch_size=(32, 32), + patch_size=(8, 8), + posemb_size=(7, 7), + seqhw=None, # Dynamic! + ) + + # Define the model parameters which are flexible: + c.flexi = dict() + c.flexi.seqhw = dict( + # The settings to sample from. Corresponding patch-sizes at 240px: + # 48, 40, 30, 24, 20, 16, 15, 12, 10, 8 + v=(5, 6, 8, 10, 12, 15, 16, 20, 24, 30), + # The probabilities/weights of them. Default uniform. + p=(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), + ) + + # Optimizer section + c.optax_name = 'scale_by_adam' + c.optax = dict(mu_dtype='bfloat16') + c.grad_clip_norm = 1.0 + + c.lr = 0.001 + c.wd = 0.0001 + c.schedule = dict(warmup_steps=10_000, decay_type='cosine') + + c.mixup = dict(p=0.2, fold_in=None) + + def mksplit(split): + if c.runlocal: + return split.split('[')[0] + '[:16]' + return split + + # Evaluations on i21k itself. + def eval_i21k(s, split): + return dict( + type='classification', + pred=f'predict_seqhw={s}', + data={**c.input.data, 'split': mksplit(split)}, + pp_fn=pp_eval() + pp_common_i21k, + loss_name=c.loss, + log_steps=5000, # Very fast O(seconds) so it's fine to run it often. + ) + + c.evals = {} + for s in c.flexi.seqhw.v: + c.evals[f'test{s:02d}'] = eval_i21k(s, 'full[:25_600]') + c.evals[f'val{s:02d}'] = eval_i21k(s, 'full[25_600:51_200]') + c.evals[f'train{s:02d}'] = eval_i21k(s, 'full[51_200:76_800]') + + # Evaluations on ImageNet1k variants by label-mapping. + def eval_i1k(s, dataset, split, lblmap): + return dict( + type='classification_with_labelmap', + pred=f'predict_seqhw={s}', + data=dict(name=dataset, split=mksplit(split)), + pp_fn=pp_eval() + pp_common_i1k.format(lbl='label'), + loss_name=c.loss, + log_steps=5000, # Very fast O(seconds) so it's fine to run it often. + label_mapping=lblmap, + ) + for s in c.flexi.seqhw.v: + c.evals[f'i1k_val{s:02d}'] = eval_i1k(s, 'imagenet2012', 'validation', 'i1k_i21k') + c.evals[f'i1k_v2{s:02d}'] = eval_i1k(s, 'imagenet_v2', 'test', 'i1k_i21k') + c.evals[f'i1k_a{s:02d}'] = eval_i1k(s, 'imagenet_a', 'test', 'i1ka_i21k') + c.evals[f'i1k_r{s:02d}'] = eval_i1k(s, 'imagenet_r', 'test', 'i1kr_i21k') + c.evals[f'i1k_real{s:02d}'] = eval_i1k(s, 'imagenet2012_real', 'validation', 'i1k_i21k') + c.evals[f'i1k_real{s:02d}'].pp_fn = pp_eval() + pp_common_i1k.format(lbl='real_label') + # TODO: add objectnet. + + # Few-shot evaluators not added for overkill reasons for now. + return c diff --git a/big_vision/configs/proj/flexivit/timing.py b/big_vision/configs/proj/flexivit/timing.py new file mode 100644 index 0000000000000000000000000000000000000000..07514087765f6959309bf473a90932590346613f --- /dev/null +++ b/big_vision/configs/proj/flexivit/timing.py @@ -0,0 +1,53 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long,missing-function-docstring +r"""A config to run timing for FlexiViT (only inference, no I/O etc.). + +big_vision.tools.eval_only \ + --config big_vision/configs/proj/flexivit/timing.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 90 +""" + +from ml_collections import ConfigDict + + +def get_config(): + c = ConfigDict() + + shape = (240, 240, 3) + c.batch_size = 8 # swept + c.init_shapes = [(1, *shape)] + c.representation_layer = 'pre_logits' + + # Creating complete model using all params, the sweep will go over variants. + c.model_name = 'xp.flexivit.vit' + c.model = dict( + variant='B', + pool_type='tok', + patch_size=(10, 10), # Like deit@384 + seqhw=(24, 24), + ) + c.num_classes = 0 + + c.evals = {} + c.evals.timing = dict( + type='timing', + input_shapes=[shape], + timing=True, + pred_kw=dict(outputs=('pre_logits',)), + ) + + return c \ No newline at end of file diff --git a/big_vision/configs/proj/givt/README.md b/big_vision/configs/proj/givt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a6818a55aeb9846e3c8fe89ebcc44fcf08d51def --- /dev/null +++ b/big_vision/configs/proj/givt/README.md @@ -0,0 +1,111 @@ +# GIVT: Generative Infinite-Vocabulary Transformers + +*by Michael Tschannen, Cian Eastwood, Fabian Mentzer* [[arxiv]](https://arxiv.org/abs/2312.02116) [[colab]](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/givt/givt_demo_colab.ipynb) + +![GIVT overview](givt_overview.png) + + +### Summary + +We introduce generative infinite-vocabulary transformers (GIVT) which generate vector sequences with real-valued entries, instead of discrete tokens from a finite vocabulary. +To this end, we propose two surprisingly simple modifications to decoder-only transformers: 1) at the input, we replace the finite-vocabulary lookup table with a linear projection of the input vectors; and 2) at the output, we replace the logits prediction (usually mapped to a categorical distribution) with the parameters of a multivariate Gaussian mixture model. +Inspired by the image-generation paradigm of VQ-GAN and MaskGIT, where transformers are used to model the discrete latent sequences of a VQ-VAE, we use GIVT to model the unquantized real-valued latent sequences of a β-VAE. +In class-conditional image generation GIVT outperforms VQ-GAN (and improved variants thereof) as well as MaskGIT, and achieves performance competitive with recent latent diffusion models. +Finally, we obtain strong results outside of image generation when applying GIVT to panoptic segmentation and depth estimation with a VAE variant of the UViM framework. + +### Checkpoints + +We provide model checkpoints for a subset of the models from the paper. +These are meant as small-scale baselines for researchers interested in exploring GIVT, and are not optimized to provide the best possible visual quality (e.g. scaling the model size can substantially improve visual quality as shown in the paper). +See below for instructions to train your own models. + +**ImageNet 2012 VAEs** + +| β | 1e-5 | 2.5e-5 | 5e-5 | 1e-4 | 2e-4 | +|:-----------|:------:|:----:|:----:|:----:|:----:| +| checkpoint | [link][vae_i1k_0] | [link][vae_i1k_1] | [link][vae_i1k_2] | [link][vae_i1k_3] | [link][vae_i1k_4] | + +[vae_i1k_0]: https://storage.googleapis.com/big_vision/givt/vae_imagenet_2012_beta_1e-5_params +[vae_i1k_1]: https://storage.googleapis.com/big_vision/givt/vae_imagenet_2012_beta_2p5e-5_params +[vae_i1k_2]: https://storage.googleapis.com/big_vision/givt/vae_imagenet_2012_beta_5e-5_params +[vae_i1k_3]: https://storage.googleapis.com/big_vision/givt/vae_imagenet_2012_beta_1e-4_params +[vae_i1k_4]: https://storage.googleapis.com/big_vision/givt/vae_imagenet_2012_beta_2e-4_params + +**Class-conditional ImageNet 2012 generative models** + +| model | resolution | β | inference | FID | checkpoint | +|:------|:----------:|:------:|:-------------|:---:|:-----------| +| GIVT-Causal | 256 x 256 | 5e-5 | t=0.95, DB-CFG=0.4 | 3.35 | [link][givt_i1k_1] | +| GIVT-MaskGIT | 256 x 256 | 5e-5 | t_C=35, DB-CFG=0.1 | 4.53 | [link][givt_i1k_2] | +| GIVT-MaskGIT | 512 x 512 | 5e-5 | t_C=140 | 4.86 | [link][givt_i1k_3] | + +[givt_i1k_1]: https://storage.googleapis.com/big_vision/givt/givt_imagenet_2012_causal_params.npz +[givt_i1k_2]: https://storage.googleapis.com/big_vision/givt/givt_imagenet_2012_maskgit_params.npz +[givt_i1k_3]: https://storage.googleapis.com/big_vision/givt/givt_imagenet_2012_maskgit_512_params.npz + + +**UViM** + +| task | model | dataset | accuracy | checkpoint | +|:-----|:------|:--------|---------:|:-----------| +| Panoptic segmentation | VAE (stage 1) | [COCO (2017)] | 71.0 (PQ) | [link][vae_coco_panoptic] | +| Panoptic segmentation | GIVT (stage 2) | [COCO (2017)] | 40.2 (PQ) | [link][givt_coco_panoptic] | +| Depth estimation | VAE (stage 1) | [NYU Depth v2] | 0.195 (RMSE) | [link][vae_nyu_depth] | +| Depth estimation | GIVT (stage 2) | [NYU Depth v2] | 0.474 (RMSE) | [link][givt_nyu_depth] | + +[NYU Depth v2]: https://cs.nyu.edu/~silberman/datasets/nyu_depth_v2.html +[COCO (2017)]: https://cocodataset.org/#home +[vae_coco_panoptic]: https://storage.googleapis.com/big_vision/givt/vae_coco_panoptic_params.npz +[givt_coco_panoptic]: https://storage.googleapis.com/big_vision/givt/givt_coco_panoptic_params.npz +[vae_nyu_depth]: https://storage.googleapis.com/big_vision/givt/vae_nyu_depth_params.npz +[givt_nyu_depth]: https://storage.googleapis.com/big_vision/givt/givt_nyu_depth_params.npz + +### Training models + +This directory contains configs to train GIVT models as well as VAEs (for the UViM variants). +For training the ImageNet 2012 VAE models we used a modified version of the [MaskGIT code](https://github.com/google-research/maskgit). + +The `big_vision` input pipeline relies on [TensorFlow Datasets (TFDS)](https://www.tensorflow.org/datasets) +which supports some data sets out-of-the-box, whereas others require manual download of the data +(for example ImageNet and COCO (2017), see the `big_vision` [main README](../../../../#cloud-tpu-vm-setup) and the [UViM README](../uvim), respectively, for details). + +After setting up `big_vision` as described in the [main README](../../../../#cloud-tpu-vm-setup), training can be launched locally as follows + +``` +python -m big_vision.trainers.proj.givt.generative \ + --config big_vision/configs/proj/givt/givt_imagenet2012.py \ + --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%m-%d_%H%M'` +``` + +Add the suffix `:key1=value1,key2=value2,...` to the config path in the launch +command to modify the config with predefined arguments (see config for details). For example: +`--config big_vision/configs/proj/givt/givt_imagenet_2012.py:model_size=large`. +Note that `givt_imagenet2012.py` uses [Imagenette](https://github.com/fastai/imagenette) to ensure that the config is runnable without manual ImageNet download. +This is only meant for testing and will overfit immediately. Please download ImageNet to reproduce the paper results. + +VAE trainings for the GIVT variant of UViM can be launched as + +``` +python -m big_vision.trainers.proj.givt.vae \ + --config big_vision/configs/proj/givt/vae_nyu_depth.py \ + --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%m-%d_%H%M'` +``` + +Please refer to the [main README](../../../../#cloud-tpu-vm-setup) +for details on how to launch training on a (multi-host) TPU setup. + + +### Disclaimer + +This is not an official Google Product. + + +### Citation +``` +@article{tschannen2023givt, + title={GIVT: Generative Infinite-Vocabulary Transformers}, + author={Tschannen, Michael and Eastwood, Cian and Mentzer, Fabian}, + journal={arXiv:2312.02116}, + year={2023} +} +``` \ No newline at end of file diff --git a/big_vision/configs/proj/givt/givt_coco_panoptic.py b/big_vision/configs/proj/givt/givt_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..e89d43609d7fb317391c1e380cd9921abb2c10a8 --- /dev/null +++ b/big_vision/configs/proj/givt/givt_coco_panoptic.py @@ -0,0 +1,186 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Train a GIVT encoder-decoder model on COCO panoptic.""" + +import itertools +import ml_collections + +ConfigDict = ml_collections.ConfigDict + +VTT_MODELS = { + 'base': dict(num_layers=12, num_decoder_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768), + 'large': dict(num_layers=24, num_decoder_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024), +} + +RES = 512 +PATCH_SIZE = 16 +LABEL_RES = 512 +LABEL_PATCH_SIZE = 16 + + +def get_config(runlocal=False): + """Config for training.""" + config = ConfigDict() + + config.input = {} + config.input.pp = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({RES})|resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|' + f'copy("image", "cond_image")|copy("labels", "image")|' + f'keep("image", "cond_image")' + ) + pp_eval = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'resize({RES})|resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|' + f'copy("image", "cond_image")|copy("labels", "image")|' + f'keep("image", "cond_image")' + ) + pp_predict = ( + f'decode|resize({RES})|value_range(-1, 1)|copy("image", "cond_image")|' + f'keep("cond_image", "image/id")' # image/id used for rng seeds. + ) + + config.input.data = dict(name='coco/2017_panoptic', split='train[4096:]') + config.input.batch_size = 512 + config.input.shuffle_buffer_size = 50_000 + + config.total_epochs = 200 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + config.prefetch_to_device = 2 + config.seed = 0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.ar_generation_config = ml_collections.ConfigDict() + config.ar_generation_config.temp = 0.85 + config.ar_generation_config.temp_probs = 1.0 + config.ar_generation_config.beam_size = 4 + config.ar_generation_config.fan_size = 8 + config.ar_generation_config.rand_top_k = False + config.ar_generation_config.rand_top_k_temp = 1.0 + + config.lr = 0.001 + config.wd = 0.000001 + config.lr_mults = [ + ('pos_embedding_encoder.*', 0.1), + ('EmbedPatches.*', 0.1), + ('encoder.*', 0.1), + ('decoder.*', 1.0) + ] + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + + # Oracle section + config.vae = ConfigDict() + config.vae.model_name = 'proj.givt.vit' + config.vae.model = ConfigDict() + config.vae.model.input_size = (RES, RES) + config.vae.model.patch_size = (PATCH_SIZE, PATCH_SIZE) + config.vae.model.code_len = 256 + config.vae.model.width = 768 + config.vae.model.enc_depth = 6 + config.vae.model.dec_depth = 12 + config.vae.model.mlp_dim = 3072 + config.vae.model.num_heads = 12 + config.vae.model.codeword_dim = 16 + config.vae.model.code_dropout = 'none' + config.vae.model.bottleneck_resize = True + # values: (channel index in source image, number of classes) + config.vae.model.inout_specs = { + 'semantics': (0, 133 + 1), # +1 for void label + 'instances': (1, 100), # COCO: actually 98 train/78 validation. + } + config.vae.model_init = 'gs://big_vision/givt/vae_coco_panoptic_params.npz' + + # Model section + config.model_name = 'proj.givt.givt' + # # Base model (for exploration) + # config.model_init = {'encoder': 'howto-i21k-B/16'} + # config.model = ConfigDict(VTT_MODELS['base']) + # Large model + config.model_init = {'encoder': 'howto-i21k-L/16'} + config.model_load = dict(dont_load=('cls', 'head/bias', 'head/kernel')) + config.model = ConfigDict(VTT_MODELS['large']) + config.model.patches = (PATCH_SIZE, PATCH_SIZE) + config.model.input_size = (RES, RES) + config.model.posemb_type = 'learn' + config.model.seq_len = config.vae.model.code_len + config.model.num_labels = None + config.model.num_mixtures = 1 + config.model.fix_square_plus = True + config.model.out_dim = config.vae.model.codeword_dim + config.model.scale_tol = 1e-6 + config.model.dec_dropout_rate = 0.0 + + # Evaluation section + config.evals = {} + config.evals.val = ConfigDict() + config.evals.val.type = 'mean' + config.evals.val.pred = 'validation' + config.evals.val.data = dict(name=config.input.data.name, split='train[:4096]') + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 1000 + + config.eval_only = False + + base = { + 'type': 'proj.givt.coco_panoptic', + 'data': {**config.input.data}, + 'pp_fn': pp_predict, + 'log_steps': 10_000, + 'pred': 'sample_panoptic', + # Filters objects that occupy less than 0.03^2 fraction of all pixels. + # 'pred_kw': {'min_fraction': 0.03 ** 2}, + } + config.evals.coco_panoptic_train = dict(base) + config.evals.coco_panoptic_train.data.split = 'train[4096:8192]' + config.evals.coco_panoptic_holdout = dict(base) + config.evals.coco_panoptic_holdout.data.split = 'train[:4096]' + config.evals.coco_panoptic = dict(base) + config.evals.coco_panoptic.data.split = 'validation' + + config.evals.save_pred = dict(type='proj.givt.save_predictions') + config.evals.save_pred.pred = 'sample_panoptic' + config.evals.save_pred.pp_fn = pp_eval + config.evals.save_pred.log_steps = 100_000 + config.evals.save_pred.data = dict(config.input.data) + config.evals.save_pred.data.split = 'validation[:1024]' + config.evals.save_pred.outfile = 'inference.npz' + + if runlocal: + config.input.batch_size = 4 + config.input.shuffle_buffer_size = 10 + config.evals.val.data.split = 'train[:16]' + config.evals.val.log_steps = 20 + config.model.num_layers = 1 + config.model.num_decoder_layers = 1 + del config.model_init + config.evals.val.data.split = 'validation[:4]' + config.evals.coco_panoptic.data.split = 'validation[:4]' + config.evals.save_pred.data.split = 'validation[:4]' + for k in config.evals.keys(): + if k not in ['val', 'coco_panoptic', 'save_pred']: + del config.evals[k] + + return config diff --git a/big_vision/configs/proj/givt/givt_demo_colab.ipynb b/big_vision/configs/proj/givt/givt_demo_colab.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3e8c93004a87e42c31c02bc6fb3061413b0fd494 --- /dev/null +++ b/big_vision/configs/proj/givt/givt_demo_colab.ipynb @@ -0,0 +1,309 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# GIVT Demo colab\n", + "\n", + "[[paper]](https://arxiv.org/abs/2312.02116) [[GitHub]](https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/givt/README.md)\n", + "\n", + "This colab implements class-conditional image generation using GIVT-Causal and GIVT-MaskGIT for the 1k ImageNet2012 classes.\n", + "\n", + "The available model checkpoints are meant as small-scale baselines (~300M parameters) for researchers interested in exploring GIVT, and are not optimized to provide the best possible visual quality (e.g. scaling the model size can substantially improve visual quality as shown in the paper).\n", + "\n", + "The colab was tested with the CPU and T4 GPU runtimes. We recommend the T4 GPU runtime (the CPU rutime is very slow).\n", + "\n", + "_Disclaimer: This is not an official Google Product._" + ], + "metadata": { + "id": "botgo-GZiWI_" + } + }, + { + "cell_type": "markdown", + "source": [ + "### `big_vision` setup" + ], + "metadata": { + "id": "jQxc9UZ-mVrQ" + } + }, + { + "cell_type": "code", + "source": [ + "#@markdown Clone and set up repository\n", + "!git clone --branch=main --depth=1 https://github.com/google-research/big_vision\n", + "!cd big_vision && git pull\n", + "\n", + "# Install dependencies - pin TensorFlow-related packages to ensure compatibility\n", + "# which might not be needed in in the future\n", + "!echo -e \"keras==3.0.5\\ntensorflow==2.16.1\\ntensorflow-probability==0.24.0\" > big_vision/big_vision/constraints.txt\n", + "!pip install -r big_vision/big_vision/requirements.txt -c big_vision/big_vision/constraints.txt\n", + "%cd big_vision" + ], + "metadata": { + "id": "ZAXiVta3n2jL", + "cellView": "form" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "qYS7JNups4MU", + "cellView": "form" + }, + "outputs": [], + "source": [ + "#@markdown Imports\n", + "import jax\n", + "from functools import partial\n", + "import ml_collections\n", + "import matplotlib.pyplot as plt\n", + "\n", + "from big_vision.configs.proj.givt import givt_imagenet2012\n", + "from big_vision.datasets.imagenet import class_names as imagenet_class_names\n", + "from big_vision.models.proj.givt import givt, cnn, decode, parallel_decode\n", + "\n", + "jnp = jax.numpy" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Select and download model\n", + "\n" + ], + "metadata": { + "id": "MaCM_PIcd2Rb" + } + }, + { + "cell_type": "code", + "source": [ + "model = \"GIVT-Causal 256x256\" #@param [\"GIVT-Causal 256x256\", \"GIVT-MaskGIT 256x256\", \"GIVT-MaskGIT 512x512\"]\n", + "\n", + "givt_ckpt_path, cfg_w, temp, is_ar, res = {\n", + " \"GIVT-Causal 256x256\": (\n", + " \"gs://big_vision/givt/givt_imagenet_2012_causal_params.npz\", 0.4, 0.95, True, 256),\n", + " \"GIVT-MaskGIT 256x256\": (\n", + " \"gs://big_vision/givt/givt_imagenet_2012_maskgit_params.npz\", 0.0, 35.0, False, 256),\n", + " \"GIVT-MaskGIT 512x512\": (\n", + " \"gs://big_vision/givt/givt_imagenet_2012_maskgit_512_params.npz\", 0.0, 140.0, False, 512),\n", + "}[model]\n", + "\n", + "config = givt_imagenet2012.get_config(arg=f\"res={res},style={'ar' if is_ar else 'masked'}\")\n", + "\n", + "print(\"Loading VAE model...\")\n", + "vae_model = cnn.Model(**config.vae.model)\n", + "vae_params = cnn.load(None, config.vae.model_init, **config.vae.model_load)\n", + "\n", + "print(\"Loading GIVT model...\")\n", + "givt_model = givt.Model(**config.model)\n", + "givt_params = jax.device_put(\n", + " givt.load(None, givt_ckpt_path), jax.devices()[0])" + ], + "metadata": { + "id": "7l6QIjdyN3dg", + "cellView": "form" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### VAE encode/decode and sampling loop" + ], + "metadata": { + "id": "SUj5k1bxd6wr" + } + }, + { + "cell_type": "code", + "source": [ + "@jax.jit\n", + "def vae_encode(images, rng):\n", + " \"\"\"Encode image with VAE encoder.\"\"\"\n", + " mu, logvar = vae_model.apply(\n", + " {\"params\": vae_params}, images, method=vae_model.encode,\n", + " )\n", + " return vae_model.apply(\n", + " {\"params\": vae_params},\n", + " mu,\n", + " logvar,\n", + " method=vae_model.reparametrize,\n", + " rngs={\"dropout\": rng},\n", + " )\n", + "\n", + "@jax.jit\n", + "def vae_decode(z):\n", + " \"\"\"Reconstruct image with VAE decoder from latent code z.\"\"\"\n", + " return vae_model.apply({\"params\": vae_params}, z, method=vae_model.decode)\n", + "\n", + "### jit-compilation seems to go OOM (RAM) on the free tier GPU colab, but might\n", + "### lead to speedups on machines with more resources\n", + "# @partial(jax.jit, static_argnums=(2, 3))\n", + "def sample(labels, rng, ar_generation_config=None, masked_generation_config=None):\n", + " \"\"\"Sample from GIVT-Causal or GIVT-MaskGIT.\"\"\"\n", + " print(f\"Sampling, style={givt_model.style}\")\n", + " shared_kwargs = dict(\n", + " labels=labels,\n", + " model=givt_model,\n", + " seq_len=config.model.seq_len,\n", + " feature_dim=config.model.out_dim,\n", + " )\n", + "\n", + " match givt_model.style:\n", + " case \"ar\":\n", + " sampled_codes, _ = decode.generate(\n", + " params={\"params\": givt_params},\n", + " seed=rng,\n", + " config=dict(ar_generation_config),\n", + " **shared_kwargs,\n", + " )\n", + " info = sampled_codes\n", + " case \"masked\":\n", + " masked_out = parallel_decode.decode_masked(\n", + " rng=rng,\n", + " variables={\"params\": givt_params},\n", + " config=masked_generation_config,\n", + " **shared_kwargs,\n", + " )\n", + " sampled_codes = masked_out.current_inputs_q\n", + " info = masked_out\n", + " case _:\n", + " raise NotImplementedError\n", + " return sampled_codes, info" + ], + "metadata": { + "id": "vSn7Si2FS1zi" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "### Generate images for class label" + ], + "metadata": { + "id": "tOnWaJZVeOIX" + } + }, + { + "cell_type": "code", + "source": [ + "rng = 0 #@param = 'int'\n", + "label = 'goldfish' #@param [\"tench\", \"goldfish\", \"great white shark\", \"tiger shark\", \"hammerhead shark\", \"electric ray\", \"stingray\", \"rooster\", \"hen\", \"ostrich\", \"brambling\", \"goldfinch\", \"house finch\", \"junco\", \"indigo bunting\", \"American robin\", \"bulbul\", \"jay\", \"magpie\", \"chickadee\", \"American dipper\", \"kite (bird of prey)\", \"bald eagle\", \"vulture\", \"great grey owl\", \"fire salamander\", \"smooth newt\", \"newt\", \"spotted salamander\", \"axolotl\", \"American bullfrog\", \"tree frog\", \"tailed frog\", \"loggerhead sea turtle\", \"leatherback sea turtle\", \"mud turtle\", \"terrapin\", \"box turtle\", \"banded gecko\", \"green iguana\", \"Carolina anole\", \"desert grassland whiptail lizard\", \"agama\", \"frilled-necked lizard\", \"alligator lizard\", \"Gila monster\", \"European green lizard\", \"chameleon\", \"Komodo dragon\", \"Nile crocodile\", \"American alligator\", \"triceratops\", \"worm snake\", \"ring-necked snake\", \"eastern hog-nosed snake\", \"smooth green snake\", \"kingsnake\", \"garter snake\", \"water snake\", \"vine snake\", \"night snake\", \"boa constrictor\", \"African rock python\", \"Indian cobra\", \"green mamba\", \"sea snake\", \"Saharan horned viper\", \"eastern diamondback rattlesnake\", \"sidewinder rattlesnake\", \"trilobite\", \"harvestman\", \"scorpion\", \"yellow garden spider\", \"barn spider\", \"European garden spider\", \"southern black widow\", \"tarantula\", \"wolf spider\", \"tick\", \"centipede\", \"black grouse\", \"ptarmigan\", \"ruffed grouse\", \"prairie grouse\", \"peafowl\", \"quail\", \"partridge\", \"african grey parrot\", \"macaw\", \"sulphur-crested cockatoo\", \"lorikeet\", \"coucal\", \"bee eater\", \"hornbill\", \"hummingbird\", \"jacamar\", \"toucan\", \"duck\", \"red-breasted merganser\", \"goose\", \"black swan\", \"tusker\", \"echidna\", \"platypus\", \"wallaby\", \"koala\", \"wombat\", \"jellyfish\", \"sea anemone\", \"brain coral\", \"flatworm\", \"nematode\", \"conch\", \"snail\", \"slug\", \"sea slug\", \"chiton\", \"chambered nautilus\", \"Dungeness crab\", \"rock crab\", \"fiddler crab\", \"red king crab\", \"American lobster\", \"spiny lobster\", \"crayfish\", \"hermit crab\", \"isopod\", \"white stork\", \"black stork\", \"spoonbill\", \"flamingo\", \"little blue heron\", \"great egret\", \"bittern bird\", \"crane bird\", \"limpkin\", \"common gallinule\", \"American coot\", \"bustard\", \"ruddy turnstone\", \"dunlin\", \"common redshank\", \"dowitcher\", \"oystercatcher\", \"pelican\", \"king penguin\", \"albatross\", \"grey whale\", \"killer whale\", \"dugong\", \"sea lion\", \"Chihuahua\", \"Japanese Chin\", \"Maltese\", \"Pekingese\", \"Shih Tzu\", \"King Charles Spaniel\", \"Papillon\", \"toy terrier\", \"Rhodesian Ridgeback\", \"Afghan Hound\", \"Basset Hound\", \"Beagle\", \"Bloodhound\", \"Bluetick Coonhound\", \"Black and Tan Coonhound\", \"Treeing Walker Coonhound\", \"English foxhound\", \"Redbone Coonhound\", \"borzoi\", \"Irish Wolfhound\", \"Italian Greyhound\", \"Whippet\", \"Ibizan Hound\", \"Norwegian Elkhound\", \"Otterhound\", \"Saluki\", \"Scottish Deerhound\", \"Weimaraner\", \"Staffordshire Bull Terrier\", \"American Staffordshire Terrier\", \"Bedlington Terrier\", \"Border Terrier\", \"Kerry Blue Terrier\", \"Irish Terrier\", \"Norfolk Terrier\", \"Norwich Terrier\", \"Yorkshire Terrier\", \"Wire Fox Terrier\", \"Lakeland Terrier\", \"Sealyham Terrier\", \"Airedale Terrier\", \"Cairn Terrier\", \"Australian Terrier\", \"Dandie Dinmont Terrier\", \"Boston Terrier\", \"Miniature Schnauzer\", \"Giant Schnauzer\", \"Standard Schnauzer\", \"Scottish Terrier\", \"Tibetan Terrier\", \"Australian Silky Terrier\", \"Soft-coated Wheaten Terrier\", \"West Highland White Terrier\", \"Lhasa Apso\", \"Flat-Coated Retriever\", \"Curly-coated Retriever\", \"Golden Retriever\", \"Labrador Retriever\", \"Chesapeake Bay Retriever\", \"German Shorthaired Pointer\", \"Vizsla\", \"English Setter\", \"Irish Setter\", \"Gordon Setter\", \"Brittany dog\", \"Clumber Spaniel\", \"English Springer Spaniel\", \"Welsh Springer Spaniel\", \"Cocker Spaniel\", \"Sussex Spaniel\", \"Irish Water Spaniel\", \"Kuvasz\", \"Schipperke\", \"Groenendael dog\", \"Malinois\", \"Briard\", \"Australian Kelpie\", \"Komondor\", \"Old English Sheepdog\", \"Shetland Sheepdog\", \"collie\", \"Border Collie\", \"Bouvier des Flandres dog\", \"Rottweiler\", \"German Shepherd Dog\", \"Dobermann\", \"Miniature Pinscher\", \"Greater Swiss Mountain Dog\", \"Bernese Mountain Dog\", \"Appenzeller Sennenhund\", \"Entlebucher Sennenhund\", \"Boxer\", \"Bullmastiff\", \"Tibetan Mastiff\", \"French Bulldog\", \"Great Dane\", \"St. Bernard\", \"husky\", \"Alaskan Malamute\", \"Siberian Husky\", \"Dalmatian\", \"Affenpinscher\", \"Basenji\", \"pug\", \"Leonberger\", \"Newfoundland dog\", \"Great Pyrenees dog\", \"Samoyed\", \"Pomeranian\", \"Chow Chow\", \"Keeshond\", \"brussels griffon\", \"Pembroke Welsh Corgi\", \"Cardigan Welsh Corgi\", \"Toy Poodle\", \"Miniature Poodle\", \"Standard Poodle\", \"Mexican hairless dog (xoloitzcuintli)\", \"grey wolf\", \"Alaskan tundra wolf\", \"red wolf or maned wolf\", \"coyote\", \"dingo\", \"dhole\", \"African wild dog\", \"hyena\", \"red fox\", \"kit fox\", \"Arctic fox\", \"grey fox\", \"tabby cat\", \"tiger cat\", \"Persian cat\", \"Siamese cat\", \"Egyptian Mau\", \"cougar\", \"lynx\", \"leopard\", \"snow leopard\", \"jaguar\", \"lion\", \"tiger\", \"cheetah\", \"brown bear\", \"American black bear\", \"polar bear\", \"sloth bear\", \"mongoose\", \"meerkat\", \"tiger beetle\", \"ladybug\", \"ground beetle\", \"longhorn beetle\", \"leaf beetle\", \"dung beetle\", \"rhinoceros beetle\", \"weevil\", \"fly\", \"bee\", \"ant\", \"grasshopper\", \"cricket insect\", \"stick insect\", \"cockroach\", \"praying mantis\", \"cicada\", \"leafhopper\", \"lacewing\", \"dragonfly\", \"damselfly\", \"red admiral butterfly\", \"ringlet butterfly\", \"monarch butterfly\", \"small white butterfly\", \"sulphur butterfly\", \"gossamer-winged butterfly\", \"starfish\", \"sea urchin\", \"sea cucumber\", \"cottontail rabbit\", \"hare\", \"Angora rabbit\", \"hamster\", \"porcupine\", \"fox squirrel\", \"marmot\", \"beaver\", \"guinea pig\", \"common sorrel horse\", \"zebra\", \"pig\", \"wild boar\", \"warthog\", \"hippopotamus\", \"ox\", \"water buffalo\", \"bison\", \"ram (adult male sheep)\", \"bighorn sheep\", \"Alpine ibex\", \"hartebeest\", \"impala (antelope)\", \"gazelle\", \"arabian camel\", \"llama\", \"weasel\", \"mink\", \"European polecat\", \"black-footed ferret\", \"otter\", \"skunk\", \"badger\", \"armadillo\", \"three-toed sloth\", \"orangutan\", \"gorilla\", \"chimpanzee\", \"gibbon\", \"siamang\", \"guenon\", \"patas monkey\", \"baboon\", \"macaque\", \"langur\", \"black-and-white colobus\", \"proboscis monkey\", \"marmoset\", \"white-headed capuchin\", \"howler monkey\", \"titi monkey\", \"Geoffroy's spider monkey\", \"common squirrel monkey\", \"ring-tailed lemur\", \"indri\", \"Asian elephant\", \"African bush elephant\", \"red panda\", \"giant panda\", \"snoek fish\", \"eel\", \"silver salmon\", \"rock beauty fish\", \"clownfish\", \"sturgeon\", \"gar fish\", \"lionfish\", \"pufferfish\", \"abacus\", \"abaya\", \"academic gown\", \"accordion\", \"acoustic guitar\", \"aircraft carrier\", \"airliner\", \"airship\", \"altar\", \"ambulance\", \"amphibious vehicle\", \"analog clock\", \"apiary\", \"apron\", \"trash can\", \"assault rifle\", \"backpack\", \"bakery\", \"balance beam\", \"balloon\", \"ballpoint pen\", \"Band-Aid\", \"banjo\", \"baluster / handrail\", \"barbell\", \"barber chair\", \"barbershop\", \"barn\", \"barometer\", \"barrel\", \"wheelbarrow\", \"baseball\", \"basketball\", \"bassinet\", \"bassoon\", \"swimming cap\", \"bath towel\", \"bathtub\", \"station wagon\", \"lighthouse\", \"beaker\", \"military hat (bearskin or shako)\", \"beer bottle\", \"beer glass\", \"bell tower\", \"baby bib\", \"tandem bicycle\", \"bikini\", \"ring binder\", \"binoculars\", \"birdhouse\", \"boathouse\", \"bobsleigh\", \"bolo tie\", \"poke bonnet\", \"bookcase\", \"bookstore\", \"bottle cap\", \"hunting bow\", \"bow tie\", \"brass memorial plaque\", \"bra\", \"breakwater\", \"breastplate\", \"broom\", \"bucket\", \"buckle\", \"bulletproof vest\", \"high-speed train\", \"butcher shop\", \"taxicab\", \"cauldron\", \"candle\", \"cannon\", \"canoe\", \"can opener\", \"cardigan\", \"car mirror\", \"carousel\", \"tool kit\", \"cardboard box / carton\", \"car wheel\", \"automated teller machine\", \"cassette\", \"cassette player\", \"castle\", \"catamaran\", \"CD player\", \"cello\", \"mobile phone\", \"chain\", \"chain-link fence\", \"chain mail\", \"chainsaw\", \"storage chest\", \"chiffonier\", \"bell or wind chime\", \"china cabinet\", \"Christmas stocking\", \"church\", \"movie theater\", \"cleaver\", \"cliff dwelling\", \"cloak\", \"clogs\", \"cocktail shaker\", \"coffee mug\", \"coffeemaker\", \"spiral or coil\", \"combination lock\", \"computer keyboard\", \"candy store\", \"container ship\", \"convertible\", \"corkscrew\", \"cornet\", \"cowboy boot\", \"cowboy hat\", \"cradle\", \"construction crane\", \"crash helmet\", \"crate\", \"infant bed\", \"Crock Pot\", \"croquet ball\", \"crutch\", \"cuirass\", \"dam\", \"desk\", \"desktop computer\", \"rotary dial telephone\", \"diaper\", \"digital clock\", \"digital watch\", \"dining table\", \"dishcloth\", \"dishwasher\", \"disc brake\", \"dock\", \"dog sled\", \"dome\", \"doormat\", \"drilling rig\", \"drum\", \"drumstick\", \"dumbbell\", \"Dutch oven\", \"electric fan\", \"electric guitar\", \"electric locomotive\", \"entertainment center\", \"envelope\", \"espresso machine\", \"face powder\", \"feather boa\", \"filing cabinet\", \"fireboat\", \"fire truck\", \"fire screen\", \"flagpole\", \"flute\", \"folding chair\", \"football helmet\", \"forklift\", \"fountain\", \"fountain pen\", \"four-poster bed\", \"freight car\", \"French horn\", \"frying pan\", \"fur coat\", \"garbage truck\", \"gas mask or respirator\", \"gas pump\", \"goblet\", \"go-kart\", \"golf ball\", \"golf cart\", \"gondola\", \"gong\", \"gown\", \"grand piano\", \"greenhouse\", \"radiator grille\", \"grocery store\", \"guillotine\", \"hair clip\", \"hair spray\", \"half-track\", \"hammer\", \"hamper\", \"hair dryer\", \"hand-held computer\", \"handkerchief\", \"hard disk drive\", \"harmonica\", \"harp\", \"combine harvester\", \"hatchet\", \"holster\", \"home theater\", \"honeycomb\", \"hook\", \"hoop skirt\", \"gymnastic horizontal bar\", \"horse-drawn vehicle\", \"hourglass\", \"iPod\", \"clothes iron\", \"carved pumpkin\", \"jeans\", \"jeep\", \"T-shirt\", \"jigsaw puzzle\", \"rickshaw\", \"joystick\", \"kimono\", \"knee pad\", \"knot\", \"lab coat\", \"ladle\", \"lampshade\", \"laptop computer\", \"lawn mower\", \"lens cap\", \"letter opener\", \"library\", \"lifeboat\", \"lighter\", \"limousine\", \"ocean liner\", \"lipstick\", \"slip-on shoe\", \"lotion\", \"music speaker\", \"loupe magnifying glass\", \"sawmill\", \"magnetic compass\", \"messenger bag\", \"mailbox\", \"tights\", \"one-piece bathing suit\", \"manhole cover\", \"maraca\", \"marimba\", \"mask\", \"matchstick\", \"maypole\", \"maze\", \"measuring cup\", \"medicine cabinet\", \"megalith\", \"microphone\", \"microwave oven\", \"military uniform\", \"milk can\", \"minibus\", \"miniskirt\", \"minivan\", \"missile\", \"mitten\", \"mixing bowl\", \"mobile home\", \"ford model t\", \"modem\", \"monastery\", \"monitor\", \"moped\", \"mortar and pestle\", \"graduation cap\", \"mosque\", \"mosquito net\", \"vespa\", \"mountain bike\", \"tent\", \"computer mouse\", \"mousetrap\", \"moving van\", \"muzzle\", \"metal nail\", \"neck brace\", \"necklace\", \"baby pacifier\", \"notebook computer\", \"obelisk\", \"oboe\", \"ocarina\", \"odometer\", \"oil filter\", \"pipe organ\", \"oscilloscope\", \"overskirt\", \"bullock cart\", \"oxygen mask\", \"product packet / packaging\", \"paddle\", \"paddle wheel\", \"padlock\", \"paintbrush\", \"pajamas\", \"palace\", \"pan flute\", \"paper towel\", \"parachute\", \"parallel bars\", \"park bench\", \"parking meter\", \"railroad car\", \"patio\", \"payphone\", \"pedestal\", \"pencil case\", \"pencil sharpener\", \"perfume\", \"Petri dish\", \"photocopier\", \"plectrum\", \"Pickelhaube\", \"picket fence\", \"pickup truck\", \"pier\", \"piggy bank\", \"pill bottle\", \"pillow\", \"ping-pong ball\", \"pinwheel\", \"pirate ship\", \"drink pitcher\", \"block plane\", \"planetarium\", \"plastic bag\", \"plate rack\", \"farm plow\", \"plunger\", \"Polaroid camera\", \"pole\", \"police van\", \"poncho\", \"pool table\", \"soda bottle\", \"plant pot\", \"potter's wheel\", \"power drill\", \"prayer rug\", \"printer\", \"prison\", \"missile\", \"projector\", \"hockey puck\", \"punching bag\", \"purse\", \"quill\", \"quilt\", \"race car\", \"racket\", \"radiator\", \"radio\", \"radio telescope\", \"rain barrel\", \"recreational vehicle\", \"fishing casting reel\", \"reflex camera\", \"refrigerator\", \"remote control\", \"restaurant\", \"revolver\", \"rifle\", \"rocking chair\", \"rotisserie\", \"eraser\", \"rugby ball\", \"ruler measuring stick\", \"sneaker\", \"safe\", \"safety pin\", \"salt shaker\", \"sandal\", \"sarong\", \"saxophone\", \"scabbard\", \"weighing scale\", \"school bus\", \"schooner\", \"scoreboard\", \"CRT monitor\", \"screw\", \"screwdriver\", \"seat belt\", \"sewing machine\", \"shield\", \"shoe store\", \"shoji screen / room divider\", \"shopping basket\", \"shopping cart\", \"shovel\", \"shower cap\", \"shower curtain\", \"ski\", \"balaclava ski mask\", \"sleeping bag\", \"slide rule\", \"sliding door\", \"slot machine\", \"snorkel\", \"snowmobile\", \"snowplow\", \"soap dispenser\", \"soccer ball\", \"sock\", \"solar thermal collector\", \"sombrero\", \"soup bowl\", \"keyboard space bar\", \"space heater\", \"space shuttle\", \"spatula\", \"motorboat\", \"spider web\", \"spindle\", \"sports car\", \"spotlight\", \"stage\", \"steam locomotive\", \"through arch bridge\", \"steel drum\", \"stethoscope\", \"scarf\", \"stone wall\", \"stopwatch\", \"stove\", \"strainer\", \"tram\", \"stretcher\", \"couch\", \"stupa\", \"submarine\", \"suit\", \"sundial\", \"sunglasses\", \"sunglasses\", \"sunscreen\", \"suspension bridge\", \"mop\", \"sweatshirt\", \"swim trunks / shorts\", \"swing\", \"electrical switch\", \"syringe\", \"table lamp\", \"tank\", \"tape player\", \"teapot\", \"teddy bear\", \"television\", \"tennis ball\", \"thatched roof\", \"front curtain\", \"thimble\", \"threshing machine\", \"throne\", \"tile roof\", \"toaster\", \"tobacco shop\", \"toilet seat\", \"torch\", \"totem pole\", \"tow truck\", \"toy store\", \"tractor\", \"semi-trailer truck\", \"tray\", \"trench coat\", \"tricycle\", \"trimaran\", \"tripod\", \"triumphal arch\", \"trolleybus\", \"trombone\", \"hot tub\", \"turnstile\", \"typewriter keyboard\", \"umbrella\", \"unicycle\", \"upright piano\", \"vacuum cleaner\", \"vase\", \"vaulted or arched ceiling\", \"velvet fabric\", \"vending machine\", \"vestment\", \"viaduct\", \"violin\", \"volleyball\", \"waffle iron\", \"wall clock\", \"wallet\", \"wardrobe\", \"military aircraft\", \"sink\", \"washing machine\", \"water bottle\", \"water jug\", \"water tower\", \"whiskey jug\", \"whistle\", \"hair wig\", \"window screen\", \"window shade\", \"Windsor tie\", \"wine bottle\", \"airplane wing\", \"wok\", \"wooden spoon\", \"wool\", \"split-rail fence\", \"shipwreck\", \"sailboat\", \"yurt\", \"website\", \"comic book\", \"crossword\", \"traffic or street sign\", \"traffic light\", \"dust jacket\", \"menu\", \"plate\", \"guacamole\", \"consomme\", \"hot pot\", \"trifle\", \"ice cream\", \"popsicle\", \"baguette\", \"bagel\", \"pretzel\", \"cheeseburger\", \"hot dog\", \"mashed potatoes\", \"cabbage\", \"broccoli\", \"cauliflower\", \"zucchini\", \"spaghetti squash\", \"acorn squash\", \"butternut squash\", \"cucumber\", \"artichoke\", \"bell pepper\", \"cardoon\", \"mushroom\", \"Granny Smith apple\", \"strawberry\", \"orange\", \"lemon\", \"fig\", \"pineapple\", \"banana\", \"jackfruit\", \"cherimoya (custard apple)\", \"pomegranate\", \"hay\", \"carbonara\", \"chocolate syrup\", \"dough\", \"meatloaf\", \"pizza\", \"pot pie\", \"burrito\", \"red wine\", \"espresso\", \"tea cup\", \"eggnog\", \"mountain\", \"bubble\", \"cliff\", \"coral reef\", \"geyser\", \"lakeshore\", \"promontory\", \"sandbar\", \"beach\", \"valley\", \"volcano\", \"baseball player\", \"bridegroom\", \"scuba diver\", \"rapeseed\", \"daisy\", \"yellow lady's slipper\", \"corn\", \"acorn\", \"rose hip\", \"horse chestnut seed\", \"coral fungus\", \"agaric\", \"gyromitra\", \"stinkhorn mushroom\", \"earth star fungus\", \"hen of the woods mushroom\", \"bolete\", \"corn cob\", \"toilet paper\"]\n", + "label_int = dict(\n", + " zip(imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES,\n", + " range(len(imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES))))[label]" + ], + "metadata": { + "cellView": "form", + "id": "_CiyXD_6nQbu" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%capture --no-display\n", + "batch_size = 8\n", + "\n", + "target_labels = jnp.full((batch_size,), label_int, jnp.int32)\n", + "\n", + "if is_ar:\n", + " ar_generation_config = dict(cfg_inference_weight=cfg_w, temp=temp)\n", + " masked_generation_config = None\n", + "else:\n", + " ar_generation_config = {}\n", + " masked_generation_config = parallel_decode.MaskedGenerationConfig(\n", + " cfg_inference_weight=cfg_w,\n", + " choice_temperature = temp,\n", + " num_steps = 16,\n", + " ordering = \"maskgit\",\n", + " schedule = \"cosine\",\n", + " )\n", + "\n", + "# Sample from GIVT and decode\n", + "sampled_codes, _ = sample(\n", + " target_labels, jax.random.PRNGKey(rng),\n", + " tuple(ar_generation_config.items()), masked_generation_config)\n", + "\n", + "generated_images = vae_decode(sampled_codes)" + ], + "metadata": { + "id": "sCcGB0m1oQY1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#@markdown Visualize images\n", + "ncols = 4\n", + "nrows = generated_images.shape[0] // ncols\n", + "fig, axes = plt.subplots(nrows, ncols, figsize=(4 * ncols, 4 * nrows))\n", + "\n", + "for idx, (ax, img) in enumerate(zip(axes.flat, generated_images)):\n", + " ax.imshow(img * .5 + .5)\n", + " if idx == 0:\n", + " ax.set_title(f'Label: {label} ({label_int})', fontsize=10, ha='left', loc='left')\n", + " ax.set_axis_off()" + ], + "metadata": { + "id": "4FWgfAghuh8P", + "cellView": "form" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#@markdown Visualize latent codes\n", + "nrows = sampled_codes.shape[0]\n", + "ncols = sampled_codes.shape[-1] + 1\n", + "fig, axes = plt.subplots(nrows, ncols, figsize=(ncols, nrows))\n", + "\n", + "for r, (row_ax, code) in enumerate(zip(axes, sampled_codes)):\n", + " code_norm = (code - code.min()) / (code.max() - code.min())\n", + " for c, ax in enumerate(row_ax):\n", + " if c == 0:\n", + " cc = generated_images[r] * .5 + .5\n", + " else:\n", + " cc = code_norm[..., c - 1].reshape(res // 16, res // 16)\n", + " ax.imshow(cc)\n", + " ax.set_axis_off()" + ], + "metadata": { + "id": "zGPPeXONy0Am", + "cellView": "form" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/big_vision/configs/proj/givt/givt_imagenet2012.py b/big_vision/configs/proj/givt/givt_imagenet2012.py new file mode 100644 index 0000000000000000000000000000000000000000..84b0fd352487edbc66475ebde5883b087cf0f6ba --- /dev/null +++ b/big_vision/configs/proj/givt/givt_imagenet2012.py @@ -0,0 +1,222 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Train Generative Infinite Vocabulary Transformer (GIVT) on ImageNet. + +Example launch command (local; see main README for launching on TPU servers): + + python -m big_vision.trainers.proj.givt.generative \ + --config big_vision/configs/proj/givt/givt_imagenet2012.py \ + --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%m-%d_%H%M'` + +Add the suffix `:key1=value1,key2=value2,...` to the config path in the launch +command to modify the the config with the arguments below. For example: +`--config big_vision/configs/proj/givt/givt_imagenet_2012.py:model_size=large` +""" + +import big_vision.configs.common as bvcc +import ml_collections + + +RES = 256 +PATCH_SIZE = 16 + +GIVT_MODELS = { + 'base': dict(num_decoder_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768, dec_dropout_rate=0.1), + 'default': dict(num_decoder_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024, dec_dropout_rate=0.2), + 'large': dict(num_decoder_layers=48, num_heads=16, mlp_dim=8192, emb_dim=1536, dec_dropout_rate=0.3) +} + + +def get_config(arg=None): + """A config for training a simple VAE on imagenet2012.""" + arg = bvcc.parse_arg(arg, res=RES, patch_size=PATCH_SIZE, style='ar', # 'ar' or 'masked' + model_size='default', runlocal=False, singlehost=False, + adaptor=False) + config = ml_collections.ConfigDict() + + config.input = {} + ### Using Imagenette here to ensure this config is runnable without manual + ### download of ImageNet. This is only meant for testing and will overfit + ### immediately. Please download ImageNet to reproduce the paper results. + # config.input.data = dict(name='imagenet2012', split='train[4096:]') + config.input.data = dict(name='imagenette', split='train') + + config.input.batch_size = 8 * 1024 if not arg.runlocal else 8 + config.input.shuffle_buffer_size = 25_000 if not arg.runlocal else 10 + + config.total_epochs = 500 + + config.input.pp = ( + f'decode_jpeg_and_inception_crop({arg.res},' + f'area_min=80, area_max=100, ratio_min=1.0, ratio_max=1.0,' + f'method="bicubic", antialias=True)' + f'|flip_lr' + f'|value_range(-1, 1, key="image")' + f'|copy("label", "labels")' + f'|keep("image", "labels")') + + pp_eval = ( + f'decode' + f'|resize_small({arg.res}, inkey="image", outkey="image",' + f'method="bicubic", antialias=True)' + f'|central_crop({arg.res})' + f'|value_range(-1, 1, key="image")' + f'|copy("label", "labels")' + f'|keep("image", "labels")') + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + + # Flags for AR model. + config.ar_generation_config = ml_collections.ConfigDict() + config.ar_generation_config.temp = 0.95 + config.ar_generation_config.temp_probs = 1.0 + config.ar_generation_config.beam_size = 1 + config.ar_generation_config.fan_size = 1 + config.ar_generation_config.rand_top_k = False + config.ar_generation_config.rand_top_k_temp = 1.0 + config.ar_generation_config.cfg_inference_weight = 0.4 + + # Flags for masked model. + config.masked_generation_config = ml_collections.ConfigDict() + config.masked_generation_config.choice_temperature = 35.0 + config.masked_generation_config.ordering = 'maskgit' + config.masked_generation_config.cfg_inference_weight = 0.0 + config.masked_generation_config.schedule = 'cosine' + + # Used for eval sweep. + config.eval_only = False + + # VAE section + config.vae = {} + config.vae.model = ml_collections.ConfigDict() + config.vae.model.code_len = (arg.res // arg.patch_size) ** 2 + config.vae.model_name = 'proj.givt.cnn' + config.vae.model.codeword_dim = 16 + config.vae.model.filters = 128 + config.vae.model.num_res_blocks = 2 + config.vae.model.channel_multipliers = (1, 1, 2, 2, 4) + config.vae.model.conv_downsample = False + config.vae.model.activation_fn = 'swish' + config.vae.model.norm_type = 'GN' + if arg.model_size == 'large': + config.vae.model_init = 'gs://big_vision/givt/vae_imagenet_2012_beta_1e-5_params' + else: + config.vae.model_init = 'gs://big_vision/givt/vae_imagenet_2012_beta_5e-5_params' + config.vae.model.malib_ckpt = True + config.vae.model_load = {} + config.vae.model_load.malib_ckpt = config.vae.model.malib_ckpt + config.vae.model_load.use_ema_params = True + + # GIVT section + config.model_name = 'proj.givt.givt' + config.model_init = '' + assert arg.model_size in GIVT_MODELS, f'Unknown model size: {arg.model_size}' + config.model = ml_collections.ConfigDict(GIVT_MODELS[arg.model_size]) + config.model.num_layers = 0 + config.model.num_labels = 1000 # None + config.model.seq_len = config.vae.model.code_len + config.model.out_dim = config.vae.model.codeword_dim + config.model.num_mixtures = 16 + config.model.posemb_type = 'learn' + config.model.scale_tol = 1e-6 + config.model.style = arg.style + config.model.min_masking_rate_training = 0.3 + config.model.mask_style = 'concat' + config.model.drop_labels_probability = 0.1 + config.model.fix_square_plus = True + config.model.per_channel_mixtures = False + config.model_init = '' + # Required for model sharding + config.model.scan = True + config.model.remat_policy = 'nothing_saveable' + + # Adaptor section + config.adaptor_name = 'proj.givt.adaptor' if arg.adaptor else '' + config.adaptor = {} + config.adaptor.model = ml_collections.ConfigDict() + config.adaptor.model.num_blocks = 8 + config.adaptor.model.num_channels_bottleneck = 4 * config.model.out_dim + + config.optax_name = 'scale_by_adam' + config.optax = dict(b2=0.95) + config.grad_clip_norm = 1.0 + + # FSDP training by default + config.sharding_strategy = [('.*', 'fsdp(axis="data")')] + config.sharding_rules = [('act_batch', ('data',))] + + # Standard schedule + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = dict(decay_type='cosine', warmup_percent=0.1) + + # MaskGIT-specific parameters + if arg.style == 'masked': + config.model.dec_dropout_rate = 0.4 + config.wd = 0.0 + if arg.res == 512: + config.masked_generation_config.choice_temperature = 140 + # GIVT-Causal 512px specific parameters + elif arg.res == 512 and arg.model_size == 'large': + config.model.dec_dropout_rate = 0.1 + # Set up space-to-depth/pixel shuffle + config.vae.model.code_len //= 2 + config.vae.model.pixel_shuffle_patch_size = (1, 2) + config.model.seq_len //= 2 + config.model.out_dim = config.vae.model.codeword_dim * 2 + config.model.num_mixtures = 32 + config.adaptor.model.num_channels_bottleneck = 8 * config.model.out_dim + config.adaptor.model.pixel_shuffle_patch_size = (1, 2) + # Update sampling config + config.ar_generation_config.temp = 0.9 + config.ar_generation_config.cfg_inference_weight = 0.9 + + ### Evaluation section + config.evals = {} + config.evals.val = ml_collections.ConfigDict() + config.evals.val.type = 'mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = f'train[:{4096 if not arg.runlocal else 8}]' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 1_000 if not arg.runlocal else 20 + + config.evals.save_pred_sampling = dict( + type='proj.givt.save_predictions', + pp_fn=pp_eval, + log_steps=10_000, + pred='sample', + batch_size=512, + data=dict(name=config.input.data.name, split='validation[:512]'), + outfile='inference_sampled.npz', + ) + + config.seed = 0 + + config.ckpt_timeout = 30 + + if arg.runlocal: + config.input.batch_size = 4 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.num_decoder_layers = 2 + + config.evals.val.data.split = 'validation[:16]' + config.evals.val.log_steps = 20 + + return config diff --git a/big_vision/configs/proj/givt/givt_nyu_depth.py b/big_vision/configs/proj/givt/givt_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..31c03499498c34f8b1a947d9e491ee3c0e8164b8 --- /dev/null +++ b/big_vision/configs/proj/givt/givt_nyu_depth.py @@ -0,0 +1,198 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Train a GIVT encoder-decoder model for NYU depth prediction.""" + +import itertools +import big_vision.configs.common as bvcc +import ml_collections + +ConfigDict = ml_collections.ConfigDict + +VTT_MODELS = { + 'base': dict(num_layers=12, num_decoder_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768), + 'large': dict(num_layers=24, num_decoder_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024), +} + +RES = 512 +PATCH_SIZE = 16 +LABEL_RES = 512 +LABEL_PATCH_SIZE = 16 +QUANTIZATION_BINS = 256 +MIN_DEPTH = 0.001 +MAX_DEPTH = 10.0 + + +def get_config(arg='split=sweep'): + """Config for training.""" + arg = bvcc.parse_arg(arg, split='sweep', runlocal=False, singlehost=False) + config = ConfigDict() + + config.input = {} + config.input.pp = ( + f'decode|nyu_depth|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({RES})|' + f'resize({LABEL_RES},key="labels",method="nearest")|' + f'bin_nyu_depth(min_depth={MIN_DEPTH}, max_depth={MAX_DEPTH}, num_bins={QUANTIZATION_BINS})|' + f'value_range(-1,1)|' + f'copy("image", "cond_image")|copy("labels", "image")|' + f'keep("image", "cond_image")' + ) + pp_eval = ( + f'decode|nyu_depth|' + f'nyu_eval_crop|' + f'resize({RES})|' + f'resize({LABEL_RES},key="labels",method="nearest")|' + f'bin_nyu_depth(min_depth={MIN_DEPTH}, max_depth={MAX_DEPTH}, num_bins={QUANTIZATION_BINS})|' + f'value_range(-1,1)|' + f'copy("image", "cond_image")|copy("labels", "image")|' + f'keep("image", "cond_image")' + ) + pp_predict = ( + f'decode|nyu_depth|' + f'nyu_eval_crop|copy("labels","ground_truth")|' + f'resize({RES})|' + f'value_range(-1,1)|' + f'copy("image", "cond_image")|' + f'strong_hash(inkey="tfds_id", outkey="image/id")|' + f'keep("cond_image", "ground_truth", "image/id")' + ) + + config.input.data = dict(name='nyu_depth_v2', split='train') + config.input.batch_size = 512 + config.input.shuffle_buffer_size = 50_000 + + config.total_epochs = 50 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + config.prefetch_to_device = 2 + config.seed = 0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.ar_generation_config = ConfigDict() + config.ar_generation_config.temp = 0.9 + config.ar_generation_config.temp_probs = 1.0 + config.ar_generation_config.beam_size = 2 + config.ar_generation_config.fan_size = 8 + config.ar_generation_config.rand_top_k = False + config.ar_generation_config.rand_top_k_temp = 1.0 + + config.lr = 0.001 + config.wd = 0.000001 + config.lr_mults = [ + ('pos_embedding_encoder.*', 0.1), + ('EmbedPatches.*', 0.1), + ('encoder.*', 0.1), + ('decoder.*', 1.0) + ] + config.schedule = dict(decay_type='cosine', warmup_percent=0.1) + + # Oracle section + config.min_depth = MIN_DEPTH + config.max_depth = MAX_DEPTH + config.vae = ConfigDict() + config.vae.model_name = 'proj.givt.vit' + config.vae.model = ConfigDict() + config.vae.model.input_size = (RES, RES) + config.vae.model.patch_size = (PATCH_SIZE, PATCH_SIZE) + config.vae.model.code_len = 256 + config.vae.model.width = 768 + config.vae.model.enc_depth = 6 + config.vae.model.dec_depth = 12 + config.vae.model.mlp_dim = 3072 + config.vae.model.num_heads = 12 + config.vae.model.codeword_dim = 16 + config.vae.model.code_dropout = 'none' + config.vae.model.bottleneck_resize = True + # values: (channel index in source image, number of classes) + config.vae.model.inout_specs = { + 'depth': (0, QUANTIZATION_BINS), + } + config.vae.model_init = 'gs://big_vision/givt/vae_nyu_depth_params.npz' + + # Model section + config.model_name = 'proj.givt.givt' + # # Base model (for exploration) + # config.model_init = {'encoder': 'howto-i21k-B/16'} + # config.model = ConfigDict(VTT_MODELS['base']) + # Large model + config.model_init = {'encoder': 'howto-i21k-L/16'} + config.model_load = dict(dont_load=('cls', 'head/bias', 'head/kernel')) + config.model = ConfigDict(VTT_MODELS['large']) + config.model.patches = (PATCH_SIZE, PATCH_SIZE) + config.model.input_size = (RES, RES) + config.model.posemb_type = 'learn' + config.model.seq_len = config.vae.model.code_len + config.model.num_labels = None + config.model.num_mixtures = 1 + config.model.fix_square_plus = True + config.model.out_dim = config.vae.model.codeword_dim + config.model.scale_tol = 1e-6 + config.model.dec_dropout_rate = 0.0 + + # Evaluation section + config.evals = {} + config.evals.val = ConfigDict() + config.evals.val.type = 'mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'validation' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.givt.nyu_depth', + 'data': {**config.input.data}, + 'pp_fn': pp_predict, + 'pred': 'sample_depth', + 'log_steps': 2000, + 'min_depth': MIN_DEPTH, + 'max_depth': MAX_DEPTH, + } + + config.evals.nyu_depth_val = dict(base) + config.evals.nyu_depth_val.data.split = 'validation' + + config.evals.save_pred = dict(base) + config.evals.save_pred.type = 'proj.givt.save_predictions' + del config.evals.save_pred.min_depth, config.evals.save_pred.max_depth + config.evals.save_pred.log_steps = 100_000 + config.evals.save_pred.data.split = 'validation[:128]' + config.evals.save_pred.outfile = 'inference.npz' + + config.eval_only = False + config.seed = 0 + + if arg.runlocal: + config.input.batch_size = 4 + config.input.shuffle_buffer_size = 10 + config.evals.val.log_steps = 20 + config.evals.val.data.split = 'validation[:4]' + config.evals.nyu_depth_val.data.split = 'validation[:4]' + config.evals.save_pred.data.split = 'validation[:4]' + config.model.update(VTT_MODELS['base']) + del config.model_init + for k in config.evals.keys(): + if k not in ['val', 'nyu_depth_val', 'save_pred']: + del config.evals[k] + + return config diff --git a/big_vision/configs/proj/givt/givt_overview.png b/big_vision/configs/proj/givt/givt_overview.png new file mode 100644 index 0000000000000000000000000000000000000000..a05da7e9468a77f36eab1c2eced78c6229ceeb6e Binary files /dev/null and b/big_vision/configs/proj/givt/givt_overview.png differ diff --git a/big_vision/configs/proj/givt/vae_coco_panoptic.py b/big_vision/configs/proj/givt/vae_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..db29d6e657d955f92e38313a201f34ed682eee5d --- /dev/null +++ b/big_vision/configs/proj/givt/vae_coco_panoptic.py @@ -0,0 +1,136 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Train VAE for GIVT-based UViM COCO panoptic task. +""" + +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +def get_config(arg='res=512,patch_size=16'): + """Config for training label compression on COCO-panoptic.""" + arg = bvcc.parse_arg(arg, res=512, patch_size=16, + runlocal=False, singlehost=False) + config = mlc.ConfigDict() + + config.input = {} + config.input.data = dict(name='coco/2017_panoptic', split='train[4096:]') + + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 25_000 + + config.total_epochs = 500 + + config.input.pp = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|copy("labels","image")|keep("image")' + ) + pp_eval = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|copy("labels","image")|keep("image", "image/id")' + ) + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + + # Model section + config.model_name = 'proj.givt.vit' + config.model = mlc.ConfigDict() + config.model.input_size = (arg.res, arg.res) + config.model.patch_size = (arg.patch_size, arg.patch_size) + config.model.code_len = 256 + config.model.width = 768 + config.model.enc_depth = 6 + config.model.dec_depth = 12 + config.model.mlp_dim = 3072 + config.model.num_heads = 12 + config.model.codeword_dim = 32 + config.model.code_dropout = 'none' + config.model.bottleneck_resize = True + config.model.scan = True + config.model.remat_policy = 'nothing_saveable' + + config.rec_loss_fn = 'xent' # xent, l2 + # values: (index in source image, number of classes) + config.model.inout_specs = { + 'semantics': (0, 133 + 1), # +1 for void label + 'instances': (1, 100), # COCO: actually 98 train/78 validation. + } + + config.beta = 2.5e-4 + config.beta_percept = 0.0 + + config.optax_name = 'scale_by_adam' + config.optax = dict(b2=0.95) + config.grad_clip_norm = 1.0 + + # FSDP training by default + config.sharding_strategy = [('.*', 'fsdp(axis="data")')] + config.sharding_rules = [('act_batch', ('data',))] + + config.lr = 1e-3 + config.wd = 1e-4 + config.schedule = dict(decay_type='cosine', warmup_steps=0.1) + config.grad_clip_norm = 1.0 + + # Evaluation section + config.evals = {} + config.evals.val = mlc.ConfigDict() + config.evals.val.type = 'mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'train[:4096]' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.givt.coco_panoptic', + 'pp_fn': pp_eval, + 'log_steps': 5_000, + 'pred': 'predict_panoptic', + # Filters objects that occupy less than 0.03^2 fraction of all pixels. + # 'pred_kw': {'min_fraction': 0.03 ** 2}, + } + config.evals.coco_panoptic_train = dict(**base, data={'split': 'train[4096:8192]'}) + config.evals.coco_panoptic_holdout = dict(**base, data={'split': 'train[:4096]'}) + config.evals.coco_panoptic = dict(**base, data={'split': 'validation'}) + + config.evals.save_pred = dict(type='proj.givt.save_predictions') + config.evals.save_pred.pp_fn = pp_eval + config.evals.save_pred.log_steps = 100_000 + config.evals.save_pred.pred = 'predict_panoptic' + config.evals.save_pred.data = {**config.input.data} + config.evals.save_pred.data.split = 'validation[:1024]' + config.evals.save_pred.outfile = 'inference.npz' + + config.seed = 0 + + if arg.singlehost: + config.input.batch_size = 128 + config.num_epochs = 100 + elif arg.runlocal: + config.input.batch_size = 16 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.enc_depth = 1 + config.model.dec_depth = 1 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/givt/vae_nyu_depth.py b/big_vision/configs/proj/givt/vae_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..1f28cf5feaab113f91fd002d03157d953b377384 --- /dev/null +++ b/big_vision/configs/proj/givt/vae_nyu_depth.py @@ -0,0 +1,158 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Train VAE on NYU depth data for GIVT-based UViM. +""" + +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +QUANTIZATION_BINS = 256 +MIN_DEPTH = 0.001 +MAX_DEPTH = 10.0 + + +def get_config(arg='res=512,patch_size=16'): + """Config for training label compression on NYU depth.""" + arg = bvcc.parse_arg(arg, res=512, patch_size=16, + runlocal=False, singlehost=False) + config = mlc.ConfigDict() + + config.input = {} + config.input.data = dict(name='nyu_depth_v2', split='train') + + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 25_000 + + config.total_epochs = 200 + + config.input.pp = ( + f'decode|nyu_depth|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'bin_nyu_depth(min_depth={MIN_DEPTH}, max_depth={MAX_DEPTH}, num_bins={QUANTIZATION_BINS})|' + f'value_range(-1, 1)|copy("labels", "image")|keep("image")' + ) + pp_eval = ( + f'decode|nyu_depth|nyu_eval_crop|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'bin_nyu_depth(min_depth={MIN_DEPTH}, max_depth={MAX_DEPTH}, num_bins={QUANTIZATION_BINS})|' + f'value_range(-1, 1)|copy("labels", "image")|keep("image")' + ) + pp_pred = ( + f'decode|nyu_depth|nyu_eval_crop|copy("labels","ground_truth")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'bin_nyu_depth(min_depth={MIN_DEPTH}, max_depth={MAX_DEPTH}, num_bins={QUANTIZATION_BINS})|' + f'value_range(-1, 1)|copy("labels", "image")|' + f'keep("image", "ground_truth")' + ) + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + + # Model section + config.min_depth = MIN_DEPTH + config.max_depth = MAX_DEPTH + config.model_name = 'proj.givt.vit' + config.model = mlc.ConfigDict() + config.model.input_size = (arg.res, arg.res) + config.model.patch_size = (arg.patch_size, arg.patch_size) + config.model.code_len = 256 + config.model.width = 768 + config.model.enc_depth = 6 + config.model.dec_depth = 12 + config.model.mlp_dim = 3072 + config.model.num_heads = 12 + config.model.codeword_dim = 16 + config.model.code_dropout = 'none' + config.model.bottleneck_resize = True + config.model.scan = True + config.model.remat_policy = 'nothing_saveable' + config.model_init = '' + + config.rec_loss_fn = 'xent' # xent, l2 + config.mask_zero_target = True + # values: (index in source image, number of classes) + config.model.inout_specs = { + 'depth': (0, QUANTIZATION_BINS), + } + + config.beta = 2e-4 + config.beta_percept = 0.0 + + # Optimizer section + config.optax_name = 'scale_by_adam' + config.optax = dict(b2=0.95) + + # FSDP training by default + config.sharding_strategy = [('.*', 'fsdp(axis="data")')] + config.sharding_rules = [('act_batch', ('data',))] + + config.lr = 1e-3 + config.wd = 1e-4 + config.schedule = dict(decay_type='cosine', warmup_steps=0.1) + config.grad_clip_norm = 1.0 + + # Evaluation section + config.evals = {} + config.evals.val = mlc.ConfigDict() + config.evals.val.type = 'mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'validation' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.givt.nyu_depth', + 'data': {**config.input.data}, + 'pp_fn': pp_pred, + 'pred': 'predict_depth', + 'log_steps': 2000, + 'min_depth': MIN_DEPTH, + 'max_depth': MAX_DEPTH, + } + config.evals.nyu_depth_val = {**base} + config.evals.nyu_depth_val.data.split = 'validation' + + # ### Uses a lot of memory + # config.evals.save_pred = dict(type='proj.givt.save_predictions') + # config.evals.save_pred.pp_fn = pp_eval + # config.evals.save_pred.log_steps = 100_000 + # config.evals.save_pred.data = {**config.input.data} + # config.evals.save_pred.data.split = 'validation[:64]' + # config.evals.save_pred.batch_size = 64 + # config.evals.save_pred.outfile = 'inference.npz' + + config.eval_only = False + config.seed = 0 + + if arg.singlehost: + config.input.batch_size = 128 + config.num_epochs = 50 + elif arg.runlocal: + config.input.batch_size = 16 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.enc_depth = 1 + config.model.dec_depth = 1 + config.evals.val.data.split = 'validation[:16]' + config.evals.val.log_steps = 20 + config.evals.nyu_depth_val.data.split = 'validation[:16]' + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py b/big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py new file mode 100644 index 0000000000000000000000000000000000000000..90ac3ee1e0c9786af4e708aa5b68a31a869c22a9 --- /dev/null +++ b/big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py @@ -0,0 +1,134 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training ViT on ILSVRC-2012 with GSAM in https://arxiv.org/abs/2203.08065 + +Run training of a B/32 model: + +big_vision.trainers.proj.gsam.train \ + --config big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` + +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + +def get_config(arg=None): + """Config for training.""" + arg = bvcc.parse_arg(arg, variant='B/32', runlocal=False) + config = mlc.ConfigDict() + + config.dataset = 'imagenet2012' + config.train_split = 'train[:99%]' + config.cache_raw = not arg.runlocal # Needs up to 120GB of RAM! + config.shuffle_buffer_size = 250_000 # Per host, so small-ish is ok. + config.num_classes = 1000 + config.loss = 'sigmoid_xent' + config.batch_size = 4096 + config.num_epochs = 300 + + pp_common = ( + '|value_range(-1, 1)' + '|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ) + config.pp_train = ( + 'decode_jpeg_and_inception_crop(224)|flip_lr|' + + pp_common.format(lbl='label') + ) + pp = 'decode|resize_small(256)|central_crop(224)' + pp_common + + # Aggressive pre-fetching because our models here are small, so we not only + # can afford it, but we also need it for the smallest models to not be + # bottle-necked by the input pipeline. Play around with it for -L models tho. + config.prefetch_to_host = 8 + config.prefetch_to_device = 4 + + config.log_training_steps = 50 + config.checkpoint_steps = 1000 + + # Model section + config.model_name = 'vit' + config.model = dict( + variant=arg.variant, + rep_size=False, + pool_type='gap', + ) + config.init_head_bias = -10.0 + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='float32') + # The modified AdaFactor we introduced in https://arxiv.org/abs/2106.04560 + # almost always behaves exactly like adam, but at a fraction of the memory + # cost (specifically, adam_bf16 = +1.5M, adafactor = +0.5M), hence it is a + # good idea to try it when you are memory-bound! + # config.optax_name = 'big_vision.scale_by_adafactor' + # A good flag to play with when hitting instabilities, is the following: + # config.optax = dict(beta2_cap=0.95) + + config.lr = 0.003 + config.wd = 0.001 # default is 0.0001; paper used 0.3, effective wd=0.3*lr + config.schedule = dict( + warmup_steps=10_000, + decay_type='linear', + linear_end=0.01, + ) + + # GSAM settings. + # Note: when rho_max=rho_min and alpha=0, GSAM reduces to SAM. + config.gsam = dict( + rho_max=0.6, + rho_min=0.1, + alpha=0.6, + lr_max=config.get_ref('lr'), + lr_min=config.schedule.get_ref('linear_end') * config.get_ref('lr'), + ) + + # Eval section + eval_common = dict( + type='classification', + dataset='imagenet2012', + pp_fn=pp.format(lbl='label'), + loss_name=config.loss, + log_steps=2500, # Very fast O(seconds) so it's fine to run it often. + ) + config.evals = {} + config.evals.train = {**eval_common, 'split': 'train[:2%]'} + config.evals.minival = {**eval_common, 'split': 'train[99%:]'} + config.evals.val = {**eval_common, 'split': 'validation'} + config.evals.v2 = {**eval_common, 'dataset': 'imagenet_v2', 'split': 'test'} + + config.evals.real = {**eval_common} + config.evals.real.dataset = 'imagenet2012_real' + config.evals.real.split = 'validation' + config.evals.real.pp_fn = pp.format(lbl='real_label') + + config.fewshot = get_fewshot_lsr(runlocal=arg.runlocal) + config.fewshot.log_steps = 10_000 + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.shuffle_buffer_size = 10 + config.batch_size = 8 + config.minival.split = 'train[:16]' + config.val.split = 'validation[:16]' + config.real.split = 'validation[:16]' + config.v2.split = 'test[:16]' + + return config diff --git a/big_vision/configs/proj/image_text/README.md b/big_vision/configs/proj/image_text/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ec1381db4fb7b49e7179624bb448a34a2c7524ba --- /dev/null +++ b/big_vision/configs/proj/image_text/README.md @@ -0,0 +1,65 @@ +# Image/text models + +## LiT: Zero-Shot Transfer with Locked-image text Tuning + +*by Xiaohua Zhai, Xiao Wang, Basil Mustafa, Andreas Steiner, Daniel Keysers, Alexander Kolesnikov, Lucas Beyer* + +https://arxiv.org/abs/2111.07991 + +``` +@article{zhai2022lit, + title={LiT: Zero-Shot Transfer with Locked-image Text Tuning}, + author={Zhai, Xiaohua and Wang, Xiao and Mustafa, Basil and Steiner, Andreas and Keysers, Daniel and Kolesnikov, Alexander and Beyer, Lucas}, + journal={CVPR}, + year={2022} +} +``` + +Model card: +https://github.com/google-research/vision_transformer/blob/main/model_cards/lit.md + +Colabs: + +- https://colab.research.google.com/github/google-research/vision_transformer/blob/main/lit.ipynb +- https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/lit.ipynb + +### Results + +| Model | Download link | ImageNet 0-shot | MS-COCO I→T | MS-COCO T→I | Config `arg` | +| :--- | :---: | :---: | :---: | :---: | :--- | +| mixed_L16L | [link](https://storage.googleapis.com/vit_models/lit/LiT-L16L.npz) | 75.7 | 48.5 | 31.2 | `txt=bert_large,img=L/16` | +| mixed_B16B | [link](https://storage.googleapis.com/vit_models/lit/LiT-B16B.npz) | 72.1 | 49.4 | 31.1 | `txt=bert_base,img=B/16,img_head` | +| mixed_B16B_2 | [link](https://storage.googleapis.com/vit_models/lit/LiT-B16B.npz) | 73.9 | 51.5 | 31.8 | `txt=bert_base,img=B/16` | +| coco_B16B | [link](https://storage.googleapis.com/vit_models/lit/big_vision/coco_B16B/checkpoint.npz) | 20.7 | 47.2 | 32.1 | `txt=bert_base,img=B/16` | + +The first three rows are the best available models trained on open source data, +originally published in the [`google-research/vision_transformer`] repository. +These models were re-evaluated with this codebase using the following commands: + +```bash +big_vision.tools.eval_only --config big_vision/configs/proj/image_text/lit_coco.py:txt=bert_base,img=B/16,img_head,init=gs://vit_models/lit/LiT-B16B.npz + +big_vision.tools.eval_only --config big_vision/configs/proj/image_text/lit_coco.py:txt=bert_base,img=B/16_2,init=gs://vit_models/lit/LiT-B16B_2.npz + +big_vision.tools.eval_only --config big_vision/configs/proj/image_text/lit_coco.py:txt=bert_large,img=L/16,init=gs://vit_models/lit/LiT-L16L.npz +``` + +Unfortunately, the public multi-modal datasets [`CC12M`] and [`YFCC100M`] are +not yet available in [`tfds`], so these models cannot be reproduced with the +codebase. For this reason we provide the much weaker model `coco_B16B` in the +third row, which was trained on the small `tfds` dataset [`coco_captions`], and +can be used to verify correctness of the codebase +([workdir](https://console.cloud.google.com/storage/browser/vit_models/lit/big_vision/coco_B16B/)). + +[`google-research/vision_transformer`]: https://github.com/google-research/vision_transformer +[`CC12M`]: https://arxiv.org/abs/2102.08981 +[`YFCC100M`]: https://arxiv.org/abs/1503.01817 +[`tfds`]: https://www.tensorflow.org/datasets/api_docs/python/tfds +[`coco_captions`]: https://www.tensorflow.org/datasets/catalog/coco_captions + + +### Changelog + +- 2022-08-18: Added LiT-B16B_2 model that was trained for 60k steps + (LiT_B16B: 30k) without linear head on the image side (LiT_B16B: 768) and has + better performance. diff --git a/big_vision/configs/proj/image_text/SigLIP_demo.ipynb b/big_vision/configs/proj/image_text/SigLIP_demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..120f447f27a662e747fafe74e6015bb0c2500d9b --- /dev/null +++ b/big_vision/configs/proj/image_text/SigLIP_demo.ipynb @@ -0,0 +1,1022 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# General information\n", + "\n", + "Example colab for SigLIP models described in [the SigLIP paper](https://arxiv.org/abs/2303.15343).\n", + "\n", + "**These models are not official Google products and were trained and released for research purposes.**\n", + "\n", + "If you find our model(s) useful for your research, consider citing\n", + "\n", + "```\n", + "@article{zhai2023sigmoid,\n", + " title={Sigmoid loss for language image pre-training},\n", + " author={Zhai, Xiaohua and Mustafa, Basil and Kolesnikov, Alexander and Beyer, Lucas},\n", + " journal={International Conference on Computer Vision ({ICCV})},\n", + " year={2023}\n", + "}\n", + "```\n", + "\n", + "If you use our released models in your products, we will appreciate any direct feedback. We are reachable by xzhai@google.com, basilm@google.com, akolesnikov@google.com and lbeyer@google.com.\n", + "\n", + "\n", + "Only the models explicitly marked with `i18n` in the name are expected to perform reasonably well on non-english data." + ], + "metadata": { + "id": "wR53lePHuiP-" + } + }, + { + "cell_type": "code", + "source": [ + "#@markdown # Environment setup\n", + "#@markdown **IMPORTANT NOTE**: Modern jax (>0.4) does not support the Colab TPU\n", + "#@markdown anymore, so don't select TPU runtime here. CPU and GPU work and are both fast enough.\n", + "\n", + "# Install the right jax version for TPU/GPU/CPU\n", + "import os\n", + "if 'COLAB_TPU_ADDR' in os.environ:\n", + " raise \"TPU colab not supported.\"\n", + "elif 'NVIDIA_PRODUCT_NAME' in os.environ:\n", + " !nvidia-smi\n", + "import jax\n", + "jax.devices()\n", + "\n", + "\n", + "# Get latest version of big_vision codebase.\n", + "!git clone --quiet --branch=main --depth=1 https://github.com/google-research/big_vision\n", + "!cd big_vision && git pull --rebase --quiet\n", + "!pip -q install -r big_vision/big_vision/requirements.txt\n", + "# Gives us ~2x faster gsutil cp to get the model checkpoints.\n", + "!pip3 -q install --no-cache-dir -U crcmod\n", + "\n", + "%cd big_vision\n", + "\n", + "\n", + "import numpy as np\n", + "import matplotlib as mpl\n", + "import matplotlib.pyplot as plt\n", + "\n", + "%matplotlib inline\n", + "%config InlineBackend.figure_format = 'retina'\n", + "\n", + "import jax\n", + "import jax.numpy as jnp\n", + "import ml_collections\n", + "\n", + "from google.colab.output import _publish as publish" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kXSdSXVg2PAI", + "outputId": "ba908946-0cd3-4468-9034-cd108529986f", + "cellView": "form" + }, + "execution_count": 1, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Thu Sep 28 09:08:47 2023 \n", + "+-----------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 525.105.17 Driver Version: 525.105.17 CUDA Version: 12.0 |\n", + "|-------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|===============================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 75C P8 14W / 70W | 0MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-------------------------------+----------------------+----------------------+\n", + " \n", + "+-----------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=============================================================================|\n", + "| No running processes found |\n", + "+-----------------------------------------------------------------------------+\n", + "fatal: destination path 'big_vision' already exists and is not an empty directory.\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n", + " Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "/content/big_vision\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Choose and load model, perform inference" + ], + "metadata": { + "id": "byHpmgAO6inM" + } + }, + { + "cell_type": "code", + "source": [ + "# Pick your hero: (WHEN CHANGING THIS, RERUN IMAGE/TEXT EMBEDDING CELLS)\n", + "# Give this cell 1-3mins.\n", + "\n", + "# VARIANT, RES = 'B/16', 224\n", + "# VARIANT, RES = 'B/16', 256\n", + "# VARIANT, RES = 'B/16', 384\n", + "# VARIANT, RES = 'B/16', 512\n", + "# VARIANT, RES = 'L/16', 256\n", + "VARIANT, RES = 'L/16', 384\n", + "# VARIANT, RES = 'So400m/14', 224\n", + "# VARIANT, RES = 'So400m/14', 384\n", + "# VARIANT, RES = 'B/16-i18n', 256\n", + "\n", + "CKPT, TXTVARIANT, EMBDIM, SEQLEN, VOCAB = {\n", + " ('B/16', 224): ('webli_en_b16_224_63724782.npz', 'B', 768, 64, 32_000),\n", + " ('B/16', 256): ('webli_en_b16_256_60500360.npz', 'B', 768, 64, 32_000),\n", + " ('B/16', 384): ('webli_en_b16_384_68578854.npz', 'B', 768, 64, 32_000),\n", + " ('B/16', 512): ('webli_en_b16_512_68580893.npz', 'B', 768, 64, 32_000),\n", + " ('L/16', 256): ('webli_en_l16_256_60552751.npz', 'L', 1024, 64, 32_000),\n", + " ('L/16', 384): ('webli_en_l16_384_63634585.npz', 'L', 1024, 64, 32_000),\n", + " ('So400m/14', 224): ('webli_en_so400m_224_57633886.npz', 'So400m', 1152, 16, 32_000),\n", + " ('So400m/14', 384): ('webli_en_so400m_384_58765454.npz', 'So400m', 1152, 64, 32_000),\n", + " ('B/16-i18n', 256): ('webli_i18n_b16_256_66117334.npz', 'B', 768, 64, 250_000),\n", + " ('So400m/16', 256): ('webli_i18n_so400m_16_256_78061115.npz', 'So400m', 1152, 64, 250_000),\n", + "}[VARIANT, RES]\n", + "\n", + "# It is significantly faster to first copy the checkpoint (30s vs 8m30 for B and 1m vs ??? for L)\n", + "!test -f /tmp/{CKPT} || gsutil cp gs://big_vision/siglip/{CKPT} /tmp/\n", + "\n", + "if VARIANT.endswith('-i18n'):\n", + " VARIANT = VARIANT[:-len('-i18n')]\n", + "\n", + "import big_vision.models.proj.image_text.two_towers as model_mod\n", + "\n", + "model_cfg = ml_collections.ConfigDict()\n", + "model_cfg.image_model = 'vit' # TODO(lbeyer): remove later, default\n", + "model_cfg.text_model = 'proj.image_text.text_transformer' # TODO(lbeyer): remove later, default\n", + "model_cfg.image = dict(variant=VARIANT, pool_type='map')\n", + "model_cfg.text = dict(variant=TXTVARIANT, vocab_size=VOCAB)\n", + "model_cfg.out_dim = (None, EMBDIM) # (image_out_dim, text_out_dim)\n", + "model_cfg.bias_init = -10.0\n", + "model_cfg.temperature_init = 10.0\n", + "\n", + "model = model_mod.Model(**model_cfg)\n", + "\n", + "# Using `init_params` is slower but will lead to `load` below performing sanity-checks.\n", + "# init_params = jax.jit(model.init, backend=\"cpu\")(jax.random.PRNGKey(42), jnp.zeros([1, RES, RES, 3], jnp.float32), jnp.zeros([1, SEQLEN], jnp.int32))['params']\n", + "init_params = None # Faster but bypasses loading sanity-checks.\n", + "\n", + "params = model_mod.load(init_params, f'/tmp/{CKPT}', model_cfg)" + ], + "metadata": { + "id": "0DsOabGD7MRG", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "5afc9f52-7eb4-4a0d-b681-3ab5945ce9b4" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Copying gs://big_vision/siglip/webli_i18n_b16_256_66117334.npz...\n", + "- [1 files][ 1.3 GiB/ 1.3 GiB] 45.3 MiB/s \n", + "Operation completed over 1 objects/1.3 GiB. \n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title Load and embed images\n", + "\n", + "import big_vision.pp.builder as pp_builder\n", + "import big_vision.pp.ops_general\n", + "import big_vision.pp.ops_image\n", + "import big_vision.pp.ops_text\n", + "import PIL\n", + "\n", + "!wget -q https://cdn.openai.com/multimodal-neurons/assets/apple/apple-ipod.jpg\n", + "!wget -q https://cdn.openai.com/multimodal-neurons/assets/apple/apple-blank.jpg\n", + "!wget -q 'https://images.unsplash.com/photo-1566467021888-b03548769dd1?ixlib=rb-4.0.3&q=85&fm=jpg&crop=entropy&cs=srgb&dl=svetlana-gumerova-hQHm2D1fH70-unsplash.jpg&w=640' -O cold_drink.jpg\n", + "!wget -q 'https://images.rawpixel.com/image_1300/czNmcy1wcml2YXRlL3Jhd3BpeGVsX2ltYWdlcy93ZWJzaXRlX2NvbnRlbnQvbHIvdXB3azU4ODU5NzY1LXdpa2ltZWRpYS1pbWFnZS1rb3diMmhkeC5qcGc.jpg' -O hot_drink.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/authors.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/siglip.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/caffeine.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/robosign.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/fried_fish.jpeg\n", + "!wget -q 'https://pbs.twimg.com/media/FTyEyxyXsAAyKPc?format=jpg&name=small' -O cow_beach.jpg\n", + "!wget -q 'https://storage.googleapis.com/big_vision/siglip/cow_beach2.jpg' -O cow_beach2.jpg\n", + "!wget -q 'https://pbs.twimg.com/media/Frb6NIEXwAA8-fI?format=jpg&name=medium' -O mountain_view.jpg\n", + "\n", + "\n", + "images = [PIL.Image.open(fname) for fname in [\n", + " 'apple-ipod.jpg',\n", + " 'apple-blank.jpg',\n", + " 'cold_drink.jpg',\n", + " 'hot_drink.jpg',\n", + " 'caffeine.jpg',\n", + " 'siglip.jpg',\n", + " 'authors.jpg',\n", + " 'robosign.jpg',\n", + " 'cow_beach.jpg',\n", + " 'cow_beach2.jpg',\n", + " 'mountain_view.jpg',\n", + "]]\n", + "\n", + "pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)')\n", + "imgs = np.array([pp_img({'image': np.array(image)})['image'] for image in images])\n", + "zimg, _, out = model.apply({'params': params}, imgs, None)\n", + "\n", + "print(imgs.shape, zimg.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xmuXfCfBjgeF", + "outputId": "3627819b-007e-4107-e1f4-06b7ad3ac03a" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(11, 384, 384, 3) (11, 1024)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title Tokenize and embed texts\n", + "\n", + "texts = [\n", + " 'an apple',\n", + " 'a picture of an apple',\n", + " 'an ipod',\n", + " 'granny smith',\n", + " 'an apple with a note saying \"ipod\"',\n", + " 'a cold drink on a hot day',\n", + " 'a hot drink on a cold day',\n", + " 'a photo of a cold drink on a hot day',\n", + " 'a photo of a hot drink on a cold day',\n", + " #\n", + " 'a photo of two guys in need of caffeine',\n", + " 'a photo of two guys in need of water',\n", + " 'a photo of the SigLIP authors',\n", + " 'a photo of a rock band',\n", + " 'a photo of researchers at Google Brain',\n", + " 'a photo of researchers at OpenAI',\n", + " #\n", + " 'a robot on a sign',\n", + " 'a photo of a robot on a sign',\n", + " 'an empty street',\n", + " 'autumn in Toronto',\n", + " 'a photo of autumn in Toronto',\n", + " 'a photo of Toronto in autumn',\n", + " 'a photo of Toronto in summer',\n", + " 'autumn in Singapore',\n", + " #\n", + " 'cow',\n", + " 'a cow in a tuxedo',\n", + " 'a cow on the beach',\n", + " 'a cow in the prairie',\n", + " #\n", + " 'the real mountain view',\n", + " 'Zürich',\n", + " 'San Francisco',\n", + " 'a picture of a laptop with the lockscreen on, a cup of cappucino, salt and pepper grinders. The view through the window reveals lake Zürich and the Alps in the background of the city.',\n", + "]\n", + "\n", + "TOKENIZERS = {\n", + " 32_000: 'c4_en',\n", + " 250_000: 'mc4',\n", + "}\n", + "pp_txt = pp_builder.get_preprocess_fn(f'tokenize(max_len={SEQLEN}, model=\"{TOKENIZERS[VOCAB]}\", eos=\"sticky\", pad_value=1, inkey=\"text\")')\n", + "txts = np.array([pp_txt({'text': text})['labels'] for text in texts])\n", + "_, ztxt, out = model.apply({'params': params}, None, txts)\n", + "\n", + "print(txts.shape, ztxt.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KGrpkRTtjU-L", + "outputId": "7c43b56e-cd53-4801-b1e3-66774368a1d2" + }, + "execution_count": 11, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(31, 64) (31, 1024)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# This is how to get all probabilities:\n", + "print(f\"Learned temperature {out['t'].item():.1f}, learned bias: {out['b'].item():.1f}\")\n", + "probs = jax.nn.sigmoid(zimg @ ztxt.T * out['t'] + out['b'])\n", + "print(f\"{probs[0][0]:.1%} that image 0 is '{texts[0]}'\")\n", + "print(f\"{probs[0][1]:.1%} that image 0 is '{texts[1]}'\")" + ], + "metadata": { + "id": "TIdAVw9VGEAw", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "22fc0d9a-8986-4679-ca89-6e4330a55c6e" + }, + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Learned temperature 118.2, learned bias: -12.7\n", + "10.4% that image 0 is 'an apple'\n", + "42.8% that image 0 is 'a picture of an apple'\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# @title Pretty demo (code)\n", + "from IPython.display import Javascript\n", + "\n", + "DEMO_IMG_SIZE = 96\n", + "\n", + "import base64\n", + "import io\n", + "\n", + "def bv2rgb(bv_img):\n", + " return (bv_img * 127.5 + 127.5).astype(np.uint8)\n", + "\n", + "def html_img(*, enc_img=None, pixels=None, id=None, size=100, max_size=None, max_height=None, style=\"\"):\n", + " if enc_img is None and pixels is not None:\n", + " with io.BytesIO() as buf:\n", + " PIL.Image.fromarray(np.asarray(pixels)).save(buf, format=\"JPEG\")\n", + " enc_img = buf.getvalue()\n", + "\n", + " img_data = base64.b64encode(np.ascontiguousarray(enc_img)).decode('ascii')\n", + "\n", + " id_spec = f'id={id}' if id else ''\n", + " if size is not None:\n", + " style_spec = f'style=\"{style}; width: {size}px; height: {size}px\"'\n", + " elif max_size is not None:\n", + " style_spec = f'style=\"{style}; width: auto; height: auto; max-width: {max_size}px; max-height: {max_size}px;\"'\n", + " elif max_height is not None:\n", + " style_spec = f'style=\"{style}; object-fit: cover; width: auto; height: {max_height}px;\"'\n", + " else: style_spec = ''\n", + "\n", + " return f''\n", + "\n", + "\n", + "def make_table(zimg, ztxt, out):\n", + " # The default learnable bias is a little conservative. Play around with it!\n", + " t, b = out['t'].item(), out['b'].item()\n", + " tempered_logits = zimg @ ztxt.T * t\n", + " probs = 1 / (1 + np.exp(-tempered_logits - b))\n", + " publish.javascript(f\"var logits = {tempered_logits.tolist()};\")\n", + "\n", + " def color(p):\n", + " return mpl.colors.rgb2hex(mpl.cm.Greens(p / 2)) if p >= 0.01 else \"transparent\"\n", + "\n", + " publish.javascript(f\"var cmap = {[color(x) for x in np.linspace(0, 1, 50)]};\")\n", + " def cell(x, iimg, itxt):\n", + " return f\"
{x * 100:>4.0f}%
\"\n", + "\n", + " html = f'''\n", + "

\n", + " \n", + " \n", + " \n", + "

\n", + " '''\n", + "\n", + " html += \"\\n\"\n", + " html += \"\"\n", + " html += \"\".join([f\"\" + \"\".join([cell(probs[iimg, itxt], iimg, itxt) for iimg in range(len(imgs))]) + f\"
\" + html_img(pixels=bv2rgb(img), size=DEMO_IMG_SIZE) for img in imgs])\n", + " html += \"\"\n", + " for itxt, txt in enumerate(texts):\n", + " html += f\"
{txt}\"\n", + "\n", + " publish.css(r\"\"\"\n", + " table {\n", + " border-collapse: collapse;\n", + " }\n", + "\n", + " tr {\n", + " border: 1px transparent;\n", + " }\n", + "\n", + " tr:nth-child(odd) {\n", + " background-color: #F5F5F5;\n", + " }\n", + "\n", + " tr:hover {\n", + " background-color: lightyellow;\n", + " border: 1px solid black;\n", + " }\n", + "\n", + " td.pct {\n", + " text-align: center;\n", + " }\n", + " \"\"\")\n", + " publish.html(html)\n", + "\n", + " # JS code to compute and write all probs from the logits.\n", + " display(Javascript('''\n", + " function update(b) {\n", + " for(var iimg = 0; iimg < logits.length; iimg++) {\n", + " for(var itxt = 0; itxt < logits[iimg].length; itxt++) {\n", + " const el = document.getElementById(`p_${iimg}_${itxt}`);\n", + " const p = Math.round(100 / (1 + Math.exp(-logits[iimg][itxt] - b)));\n", + " const pad = p < 10.0 ? ' ' : p < 100.0 ? ' ' : ''\n", + " el.innerHTML = pad + (p).toFixed(0) + '%';\n", + "\n", + " const td = document.getElementById(`td_${iimg}_${itxt}`);\n", + " const c = cmap[Math.round(p / 100 * (cmap.length - 1))];\n", + " td.style.backgroundColor = c;\n", + " }\n", + " }\n", + " }\n", + " '''))\n", + "\n", + " # JS code to connect the bias value slider\n", + " display(Javascript('''\n", + " const value = document.querySelector(\"#value\");\n", + " const input = document.querySelector(\"#b\");\n", + " value.textContent = input.value;\n", + " input.addEventListener(\"input\", (event) => {\n", + " value.textContent = event.target.value;\n", + " update(event.target.value);\n", + " });\n", + " '''))\n", + "\n", + " # Make the cell output as large as the table to avoid annoying scrollbars.\n", + " display(Javascript(f'update({b})'))\n", + " display(Javascript('google.colab.output.resizeIframeToContent()'))" + ], + "metadata": { + "cellView": "form", + "id": "eolOc7vd_ZSj" + }, + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "make_table(zimg, ztxt, out)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 767 + }, + "id": "mt5BIywzzA6c", + "outputId": "3b06cfb9-a3da-42d7-8caf-d5366d058f8b" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "var logits = [[10.509522438049316, 12.372017860412598, 13.07434368133545, 9.578202247619629, 21.19094467163086, 1.310517430305481, 1.2763848304748535, 3.0990359783172607, 2.360225200653076, -3.670855760574341, -4.780072212219238, -1.4530967473983765, -3.3108861446380615, -3.8945610523223877, -4.378420829772949, 0.35140618681907654, 2.7228779792785645, -6.806382656097412, -3.9012961387634277, -1.7843879461288452, -4.578653812408447, -7.306142807006836, -1.253274917602539, -1.8402824401855469, -6.329799175262451, -9.506726264953613, -5.78713846206665, -1.6370103359222412, -9.404793739318848, -4.342881202697754, -13.128281593322754], [12.365941047668457, 13.45022964477539, 0.9843839406967163, 12.809731483459473, 6.767915725708008, 2.808335304260254, 1.050551414489746, 3.6161491870880127, 1.152547001838684, -7.214369297027588, -5.146897792816162, -6.283102035522461, -11.463550567626953, -7.751645565032959, -11.252680778503418, -9.319047927856445, -8.11094856262207, -8.898587226867676, -2.15217661857605, -0.10237424820661545, -3.6214966773986816, -12.085700035095215, -1.599789023399353, -1.7422595024108887, -7.456813335418701, -8.457598686218262, -5.5325212478637695, -2.4997880458831787, -8.217476844787598, -8.986675262451172, -10.336335182189941], [-1.1052173376083374, -1.3570473194122314, -3.8713269233703613, 2.3654367923736572, -9.037796020507812, 11.620930671691895, 2.1417031288146973, 13.036051750183105, -0.11228565871715546, 0.33224615454673767, 3.9813454151153564, -6.005640029907227, -5.856462001800537, -7.669452667236328, -9.974565505981445, -11.242084503173828, -12.130292892456055, -5.630223274230957, -5.570030689239502, -6.117311000823975, -7.32966423034668, -5.952571392059326, 0.4303727447986603, -0.5507297515869141, -7.554576873779297, -3.3274905681610107, -3.4397053718566895, 0.9088093638420105, -4.845495700836182, -7.663942337036133, -10.328642845153809], [1.1323682069778442, 1.3157405853271484, 0.828519880771637, -1.6223008632659912, -7.967062950134277, 4.090002059936523, 14.007913589477539, 6.785359859466553, 16.369604110717773, 1.524818778038025, -4.911859035491943, -9.018620491027832, -9.306066513061523, -8.402979850769043, -11.57016658782959, -9.890503883361816, -10.68331527709961, -5.442021369934082, 4.999141216278076, 5.106411933898926, 4.015860080718994, -12.08991527557373, 6.171087741851807, -1.0262863636016846, -8.962656021118164, -6.404715538024902, -4.912563323974609, -2.5522496700286865, -6.039242744445801, -10.613517761230469, -6.997122287750244], [-3.4062156677246094, -3.2604005336761475, -4.109685897827148, -4.58593225479126, -9.489058494567871, 1.6483688354492188, 2.376404047012329, 0.7108156681060791, 0.5808579921722412, 17.98756980895996, 9.364227294921875, 1.8207945823669434, -6.545583724975586, 3.3331942558288574, 2.5704448223114014, -7.702937602996826, -9.870623588562012, -1.303507924079895, -5.957301616668701, -6.226568222045898, -6.917541980743408, -7.621560573577881, -0.5124773979187012, -2.2896718978881836, -12.721405029296875, -6.885163307189941, -9.90884780883789, -1.4125298261642456, 2.3772332668304443, -5.4370293617248535, -1.6405099630355835], [-3.2013378143310547, -3.3440065383911133, -1.2165169715881348, -4.172476291656494, -5.278318881988525, -2.3818702697753906, -3.210822582244873, -3.580622911453247, -5.1373138427734375, -1.7848750352859497, -1.4050911664962769, 16.463136672973633, -1.4766411781311035, 16.46843147277832, 11.259382247924805, -1.0086976289749146, -1.908290982246399, -4.666292667388916, -2.9601247310638428, -2.0503976345062256, -1.600439190864563, -1.4223682880401611, -2.251126289367676, -4.444605827331543, -9.10830020904541, -10.853714942932129, -11.52085018157959, -1.6640691757202148, 2.193969964981079, 2.127061367034912, -4.728240013122559], [-0.5153040289878845, -1.290441632270813, -1.3887863159179688, -2.88513445854187, -8.828889846801758, 1.3482768535614014, 0.010438825935125351, -0.6988681554794312, -2.9927048683166504, 2.8313045501708984, 2.5383071899414062, 6.094320297241211, -1.2357840538024902, 19.095901489257812, 12.049205780029297, -2.1667087078094482, -3.2871627807617188, -4.000303268432617, -2.7362473011016846, -1.7782089710235596, -1.643406629562378, -4.0933918952941895, -2.1210238933563232, -3.1019272804260254, -8.912919998168945, -8.04006290435791, -10.427931785583496, 0.8204227089881897, -1.7909467220306396, -0.8497583270072937, -5.065787315368652], [-1.4752472639083862, -0.13337232172489166, 1.7657679319381714, -2.7154576778411865, -2.644958257675171, -1.401767373085022, 0.21228086948394775, -0.5131799578666687, 1.4820858240127563, -2.5781843662261963, 3.075222969055176, -2.9382081031799316, -7.704923152923584, -3.6199238300323486, -3.213698625564575, 10.677529335021973, 12.515663146972656, 3.690605401992798, 10.979350090026855, 12.963836669921875, 11.986873626708984, 4.023745059967041, 0.9576215744018555, -4.142323970794678, -7.46238374710083, -9.735015869140625, -8.231826782226562, -1.0106267929077148, -2.2898473739624023, -2.2792820930480957, -6.5174055099487305], [-0.3335295617580414, 1.2584013938903809, -1.2919337749481201, -2.0686888694763184, -11.050207138061523, 5.148484706878662, 0.46310505270957947, 4.050027847290039, -1.6178984642028809, -6.791775703430176, -2.2926063537597656, -7.568892002105713, -10.240560531616211, -7.8912248611450195, -11.374415397644043, -7.808314323425293, -7.384036540985107, -5.577442646026611, -4.582977771759033, -4.019510746002197, -5.569993019104004, -2.2238216400146484, -0.21682055294513702, 12.080615043640137, 6.551390647888184, 17.416383743286133, 8.308161735534668, -0.3994586169719696, -1.8691462278366089, -2.187755823135376, -4.866983413696289], [-2.294813394546509, -1.4864670038223267, -1.4635752439498901, -2.9900710582733154, -14.971826553344727, 4.747520446777344, -0.9042328000068665, 3.1032114028930664, -3.679764747619629, -5.160387992858887, -1.1286523342132568, -7.035560607910156, -6.664344787597656, -7.769715309143066, -10.94699478149414, -6.526098251342773, -6.273430347442627, -6.723901271820068, -5.448723316192627, -5.721604824066162, -7.575157165527344, -4.370161056518555, -1.393196702003479, 11.913715362548828, 17.861845016479492, 15.086359024047852, 6.581197261810303, -0.31534600257873535, -2.1320040225982666, -4.305175304412842, -7.700469970703125], [-2.552478790283203, -1.305349349975586, 0.03923465311527252, -5.891383647918701, -7.833784580230713, 1.2974026203155518, 5.689708709716797, 2.8017938137054443, 7.800131320953369, -0.12797383964061737, -4.34028434753418, -4.815661430358887, -8.476018905639648, -1.2871994972229004, -1.1152652502059937, -6.992332458496094, -7.258864402770996, 0.09565334022045135, -6.82894229888916, -5.026597023010254, -3.2372162342071533, -7.9831085205078125, -3.8290252685546875, -0.595430850982666, -5.086977005004883, -4.143807888031006, -5.033395290374756, 4.200597763061523, 6.196822166442871, -4.807774066925049, 23.876855850219727]];\n", + "//# sourceURL=js_5e545691b3" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "var cmap = ['transparent', '#f6fcf4', '#f4fbf2', '#f3faf0', '#f1faee', '#f0f9ec', '#eff9eb', '#edf8e9', '#ecf8e8', '#eaf7e6', '#e8f6e4', '#e7f6e3', '#e5f5e1', '#e4f5df', '#e1f3dc', '#def2d9', '#dcf2d7', '#daf0d4', '#d7efd1', '#d5efcf', '#d2edcc', '#d0edca', '#cdecc7', '#cbeac4', '#c9eac2', '#c6e8bf', '#c3e7bc', '#c0e6b9', '#bce4b5', '#bae3b3', '#b6e2af', '#b4e1ad', '#b0dfaa', '#acdea6', '#aadda4', '#a7dba0', '#a3da9d', '#a0d99b', '#9cd797', '#99d595', '#95d391', '#91d28e', '#8ed08b', '#8ace88', '#87cd86', '#83cb82', '#7fc97f', '#7cc87c', '#78c679', '#73c476'];\n", + "//# sourceURL=js_b212ab59e1" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " \n", + "

\n", + " \n", + "
  10%
  43%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
an apple
  43%
  69%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a picture of an apple
  60%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
an ipod
   4%
  54%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
granny smith
 100%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
an apple with a note saying \"ipod\"
   0%
   0%
  26%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a cold drink on a hot day
   0%
   0%
   0%
  79%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a hot drink on a cold day
   0%
   0%
  59%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a photo of a cold drink on a hot day
   0%
   0%
   0%
  98%
   0%
   0%
   0%
   0%
   0%
   0%
   1%
a photo of a hot drink on a cold day
   0%
   0%
   0%
   0%
 100%
   0%
   0%
   0%
   0%
   0%
   0%
a photo of two guys in need of caffeine
   0%
   0%
   0%
   0%
   4%
   0%
   0%
   0%
   0%
   0%
   0%
a photo of two guys in need of water
   0%
   0%
   0%
   0%
   0%
  98%
   0%
   0%
   0%
   0%
   0%
a photo of the SigLIP authors
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a photo of a rock band
   0%
   0%
   0%
   0%
   0%
  98%
 100%
   0%
   0%
   0%
   0%
a photo of researchers at Google Brain
   0%
   0%
   0%
   0%
   0%
  20%
  35%
   0%
   0%
   0%
   0%
a photo of researchers at OpenAI
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  12%
   0%
   0%
   0%
a robot on a sign
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  46%
   0%
   0%
   0%
a photo of a robot on a sign
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
an empty street
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  16%
   0%
   0%
   0%
autumn in Toronto
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  57%
   0%
   0%
   0%
a photo of autumn in Toronto
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  34%
   0%
   0%
   0%
a photo of Toronto in autumn
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
a photo of Toronto in summer
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
autumn in Singapore
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  36%
  32%
   0%
cow
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  99%
   0%
a cow in a tuxedo
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  99%
  92%
   0%
a cow on the beach
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   1%
   0%
   0%
a cow in the prairie
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
the real mountain view
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
Zürich
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
San Francisco
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
 100%
a picture of a laptop with the lockscreen on, a cup of cappucino, salt and pepper grinders. The view through the window reveals lake Zürich and the Alps in the background of the city." + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "\n", + " function update(b) {\n", + " for(var iimg = 0; iimg < logits.length; iimg++) {\n", + " for(var itxt = 0; itxt < logits[iimg].length; itxt++) {\n", + " const el = document.getElementById(`p_${iimg}_${itxt}`);\n", + " const p = Math.round(100 / (1 + Math.exp(-logits[iimg][itxt] - b)));\n", + " const pad = p < 10.0 ? ' ' : p < 100.0 ? ' ' : ''\n", + " el.innerHTML = pad + (p).toFixed(0) + '%';\n", + "\n", + " const td = document.getElementById(`td_${iimg}_${itxt}`);\n", + " const c = cmap[Math.round(p / 100 * (cmap.length - 1))];\n", + " td.style.backgroundColor = c;\n", + " }\n", + " }\n", + " }\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "\n", + " const value = document.querySelector(\"#value\");\n", + " const input = document.querySelector(\"#b\");\n", + " value.textContent = input.value;\n", + " input.addEventListener(\"input\", (event) => {\n", + " value.textContent = event.target.value;\n", + " update(event.target.value);\n", + " });\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "update(-12.661874771118164)" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "google.colab.output.resizeIframeToContent()" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# More international examples (choose i18n model for this)" + ], + "metadata": { + "id": "f5lIiaD700UK" + } + }, + { + "cell_type": "code", + "source": [ + "#@title Load and embed images\n", + "\n", + "import big_vision.pp.builder as pp_builder\n", + "import big_vision.pp.ops_general\n", + "import big_vision.pp.ops_image\n", + "import big_vision.pp.ops_text\n", + "import PIL\n", + "\n", + "!wget -q 'https://live.staticflickr.com/4152/5189547658_3b2a7126cb_b.jpg' -O ants_climbing_a_tree_food.jpg\n", + "!wget -q 'https://storage.googleapis.com/big_vision/siglip/pexels-poranimm-athithawatthee-842401.jpg' -O ants_climbing_tree.jpg\n", + "!wget -q 'https://images.rawpixel.com/image_1300/cHJpdmF0ZS9zdGF0aWMvaW1hZ2Uvd2Vic2l0ZS8yMDIyLTA0L2xyL3B4OTE3NDYyLWltYWdlLWt3eW8ydmxrLmpwZw.jpg' -O lion_head.jpg\n", + "!wget -q 'https://images.rawpixel.com/image_1300/cHJpdmF0ZS9sci9pbWFnZXMvd2Vic2l0ZS8yMDIzLTA5L3Jhd3BpeGVsX29mZmljZV8yN19taW5pbWFsX3NpbXBsZV9fbGlvbl9fcGFwZXJfY29sbGFnZV9taW5pbWFsX183OGRlOGU3OS02ZTE3LTQ2YzAtYTUyOS02ZDAxM2YzNDg0OWVfMi5qcGc.jpg' -O lion_head_red.jpg\n", + "!wget -q https://live.staticflickr.com/232/551040940_87299a85ec_h.jpg -O meat_ball.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip/squirrel_fish.jpg -O squirrel_fish.jpg\n", + "!wget -q 'https://ideogram.ai/api/images/direct/F3lMxBprSk6ligq5Vy3XSw' -O squirrel_fish2.jpg\n", + "!wget -q 'https://pbs.twimg.com/media/FTyEyxyXsAAyKPc?format=jpg&name=small' -O cow_beach.jpg\n", + "!wget -q 'https://storage.googleapis.com/big_vision/siglip/cow_beach2.jpg' -O cow_beach2.jpg\n", + "\n", + "\n", + "images = [PIL.Image.open(fname) for fname in [\n", + " 'ants_climbing_a_tree_food.jpg',\n", + " 'ants_climbing_tree.jpg',\n", + " 'meat_ball.jpg',\n", + " 'lion_head.jpg',\n", + " 'lion_head_red.jpg',\n", + " 'fried_fish.jpeg',\n", + " 'squirrel_fish.jpg',\n", + " 'squirrel_fish2.jpg',\n", + " 'cow_beach.jpg',\n", + " 'cow_beach2.jpg',\n", + "]]\n", + "\n", + "pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)')\n", + "imgs = np.array([pp_img({'image': np.array(image)})['image'] for image in images])\n", + "zimg, _, out = model.apply({'params': params}, imgs, None)\n", + "\n", + "print(imgs.shape, zimg.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YsK74v2J04Xp", + "outputId": "63f024ad-205c-4dd3-a5af-4dfd5ff198ca" + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "/usr/local/lib/python3.10/dist-packages/tensorflow_addons/utils/tfa_eol_msg.py:23: UserWarning: \n", + "\n", + "TensorFlow Addons (TFA) has ended development and introduction of new features.\n", + "TFA has entered a minimal maintenance and release mode until a planned end of life in May 2024.\n", + "Please modify downstream libraries to take dependencies from other repositories in our TensorFlow community (e.g. Keras, Keras-CV, and Keras-NLP). \n", + "\n", + "For more information see: https://github.com/tensorflow/addons/issues/2807 \n", + "\n", + " warnings.warn(\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(10, 256, 256, 3) (10, 768)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "#@title Tokenize and embed texts\n", + "\n", + "texts = [\n", + " '蚂蚁上树',\n", + " '肉末粉丝',\n", + " 'ants climbing a tree',\n", + " 'minced pork rice noodle',\n", + " #\n", + " '红烧狮子头',\n", + " 'red burned lion head',\n", + " 'lion head',\n", + " 'meat ball with soy sauce',\n", + " #\n", + " '松鼠鳜鱼',\n", + " 'squirrel',\n", + " 'squirrel and fish',\n", + " 'squirrel mandarinfish',\n", + " 'squirrel mandarin fish',\n", + " 'sweet and sour mandarin fish',\n", + " #\n", + " 'cow',\n", + " 'a cow in a tuxedo',\n", + " 'a cow on the beach',\n", + " 'a cow in the prairie',\n", + " 'une vache sur la plage',\n", + " 'eine Kuh am Strand',\n", + " 'วัวอยู่ที่ชายหาด',\n", + " '一只躺在沙滩上的牛',\n", + " '一只沙滩上的牛',\n", + " 'корова на пляже',\n", + " 'بقرة على الشاطئ',\n", + "]\n", + "\n", + "TOKENIZERS = {\n", + " 32_000: 'c4_en',\n", + " 250_000: 'mc4',\n", + "}\n", + "pp_txt = pp_builder.get_preprocess_fn(f'tokenize(max_len={SEQLEN}, model=\"{TOKENIZERS[VOCAB]}\", eos=\"sticky\", pad_value=1, inkey=\"text\")')\n", + "txts = np.array([pp_txt({'text': text})['labels'] for text in texts])\n", + "_, ztxt, out = model.apply({'params': params}, None, txts)\n", + "\n", + "print(txts.shape, ztxt.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dAzAuYJh1eQ3", + "outputId": "6c07c1a2-c236-4b68-b7e3-f92dcc070fcc" + }, + "execution_count": 5, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(25, 64) (25, 768)\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "make_table(zimg, ztxt, out)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 633 + }, + "id": "JlMwn6K1-62i", + "outputId": "6b8fa113-06f3-492c-ffa7-942d4799cae3" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "var logits = [[15.194855690002441, 14.548081398010254, 4.362802505493164, 8.915352821350098, 0.12249733507633209, -1.8669313192367554, -2.1026358604431152, 4.83571195602417, -1.48772132396698, -2.885380744934082, -3.757584571838379, -9.74438190460205, -6.739628791809082, 1.0982742309570312, -1.8383992910385132, -8.639388084411621, -8.514564514160156, -8.664950370788574, -9.010446548461914, -8.695591926574707, -0.29446348547935486, -2.3145699501037598, 0.3301776945590973, -9.183826446533203, -7.548545837402344], [3.1235272884368896, -2.662849187850952, 15.499628067016602, -5.6270432472229, -8.800381660461426, -5.2857537269592285, -4.901862621307373, -8.64078426361084, -8.457619667053223, -0.7642378211021423, -6.292320251464844, -6.919025421142578, -5.699285984039307, -6.146625518798828, -1.7575650215148926, -9.384129524230957, -6.215198040008545, -6.763903617858887, -6.789668560028076, -6.646523952484131, 2.078498125076294, 0.1571565568447113, 1.2640687227249146, -4.958133697509766, -4.504084587097168], [2.4513118267059326, 3.711794853210449, -2.7506296634674072, 6.2139153480529785, 12.623679161071777, -2.242187261581421, -0.873506486415863, 12.75291633605957, 5.779244422912598, -3.411043405532837, -2.7684485912323, 0.8032691478729248, 2.4132730960845947, 10.139656066894531, -1.5548374652862549, -7.363276481628418, -10.937602043151855, -10.354545593261719, -12.12853717803955, -11.330802917480469, -3.7032158374786377, -4.167450428009033, -2.857227087020874, -12.429163932800293, -10.023411750793457], [-7.848373889923096, -8.82786750793457, -4.246535301208496, -11.672212600708008, -4.754408836364746, 5.023717403411865, 10.245930671691895, -9.671830177307129, -5.305540561676025, 0.939210832118988, -3.7660276889801025, -6.9834089279174805, -5.540616512298584, -7.520627498626709, 0.6897578239440918, -4.008193016052246, -3.137038230895996, -2.492392063140869, -3.349771022796631, -2.571514129638672, -0.5961494445800781, 1.920261025428772, -0.5972135066986084, -3.192373275756836, -2.797152280807495], [-7.591951370239258, -9.57149887084961, -7.410569667816162, -10.887884140014648, -2.1018383502960205, 10.839365005493164, 12.306414604187012, -8.755990028381348, -6.4970011711120605, 1.732677698135376, -1.484777808189392, -3.788830280303955, -2.954533338546753, -4.137475967407227, 1.2805907726287842, -4.848579406738281, -4.63262939453125, -4.869859218597412, -4.654362201690674, -4.7860589027404785, -0.6505587697029114, -0.741170346736908, -1.2220640182495117, -5.068485260009766, -4.302990913391113], [0.38381102681159973, -0.5291793346405029, -4.558042049407959, -0.798613965511322, 1.3992505073547363, -3.269932508468628, -2.243269205093384, 3.4091484546661377, 13.690838813781738, -3.199730396270752, 2.4068713188171387, 4.793602466583252, 6.522286415100098, 12.24045467376709, -0.973887026309967, -5.842926025390625, -8.813263893127441, -10.347548484802246, -10.193572044372559, -9.09493350982666, 0.17290785908699036, -2.690534830093384, 0.4429348409175873, -10.299919128417969, -7.2381591796875], [-11.066581726074219, -10.138232231140137, -5.7180986404418945, -11.073030471801758, -9.701227188110352, 1.2774648666381836, 0.6818075776100159, -11.766871452331543, 7.582111358642578, 6.539462089538574, 13.692913055419922, 11.608633041381836, 12.523263931274414, 2.838015556335449, 0.06712919473648071, -8.434947967529297, -5.371018409729004, -7.046348571777344, -5.160297393798828, -4.178375244140625, -1.4383944272994995, -1.4511940479278564, -0.826172947883606, -4.657361030578613, -4.185240745544434], [-3.598116874694824, -6.576178073883057, -2.7102479934692383, -8.999201774597168, -6.829661846160889, -5.066120147705078, -1.7694122791290283, -7.724926471710205, 0.23896828293800354, 11.48562240600586, 18.98163414001465, 10.054450035095215, 10.879026412963867, -0.23405185341835022, 1.1370410919189453, -4.135552406311035, -0.34031882882118225, -1.2078852653503418, -1.5318009853363037, -3.0245869159698486, -0.7356898188591003, 2.346902847290039, 1.158348560333252, -1.281561017036438, -1.2338509559631348], [-9.843914985656738, -9.799589157104492, -6.7716383934021, -9.883660316467285, -12.059309005737305, -6.143594264984131, -3.1696691513061523, -7.953651428222656, -14.6300048828125, -5.153632164001465, -9.101214408874512, -8.86422061920166, -7.411843299865723, -9.261401176452637, 12.271851539611816, 7.439639091491699, 19.08420181274414, 9.05471420288086, 18.37834930419922, 18.505441665649414, 14.171286582946777, 12.338602066040039, 14.924001693725586, 17.368127822875977, 17.931604385375977], [-9.439372062683105, -8.37105941772461, -9.730523109436035, -9.263359069824219, -7.634936809539795, -5.775638580322266, -0.2548319399356842, -6.097734451293945, -12.719864845275879, -5.2038702964782715, -8.733600616455078, -8.040817260742188, -6.40618896484375, -8.534762382507324, 11.509172439575195, 18.91118049621582, 14.150744438171387, 6.8233747482299805, 13.563973426818848, 13.099942207336426, 10.563776016235352, 10.233851432800293, 11.005309104919434, 15.13718032836914, 14.48193359375]];\n", + "//# sourceURL=js_ca0f68d49c" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "var cmap = ['transparent', '#f6fcf4', '#f4fbf2', '#f3faf0', '#f1faee', '#f0f9ec', '#eff9eb', '#edf8e9', '#ecf8e8', '#eaf7e6', '#e8f6e4', '#e7f6e3', '#e5f5e1', '#e4f5df', '#e1f3dc', '#def2d9', '#dcf2d7', '#daf0d4', '#d7efd1', '#d5efcf', '#d2edcc', '#d0edca', '#cdecc7', '#cbeac4', '#c9eac2', '#c6e8bf', '#c3e7bc', '#c0e6b9', '#bce4b5', '#bae3b3', '#b6e2af', '#b4e1ad', '#b0dfaa', '#acdea6', '#aadda4', '#a7dba0', '#a3da9d', '#a0d99b', '#9cd797', '#99d595', '#95d391', '#91d28e', '#8ed08b', '#8ace88', '#87cd86', '#83cb82', '#7fc97f', '#7cc87c', '#78c679', '#73c476'];\n", + "//# sourceURL=js_b212ab59e1" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "

\n", + " \n", + " \n", + " \n", + "

\n", + " \n", + "
  91%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
蚂蚁上树
  84%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
肉末粉丝
   0%
  93%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
ants climbing a tree
   2%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
minced pork rice noodle
   0%
   0%
  43%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
红烧狮子头
   0%
   0%
   0%
   0%
  11%
   0%
   0%
   0%
   0%
   0%
red burned lion head
   0%
   0%
   0%
   7%
  36%
   0%
   0%
   0%
   0%
   0%
lion head
   0%
   0%
  47%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
meat ball with soy sauce
   0%
   0%
   0%
   0%
   0%
  69%
   0%
   0%
   0%
   0%
松鼠鳜鱼
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  20%
   0%
   0%
squirrel
   0%
   0%
   0%
   0%
   0%
   0%
  69%
 100%
   0%
   0%
squirrel and fish
   0%
   0%
   0%
   0%
   0%
   0%
  22%
   6%
   0%
   0%
squirrel mandarinfish
   0%
   0%
   0%
   0%
   0%
   0%
  41%
  12%
   0%
   0%
squirrel mandarin fish
   0%
   0%
   6%
   0%
   0%
  34%
   0%
   0%
   0%
   0%
sweet and sour mandarin fish
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  35%
  20%
cow
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
 100%
a cow in a tuxedo
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
 100%
  78%
a cow on the beach
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   2%
   0%
a cow in the prairie
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
 100%
  66%
une vache sur la plage
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
 100%
  55%
eine Kuh am Strand
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  78%
   9%
วัวอยู่ที่ชายหาด
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  37%
   7%
一只躺在沙滩上的牛
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  88%
  13%
一只沙滩上的牛
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  99%
  90%
корова на пляже
   0%
   0%
   0%
   0%
   0%
   0%
   0%
   0%
  99%
  83%
بقرة على الشاطئ" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "\n", + " function update(b) {\n", + " for(var iimg = 0; iimg < logits.length; iimg++) {\n", + " for(var itxt = 0; itxt < logits[iimg].length; itxt++) {\n", + " const el = document.getElementById(`p_${iimg}_${itxt}`);\n", + " const p = Math.round(100 / (1 + Math.exp(-logits[iimg][itxt] - b)));\n", + " const pad = p < 10.0 ? ' ' : p < 100.0 ? ' ' : ''\n", + " el.innerHTML = pad + (p).toFixed(0) + '%';\n", + "\n", + " const td = document.getElementById(`td_${iimg}_${itxt}`);\n", + " const c = cmap[Math.round(p / 100 * (cmap.length - 1))];\n", + " td.style.backgroundColor = c;\n", + " }\n", + " }\n", + " }\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "\n", + " const value = document.querySelector(\"#value\");\n", + " const input = document.querySelector(\"#b\");\n", + " value.textContent = input.value;\n", + " input.addEventListener(\"input\", (event) => {\n", + " value.textContent = event.target.value;\n", + " update(event.target.value);\n", + " });\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "update(-12.885268211364746)" + ] + }, + "metadata": {} + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "application/javascript": [ + "google.colab.output.resizeIframeToContent()" + ] + }, + "metadata": {} + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Explanation for non-Chinese speakers:\n", + "\n", + "- The first dish is literally called \"ants climbing a tree\" in Chinese.\n", + "- The second dish is literally called \"red burned lion head\" in Chinese.\n", + "- The third dish is literally called \"squirrel mandarinfish\" in Chinese.\n", + "\n", + "We are looking for more interesting examples that highlight culture-language aspects and where a non-EN model should \"get it\" while an EN-only does not." + ], + "metadata": { + "id": "bNGoftU3y4UQ" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Example image credits\n", + "\n", + "- The apple and apple + iPod images are from OpenAI.\n", + "- [Cold drink on hot day](https://unsplash.com/fr/photos/hQHm2D1fH70).\n", + "- [Hot drink on cold day](https://www.rawpixel.com/image/3282934).\n", + "- Cows on beach were created by Chitwan Saharia using the Imagen model and shared with permission.\n", + "- [\"ant climbing tree\" noodles](https://www.flickr.com/photos/avlxyz/5189547658)\n", + "- [actual ants climbing on a tree](https://www.pexels.com/photo/macro-photo-of-five-orange-ants-842401/)\n", + "- [real lion head](https://www.rawpixel.com/image/5941715/free-public-domain-cc0-photo)\n", + "- [cartoon red lion head](https://www.rawpixel.com/image/12447997/image-texture-paper-png)\n", + "- Collaged [squirrel](https://www.pexels.com/photo/brown-squirrel-47547/) and [fish](https://zh.wikipedia.org/zh-hans/%E9%B3%9C%E9%B1%BC) images.\n", + "- cartoon [squirrel and fish](https://ideogram.ai/g/zgoma01ASS21U1YwIC7MrA/2) generated by [ideogram.ai](http://ideogram.ai) [with permission](https://x.com/ideogram_ai/status/1697428471184515316?s=20).\n", + "- The remaining pictures are personal photos taken by the authors, long after the models were trained." + ], + "metadata": { + "id": "etDZ3sl4kZ_q" + } + } + ] +} diff --git a/big_vision/configs/proj/image_text/common.py b/big_vision/configs/proj/image_text/common.py new file mode 100644 index 0000000000000000000000000000000000000000..96e5235c557a19aaf8500f03759c0655ea28956d --- /dev/null +++ b/big_vision/configs/proj/image_text/common.py @@ -0,0 +1,127 @@ +# Copyright 2024 Big Vision 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. + +"""Snippets and constants used a lot in image-text configs.""" + +import ml_collections + + +# pylint: disable=line-too-long +inits = { + # Downloaded & extracted from original repo: + # https://github.com/google-research/bert + 'bert_base': ('base', 'gs://vit_models/lit/bert/uncased_L-12_H-768_A-12'), + 'bert_large': ('large', 'gs://vit_models/lit/bert/uncased_L-uncased_L-24_H-1024_A-16'), + # Recommended "How to train your ViT..." checkpoints from + # https://github.com/google-research/vision_transformer#available-vit-models + 'B/32': ('B/32', 'gs://vit_models/augreg/B_32-i21k-300ep-lr_0.001-aug_light1-wd_0.1-do_0.0-sd_0.0.npz'), + 'B/16': ('B/16', 'gs://vit_models/augreg/B_16-i21k-300ep-lr_0.001-aug_medium1-wd_0.1-do_0.0-sd_0.0.npz'), + 'L/16': ('L/16', 'gs://vit_models/augreg/L_16-i21k-300ep-lr_0.001-aug_strong1-wd_0.1-do_0.0-sd_0.0.npz'), +} +# pylint: enable=line-too-long + + +def _square875(sz): + return f'resize({int(sz/0.875)})|central_crop({sz})|value_range(-1,1)' + + +def _aspect75(sz): + return f'resize_small({int(sz/0.75)})|central_crop({sz})|value_range(-1,1)' + + +def _drop_no_real_label(f): + return len(f['real_label']) > 0 + + +def _drop_no_imagenet(f): + return len(f['labels_imagenet']) > 0 + + +DISCLF_DATASET_OVERRIDES = { + 'imagenet2012': {'class_names': 'clip', 'split': 'validation'}, + 'imagenet2012_minival': { + 'dataset_name': 'imagenet2012', + 'class_names': 'clip', + 'split': 'train[99%:]', + }, + 'imagenet2012_real': { + 'split': 'validation', + 'class_names': 'clip', + 'class_names_dataset_name': 'imagenet2012', + 'pp_img': lambda sz: ( + _square875(sz) + '|pad_to_shape(inkey="real_label", outkey="label", shape=[10], pad_value=-1)|keep("label", "image")'), # pylint: disable=line-too-long + 'pre_filter_fn': _drop_no_real_label, + }, + 'imagenet_v2': {'class_names': 'clip'}, + 'imagenet_a': { + 'class_names': 'clip', + 'pp_img': lambda sz: _aspect75(sz) + '|map("i1k_i1ka")', + }, + 'imagenet_r': { + 'class_names': 'clip', + 'pp_img': lambda sz: _square875(sz) + '|map("i1k_i1kr")', + }, +} + + +def get_disclf(sz, *, pp_txt=None, dataset_names=('imagenet2012',), **kw): + """Returns config for discriminative_classifier of specified datasets.""" + config = ml_collections.ConfigDict(dict( + dataset_names=list(dataset_names), + type='proj.image_text.discriminative_classifier', + prefix='z/0shot/', + pp_img=_square875(sz), + dataset_overrides={}, + cache_final=True, + **kw, + )) + if pp_txt: + config.pp_txt = pp_txt + for name in dataset_names: + if name in DISCLF_DATASET_OVERRIDES: + config.dataset_overrides[name] = {**DISCLF_DATASET_OVERRIDES[name]} + d = config.dataset_overrides[name] + if 'pp_img' in d and callable(d['pp_img']): + with d.ignore_type(): + d['pp_img'] = d['pp_img'](sz) + return config + + +def get_coco( + *, + pp_img='resize(224)|value_range(-1, 1)', + pp_txt='tokenize(max_len=16, inkey="texts", eos="sticky", pad_value=1)', + prefix='z/retr/coco_', + **kw): + """Returns config for mscoco retrieval zero-shot. + + Args: + pp_img: Pre-processing string for "image" feature. + pp_txt: Pre-processing string for texts (expected to tokenize "texts" to + "labels"). + prefix: Prefix to use for metrics. + **kw: Other config settings, most notably log_{steps,percent,...}. + + Returns: + `ConfigDict` that can be used as a retrieval evaluator configuration. + """ + return ml_collections.ConfigDict({ + 'type': 'proj.image_text.retrieval', + 'pp_txt': pp_txt, + 'pp_img': pp_img, + 'prefix': prefix, + 'dataset': 'coco_captions', + 'txt_name': ('captions', 'text'), + **kw, + }) diff --git a/big_vision/configs/proj/image_text/lit.ipynb b/big_vision/configs/proj/image_text/lit.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..db3c9536c216102e8d0343d0283a79517eb0302c --- /dev/null +++ b/big_vision/configs/proj/image_text/lit.ipynb @@ -0,0 +1,1903 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "source": [ + "This Colab shows some example code how to make use of the\n", + "[LiT: Zero-Shot Transfer with Locked-image text Tuning](https://arxiv.org/abs/2111.07991)\n", + "models in the `big_vision` codebase.\n", + "\n", + "For more information refer to\n", + "\n", + "https://github.com/google-research/big_vision/blob/main/README.md\n", + "\n", + "https://github.com/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/README.md" + ], + "metadata": { + "id": "3OCq_g6vBiWX" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Initialize" + ], + "metadata": { + "id": "hg1hy3ER9LHT" + } + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "i0ws1pPjl6nY", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "175aa7e3-4c88-47aa-f0c9-6b3a36b87478" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "fatal: destination path 'big_vision' already exists and is not an empty directory.\n", + "Already up to date.\n" + ] + } + ], + "source": [ + "!git clone --branch=main --depth=1 https://github.com/google-research/big_vision\n", + "!cd big_vision \u0026\u0026 git pull" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "xa2C2eTej-XX", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "f5b50c10-8ecb-4873-841f-3846bca1dc18" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + ] + } + ], + "source": [ + "!pip install -qr big_vision/big_vision/requirements.txt" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "f09dX0cekPRa" + }, + "outputs": [], + "source": [ + "import sys\n", + "bv_path = './big_vision'\n", + "if bv_path not in sys.path:\n", + " sys.path.insert(0, bv_path)\n", + "\n", + "%load_ext autoreload\n", + "%autoreload 2" + ] + }, + { + "cell_type": "code", + "source": [ + "from absl import flags\n", + "from absl import logging\n", + "import tensorflow_datasets as tfds\n", + "from google.colab import files\n", + "\n", + "logging.set_verbosity(logging.INFO)\n", + "\n", + "def set_max_height(max_height):\n", + " \"\"\"Limits scrollable area of output cell to `max_height` pixels.\"\"\"\n", + " import IPython.display\n", + " IPython.display.display(IPython.display.Javascript('''\n", + " google.colab.output.setIframeHeight(0, true, {maxHeight: %d})\n", + " ''' % max_height))" + ], + "metadata": { + "id": "3J0Ilcu6LczM" + }, + "execution_count": 4, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "ij41ZsIkmVQB", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "03058240-b85b-46e2-e944-92a980077bcf" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Mar 13 10:56:53 2023 \n", + "+-----------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 525.85.12 Driver Version: 525.85.12 CUDA Version: 12.0 |\n", + "|-------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|===============================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 68C P0 30W / 70W | 0MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-------------------------------+----------------------+----------------------+\n", + " \n", + "+-----------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=============================================================================|\n", + "| No running processes found |\n", + "+-----------------------------------------------------------------------------+\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[StreamExecutorGpuDevice(id=0, process_index=0, slice_index=0)]" + ] + }, + "metadata": {}, + "execution_count": 5 + } + ], + "source": [ + "# Set up Colab TPUs (if available).\n", + "import os\n", + "if 'COLAB_TPU_ADDR' in os.environ:\n", + " import jax.tools.colab_tpu\n", + " jax.tools.colab_tpu.setup_tpu()\n", + "else:\n", + " !nvidia-smi\n", + "import jax\n", + "jax.devices()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "cCqkcNAqz7xr" + }, + "outputs": [], + "source": [ + "# Uncomment this snippet to access a private GCS bucket with prepared\n", + "# TFDS datasets.\n", + "\n", + "# import tensorflow_datasets as tfds\n", + "# from google.colab import auth\n", + "# auth.authenticate_user() # Required to access access protected GCS buckets.\n", + "# import os\n", + "# os.environ['TFDS_DATA_DIR'] = 'gs://tensorflow-datasets/datasets'\n", + "# builder = tfds.builder('coco_captions')\n", + "# b = next(iter(builder.as_dataset('val')))" + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Load training run data" + ], + "metadata": { + "id": "TiJsesb1Bds8" + } + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "import re\n", + "\n", + "import matplotlib.pyplot as plt\n", + "import numpy as np\n", + "import pandas as pd\n", + "from tensorflow.io import gfile\n", + "\n", + "def plot_metrics(workdir, regexes, cols=4, cmp={}):\n", + " \"\"\"Plots metrics matching `regexes` from `workdir`.\"\"\"\n", + " df = pd.DataFrame([json.loads(line) for line in gfile.GFile(f'{workdir}/big_vision_metrics.txt')])\n", + " df = df.set_index('step')\n", + " ms = []\n", + " for regex in regexes:\n", + " for col in df.columns:\n", + " if col not in ms and re.match(regex, col):\n", + " ms.append(col)\n", + " rows = int(np.ceil(len(ms) / cols))\n", + " _, axs = plt.subplots(rows, cols, figsize=(4*cols, 3*rows))\n", + " if rows == 1: axs = [axs]\n", + " for i, m in enumerate(ms):\n", + " ax = axs[i // cols][i % cols]\n", + " df[m].dropna().plot(ax=ax)\n", + " if m in cmp: cmp[m].dropna().plot(ax=ax)\n", + " ax.set_title(m)\n", + " plt.tight_layout()\n", + " return df\n", + "\n", + "# Reference run using the tiny 80k \"coco-captions\" TFDS dataset.\n", + "df = plot_metrics('gs://vit_models/lit/big_vision/coco_B16B', [\n", + " 'training', 'val/loss', 'img/', '.*net2012',\n", + " '.*cifar100', '.*pet', '.*@1$',\n", + "])" + ], + "metadata": { + "id": "EGSsrwtnBfD5", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 306 + }, + "outputId": "96985539-74f1-41cd-b4fe-89c1901b447d" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cFigure size 1152x432 with 8 Axes\u003e" + ], + "image/png": "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(internal link)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\n" + }, + "metadata": { + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Inference\n", + "\n", + "Using an example from the online Demo\n", + "\n", + "https://google-research.github.io/vision_transformer/lit/" + ], + "metadata": { + "id": "DDNZ7kkGNr8_" + } + }, + { + "cell_type": "code", + "source": [ + "!test -f apple-ipod.jpg || wget https://cdn.openai.com/multimodal-neurons/assets/apple/apple-ipod.jpg\n", + "\n", + "labels = [\n", + " 'an apple',\n", + " 'an ipod',\n", + " 'granny smith',\n", + " 'an apple with a note saying \"ipod\"',\n", + " 'an adversarial attack',\n", + "]\n", + "\n", + "import PIL\n", + "import numpy as np\n", + "img = np.array(PIL.Image.open('apple-ipod.jpg'))\n", + "import matplotlib.pyplot as plt\n", + "plt.imshow(img)\n", + "img.shape, img.dtype" + ], + "metadata": { + "id": "4Fp2PiiYYnqp", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 286 + }, + "outputId": "9c8498b3-4330-46ef-e8d1-05864e59fccf" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "((322, 322, 3), dtype('uint8'))" + ] + }, + "metadata": {}, + "execution_count": 8 + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cFigure size 432x288 with 1 Axes\u003e" + ], + "image/png": "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(internal link)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\n" + }, + "metadata": { + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "code", + "source": [ + "!test -f LiT-B16B.npz || gsutil cp gs://vit_models/lit/LiT-B16B.* ." + ], + "metadata": { + "id": "sQLpVwrFTt5P" + }, + "execution_count": 9, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "RkWQEDrkmSf0", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 17 + }, + "outputId": "34b2cd6d-7c02-457b-a127-30f7e6676b35" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ], + "application/javascript": [ + "\n", + " ((filepath) =\u003e {{\n", + " if (!google.colab.kernel.accessAllowed) {{\n", + " return;\n", + " }}\n", + " google.colab.files.view(filepath);\n", + " }})(\"/content/big_vision/big_vision/configs/proj/image_text/lit_coco.py\")" + ] + }, + "metadata": {} + } + ], + "source": [ + "files.view('big_vision/big_vision/configs/proj/image_text/siglip_lit_coco.py')\n", + "from big_vision.configs.proj.image_text import siglip_lit_coco as lit_coco\n", + "arg = 'txt=bert_base,img=B/16,img_head,init=LiT-B16B.npz'\n", + "config = lit_coco.get_config(arg)" + ] + }, + { + "cell_type": "code", + "source": [ + "# Initialize template params...\n", + "import importlib\n", + "import jax.numpy as jnp\n", + "\n", + "model_mod = importlib.import_module(f'big_vision.models.{config.model_name}')\n", + "\n", + "model = model_mod.Model(**config.model)\n", + "\n", + "init_params = [\n", + " jnp.zeros(shape, dtype)\n", + " for shape, dtype in zip(config.init_shapes, config.init_types)\n", + "]\n", + "\n", + "params0 = model.init(jax.random.PRNGKey(42), *init_params)['params'].unfreeze()" + ], + "metadata": { + "id": "2Frpb9t8NsnH" + }, + "execution_count": 12, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# ... and load/modify pre-trained params.\n", + "from big_vision import utils\n", + "# Note that `.load()` is responsible for parameter tree surgery to adapt old\n", + "# checkpoints to most recent source code.\n", + "params = model_mod.load(params0, 'LiT-B16B.npz', config.model)" + ], + "metadata": { + "id": "gyZMqL5C9usk", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "61b595ff-fccf-4367-b565-d7bcc74fbea6" + }, + "execution_count": 13, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "INFO:absl:ViT: Loading and fixing VERY old posemb\n", + "INFO:absl:ViT: Loading and fixing combined cls+posemb\n", + "/content/./big_vision/big_vision/utils.py:593: FutureWarning: jax.tree_flatten is deprecated, and will be removed in a future release. Use jax.tree_util.tree_flatten instead.\n", + " vals, tree_def = jax.tree_flatten(tree)\n", + "INFO:absl:\n", + "INFO:absl:\n", + "INFO:absl:Could not find original BERT checkpoint path 'LiT-B16B.npz:txt/bert_model.ckpt', loading big_vision checkpoint 'LiT-B16B.npz:txt'\n", + "INFO:absl:\n", + "INFO:absl:\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# The preprocessing is optimized for efficiently streaming through TFDS\n", + "# datasets - below code runs it separately on the image and every text.\n", + "set_max_height(222)\n", + "\n", + "from big_vision.pp import builder as pp_builder\n", + "for pp_mod in config.pp_modules:\n", + " importlib.import_module(f'big_vision.pp.{pp_mod}')\n", + "\n", + "pp_str = config.evals.val.pp_fn.replace('decode|', '')\n", + "imgs = np.array(pp_builder.get_preprocess_fn(pp_str)({\n", + " 'image': img[None],\n", + " 'captions/text': np.array(['']),\n", + "})['image'])\n", + "txts = np.stack([\n", + " pp_builder.get_preprocess_fn(pp_str, log_data=False)({\n", + " 'image': img[None],\n", + " 'captions/text': np.array([label]),\n", + " })['labels']\n", + " for label in labels\n", + "])\n", + "imgs.shape, txts.shape" + ], + "metadata": { + "id": "EWq4la1QZqZh", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 222 + }, + "outputId": "cfe73bae-b359-4470-bf25-f8f0eb9254de" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ], + "application/javascript": [ + "\n", + " google.colab.output.setIframeHeight(0, true, {maxHeight: 222})\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "INFO:absl:Data before pre-processing:\n", + "{'image': array([[[[123, 94, 52],\n", + " [123, 96, 53],\n", + " [139, 114, 74],\n", + " ...,\n", + " [ 64, 54, 19],\n", + " [ 60, 54, 22],\n", + " [ 54, 48, 16]],\n", + "\n", + " [[149, 125, 81],\n", + " [149, 126, 84],\n", + " [152, 130, 89],\n", + " ...,\n", + " [ 69, 55, 26],\n", + " [ 61, 54, 28],\n", + " [ 48, 41, 15]],\n", + "\n", + " [[152, 133, 91],\n", + " [155, 138, 95],\n", + " [153, 135, 95],\n", + " ...,\n", + " [ 75, 59, 33],\n", + " [ 68, 60, 39],\n", + " [ 45, 37, 16]],\n", + "\n", + " ...,\n", + "\n", + " [[112, 101, 73],\n", + " [115, 103, 77],\n", + " [177, 165, 141],\n", + " ...,\n", + " [ 15, 9, 9],\n", + " [ 1, 0, 0],\n", + " [ 1, 0, 0]],\n", + "\n", + " [[113, 98, 79],\n", + " [160, 145, 126],\n", + " [191, 175, 159],\n", + " ...,\n", + " [ 3, 4, 6],\n", + " [ 0, 0, 0],\n", + " [ 0, 0, 0]],\n", + "\n", + " [[113, 98, 79],\n", + " [160, 145, 126],\n", + " [191, 175, 159],\n", + " ...,\n", + " [ 3, 4, 6],\n", + " [ 0, 0, 0],\n", + " [ 0, 0, 0]]]], dtype=uint8), 'captions/text': array([''], dtype='\u003cU1')}\n", + "INFO:absl:Data after pre-processing:\n", + "{'image': \u003ctf.Tensor: shape=(1, 224, 224, 3), dtype=float32, numpy=\n", + "array([[[[ 0.00392163, -0.20784312, -0.54509807],\n", + " [ 0.07450986, -0.12156862, -0.44313723],\n", + " [ 0.0196079 , -0.14509803, -0.44313723],\n", + " ...,\n", + " [-0.5137255 , -0.5921569 , -0.8352941 ],\n", + " [-0.5058824 , -0.5764706 , -0.8352941 ],\n", + " [-0.5764706 , -0.62352943, -0.8666667 ]],\n", + "\n", + " [[ 0.18431377, 0.02745104, -0.3098039 ],\n", + " [ 0.19215691, 0.04313731, -0.27843136],\n", + " [-0.01960784, -0.1372549 , -0.44313723],\n", + " ...,\n", + " [-0.41176468, -0.5294118 , -0.7411765 ],\n", + " [-0.45098037, -0.5529412 , -0.7490196 ],\n", + " [-0.60784316, -0.67058825, -0.8509804 ]],\n", + "\n", + " [[ 0.00392163, -0.12156862, -0.41960782],\n", + " [-0.01176471, -0.1372549 , -0.42745095],\n", + " [-0.16862744, -0.27843136, -0.5686275 ],\n", + " ...,\n", + " [-0.3098039 , -0.44313723, -0.64705884],\n", + " [-0.35686272, -0.49019605, -0.6862745 ],\n", + " [-0.5372549 , -0.654902 , -0.8039216 ]],\n", + "\n", + " ...,\n", + "\n", + " [[-0.09803921, -0.19215685, -0.3960784 ],\n", + " [ 0.0196079 , -0.0745098 , -0.27058822],\n", + " [ 0.5529412 , 0.45098042, 0.28627455],\n", + " ...,\n", + " [-0.84313726, -0.88235295, -0.90588236],\n", + " [-0.94509804, -0.9607843 , -0.9764706 ],\n", + " [-0.99215686, -1. , -1. ]],\n", + "\n", + " [[-0.09019607, -0.19215685, -0.38039213],\n", + " [ 0.28627455, 0.17647064, 0.00392163],\n", + " [ 0.52156866, 0.41960788, 0.26274514],\n", + " ...,\n", + " [-0.8745098 , -0.92941177, -0.9372549 ],\n", + " [-0.94509804, -0.96862745, -0.9607843 ],\n", + " [-1. , -1. , -1. ]],\n", + "\n", + " [[-0.03529412, -0.15294117, -0.30196077],\n", + " [ 0.41176474, 0.28627455, 0.15294123],\n", + " [ 0.52156866, 0.39607847, 0.27058828],\n", + " ...,\n", + " [-0.9607843 , -0.9843137 , -0.9764706 ],\n", + " [-0.99215686, -0.9843137 , -0.9764706 ],\n", + " [-1. , -1. , -1. ]]]], dtype=float32)\u003e, 'labels': \u003ctf.Tensor: shape=(16,), dtype=int32, numpy=\n", + "array([101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n", + " 0, 0, 0], dtype=int32)\u003e}\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "((1, 224, 224, 3), (5, 16))" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%debug" + ], + "metadata": { + "id": "2KQGmbebjTmt", + "outputId": "c88fbf9c-550a-464e-d8d7-c066b94671d2", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 15, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "ERROR:root:No traceback has been produced, nothing to debug.\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "zimg, _, _ = model.apply({'params': params}, imgs, None)\n", + "_, ztxt, _ = model.apply({'params': params}, None, txts)" + ], + "metadata": { + "id": "naL1Ul1ablZ5" + }, + "execution_count": 16, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "probs = jax.nn.softmax((zimg[0] @ ztxt.T * np.exp(params['t'])))\n", + "list(zip(labels, probs.tolist()))" + ], + "metadata": { + "id": "TIdAVw9VGEAw", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "15d3f416-438e-4529-c43e-0b29702320fd" + }, + "execution_count": 17, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "[('an apple', 0.0028924213256686926),\n", + " ('an ipod', 1.852169361882261e-06),\n", + " ('granny smith', 1.0219791874988005e-05),\n", + " ('an apple with a note saying \"ipod\"', 0.9970954656600952),\n", + " ('an adversarial attack', 4.8630912630187595e-08)]" + ] + }, + "metadata": {}, + "execution_count": 17 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Run evaluation\n", + "\n", + "Below code runs a minimal version of the `big_vision.tols.eval_only` script." + ], + "metadata": { + "id": "gFsqgYov9hAx" + } + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "id": "juyZDho1n51-", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 17 + }, + "outputId": "35023da6-b321-4657-9b46-bc46a3f39593" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ], + "application/javascript": [ + "\n", + " ((filepath) =\u003e {{\n", + " if (!google.colab.kernel.accessAllowed) {{\n", + " return;\n", + " }}\n", + " google.colab.files.view(filepath);\n", + " }})(\"/content/big_vision/big_vision/tools/eval_only.py\")" + ] + }, + "metadata": {} + } + ], + "source": [ + "files.view('big_vision/big_vision/tools/eval_only.py')\n", + "from big_vision.tools import eval_only" + ] + }, + { + "cell_type": "code", + "source": [ + "!test -f LiT-B16B.npz || gsutil cp gs://vit_models/lit/LiT-B16B.* ." + ], + "metadata": { + "id": "9wcyh0wJJb5z" + }, + "execution_count": 19, + "outputs": [] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": { + "id": "ZLKRKtQlEbH8" + }, + "outputs": [], + "source": [ + "from big_vision.configs.proj.image_text import lit_coco\n", + "arg = 'txt=bert_base,img=B/16,img_head,init=LiT-B16B.npz'\n", + "config = lit_coco.get_config(arg)" + ] + }, + { + "cell_type": "code", + "source": [ + "# From all the pre-defined evaluators...\n", + "set_max_height(222)\n", + "config.evals" + ], + "metadata": { + "id": "P7GJX-ekFaJM", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 222 + }, + "outputId": "12768e66-3d59-462e-c92c-7a2b31a5dcde" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ], + "application/javascript": [ + "\n", + " google.colab.output.setIframeHeight(0, true, {maxHeight: 222})\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "coco:\n", + " data:\n", + " name: coco_captions\n", + " split: val\n", + " log_steps: 500\n", + " pp_fn: decode|resize(224)|value_range(-1,1)|flatten|bert_tokenize(inkey=\"captions/text\",\n", + " max_len=16, vocab_path=\"LiT-B16B.txt\")|keep(\"image\", \"labels\")\n", + " type: proj.image_text.contrastive\n", + " use_global_batch: true\n", + "disclf:\n", + " log_steps: 500\n", + " pp_img: resize(224)|value_range(-1,1)\n", + " pp_txt: bert_tokenize(inkey=\"texts\", max_len=16, vocab_path=\"LiT-B16B.txt\")\n", + " prefix: z/0shot/\n", + " type: proj.image_text.discriminative_classifier\n", + "imagenet:\n", + " data:\n", + " name: imagenet2012\n", + " split: validation\n", + " log_steps: 500\n", + " pp_fn: decode|resize(224)|value_range(-1,1)|clip_i1k_label_names|bert_tokenize(inkey=\"labels\",\n", + " max_len=16, vocab_path=\"LiT-B16B.txt\")|keep(\"image\", \"labels\")\n", + " type: proj.image_text.contrastive\n", + " use_global_batch: true\n", + "retrieval_coco:\n", + " dataset: coco_captions\n", + " log_steps: 500\n", + " pp_img: resize(224)|value_range(-1, 1)\n", + " pp_txt: bert_tokenize(inkey=\"texts\", max_len=16, vocab_path=\"LiT-B16B.txt\")\n", + " prefix: z/retr/coco_\n", + " txt_name: !!python/tuple\n", + " - captions\n", + " - text\n", + " type: proj.image_text.retrieval\n", + "val:\n", + " data:\n", + " name: coco_captions\n", + " split: val\n", + " log_steps: 500\n", + " pp_fn: decode|resize(224)|value_range(-1,1)|flatten|bert_tokenize(inkey=\"captions/text\",\n", + " max_len=16, vocab_path=\"LiT-B16B.txt\")|keep(\"image\", \"labels\")\n", + " type: proj.image_text.contrastive\n", + " use_global_batch: true" + ] + }, + "metadata": {}, + "execution_count": 21 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# ... run a single zeroshot discriminative classifier on pets.\n", + "config.evals = {\n", + " 'disclf': {\n", + " **config.evals.disclf,\n", + " 'dataset_names': ['oxford_iiit_pet'],\n", + " 'dataset_overrides': (),\n", + " },\n", + "}\n", + "config.evals" + ], + "metadata": { + "id": "aqqr9PB0Fh8w", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "33ecdf97-74fb-4b1c-8b5c-1b16e6287f27" + }, + "execution_count": 22, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "disclf:\n", + " dataset_names:\n", + " - oxford_iiit_pet\n", + " dataset_overrides: !!python/tuple []\n", + " log_steps: 500\n", + " pp_img: resize(224)|value_range(-1,1)\n", + " pp_txt: bert_tokenize(inkey=\"texts\", max_len=16, vocab_path=\"LiT-B16B.txt\")\n", + " prefix: z/0shot/\n", + " type: proj.image_text.discriminative_classifier" + ] + }, + "metadata": {}, + "execution_count": 22 + } + ] + }, + { + "cell_type": "code", + "source": [ + "# Prepare pets dataset.\n", + "tfds.builder('oxford_iiit_pet').download_and_prepare()" + ], + "metadata": { + "id": "AZMFa5NjAwu-", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "e183d317-5cf5-400f-af10-dd2ee30ee4a7" + }, + "execution_count": 23, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "INFO:absl:Load dataset info from /root/tensorflow_datasets/oxford_iiit_pet/3.2.0\n", + "INFO:absl:Reusing dataset oxford_iiit_pet (/root/tensorflow_datasets/oxford_iiit_pet/3.2.0)\n" + ] + } + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": { + "id": "YWipdlfQn7Zg", + "outputId": "2d4c760f-85d8-4cab-8bea-d4816709f975", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "mkdir: cannot create directory ‘lit_coco_B16B_eval’: File exists\n" + ] + } + ], + "source": [ + "workdir = 'lit_coco_B16B_eval'\n", + "!mkdir $workdir\n", + "flags.FLAGS.workdir = workdir\n", + "config.input.batch_size = 512\n", + "flags.FLAGS.config = config" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "id": "JRKXQ9KyoCVi", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 444 + }, + "outputId": "dcd18c18-20ec-4108-8139-e68218b45164" + }, + "outputs": [ + { + "output_type": "display_data", + "data": { + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ], + "application/javascript": [ + "\n", + " google.colab.output.setIframeHeight(0, true, {maxHeight: 444})\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "INFO:absl:Workdir: lit_coco_B16B_eval\n", + "INFO:absl:NOTE: Initializing proj.image_text.two_towers model...\n", + "INFO:absl:\u001b[35m[0]\u001b[0m z/secs/init = 53.88540307900007\n", + "INFO:absl:TIMING[z/secs/init]: 53.88540307900007\n", + "INFO:absl:\n", + "init params\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+----------+\n", + "| Name | Shape | Size | Mean | Std |\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+----------+\n", + "| img/Transformer/encoder_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoder_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 6.23e-09 | 1.03e-06 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -1.19e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_1/bias | (768,) | 768 | -4.47e-08 | 1e-06 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -3.51e-07 | 0.0228 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 9.09e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -7.25e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 8.03e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -6.62e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 1.16e-08 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -8.66e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_1/bias | (768,) | 768 | 3.66e-08 | 1e-06 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -6.84e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 4.01e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 8.36e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -1.96e-06 | 0.036 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -5.22e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 2.45e-08 | 1.02e-06 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 1.55e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_1/bias | (768,) | 768 | -1.63e-08 | 9.76e-07 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 7.67e-07 | 0.0228 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 3.51e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -6.83e-07 | 0.0361 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -5.43e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -1.21e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 2.03e-08 | 9.84e-07 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 1.7e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_1/bias | (768,) | 768 | -1.2e-08 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.59e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 7.33e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 1.83e-05 | 0.036 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 1.72e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 1.88e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.56e-08 | 1e-06 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -8.46e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_1/bias | (768,) | 768 | -6.15e-08 | 9.86e-07 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -8.32e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 7.9e-06 | 0.0361 |\n", + "INFO:absl:\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 1.59e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -2.98e-06 | 0.0361 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -3.39e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -5.33e-09 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 7.81e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_1/bias | (768,) | 768 | -1.28e-08 | 9.99e-07 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.42e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 1.1e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -0.000122 | 0.0361 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -4.36e-06 | 0.0361 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 2.49e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 4.7e-09 | 9.95e-07 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 1.16e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_1/bias | (768,) | 768 | -3.34e-08 | 9.85e-07 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.28e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -6.55e-06 | 0.0361 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 4.19e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 6.5e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 2.84e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -5.96e-09 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 3.21e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_1/bias | (768,) | 768 | 3.41e-08 | 9.66e-07 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -8.61e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 6.99e-06 | 0.0361 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -4.31e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -3.99e-06 | 0.0361 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -4.3e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 1.27e-08 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -1.43e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_1/bias | (768,) | 768 | -4.46e-08 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 3.42e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 1.97e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 2.28e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -2.81e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 2.17e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.34e-08 | 9.87e-07 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 3.61e-07 | 0.0228 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_1/bias | (768,) | 768 | -7.23e-08 | 1e-06 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 7.83e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -3.07e-05 | 0.0361 |\n", + "INFO:absl:\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 5.08e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -3.23e-05 | 0.036 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -2.02e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | 3.85e-10 | 1e-06 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 1.22e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_1/bias | (768,) | 768 | 3.61e-08 | 1.01e-06 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 4.09e-07 | 0.0228 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 1.49e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -5.84e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 4.68e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 4.5e-05 | 0.036 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_0/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_0/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_1/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_1/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.35e-08 | 9.85e-07 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -1.19e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_1/bias | (768,) | 768 | 1.02e-08 | 9.43e-07 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 2.57e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -0.000109 | 0.0361 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 6.98e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 6.36e-05 | 0.0361 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0 | 0.0 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -1.12e-05 | 0.0361 |\n", + "| img/cls | (1, 1, 768) | 768 | 0.0 | 0.0 |\n", + "| img/embedding/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/embedding/kernel | (16, 16, 3, 768) | 589,824 | 4.59e-06 | 0.0361 |\n", + "| img/head/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| img/head/kernel | (768, 768) | 589,824 | -3.08e-05 | 0.0361 |\n", + "| img/pos_embedding | (1, 196, 768) | 150,528 | 4.29e-05 | 0.0361 |\n", + "| t | (1,) | 1 | 2.3 | 0.0 |\n", + "| txt/BertEncoder_0/embedder/embedders_position_ids/embedding | (512, 768) | 393,216 | 2.24e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/embedder/embedders_segment_ids/embedding | (2, 768) | 1,536 | -0.000444 | 0.0177 |\n", + "| txt/BertEncoder_0/embedder/embedders_token_ids/embedding | (30522, 768) | 23,440,896 | 8.75e-07 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 9.06e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 5.48e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 3.62e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -2.55e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -5.72e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | -5.07e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -3.57e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 4.81e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 2.48e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -3.36e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -3.38e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.24e-05 | 0.0176 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 2.89e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -5.85e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -2.57e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -1.51e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 1.63e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 4.42e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 3.27e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -2.42e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 3.01e-07 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -1.24e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 1.53e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.69e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -2.72e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -8.02e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -9.89e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -8.38e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -4.68e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.8e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 2.57e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 4.71e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -8.88e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 1.81e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 1.51e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.5e-07 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -6.07e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 2.15e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -2.21e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -6.73e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -5.38e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | -1.15e-05 | 0.0176 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 3.9e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 2.32e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -5.67e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -2.08e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -1.6e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 3.79e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 4.87e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -2.25e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 1.32e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -3.61e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -4.74e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.82e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 5.25e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -1.27e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -9.13e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -1.41e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 6.44e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 1.11e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 1.21e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 1.18e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 3.39e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -4.93e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 1.02e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 4.39e-08 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -2.86e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 1.33e-06 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/value/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -1.02e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 1.6e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/dense_layer/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 2.31e-05 | 0.0176 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 5.85e-06 | 0.0176 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/layer_norm/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/BertEncoder_0/layer_norm/scale | (768,) | 768 | 1.0 | 0.0 |\n", + "| txt/head/bias | (768,) | 768 | 0.0 | 0.0 |\n", + "| txt/head/kernel | (768, 768) | 589,824 | 5.69e-05 | 0.0361 |\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+----------+\n", + "Total: 195,870,721\n", + "/content/./big_vision/big_vision/tools/eval_only.py:93: FutureWarning: jax.tree_leaves is deprecated, and will be removed in a future release. Use jax.tree_util.tree_leaves instead.\n", + " num_params = sum(p.size for p in jax.tree_leaves(params_cpu))\n", + "INFO:absl:\u001b[35m[0]\u001b[0m num_params = 195870721.0\n", + "INFO:absl:NOTE: Initialize model from LiT-B16B.npz...\n", + "INFO:absl:ViT: Loading and fixing VERY old posemb\n", + "INFO:absl:ViT: Loading and fixing combined cls+posemb\n", + "INFO:absl:\n", + "INFO:absl:\n", + "INFO:absl:Could not find original BERT checkpoint path 'LiT-B16B.npz:txt/bert_model.ckpt', loading big_vision checkpoint 'LiT-B16B.npz:txt'\n", + "INFO:absl:\n", + "INFO:absl:\n", + "INFO:absl:\n", + "loaded params\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+--------+\n", + "| Name | Shape | Size | Mean | Std |\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+--------+\n", + "| img/Transformer/encoder_norm/bias | (768,) | 768 | 0.0156 | 0.669 |\n", + "| img/Transformer/encoder_norm/scale | (768,) | 768 | 2.83 | 0.42 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_0/bias | (768,) | 768 | 0.00364 | 0.216 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_0/scale | (768,) | 768 | 0.479 | 0.652 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_1/bias | (768,) | 768 | 0.0148 | 0.243 |\n", + "| img/Transformer/encoderblock_0/LayerNorm_1/scale | (768,) | 768 | 1.49 | 2.31 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -2.17 | 1.25 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -4.87e-05 | 0.0203 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.0293 | 0.857 |\n", + "| img/Transformer/encoderblock_0/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -4.37e-05 | 0.0204 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0052 | 0.517 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 3.47e-05 | 0.0305 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.00287 | 0.316 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 1.99e-05 | 0.0189 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.0976 | 1.21 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 6.25e-05 | 0.03 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | -0.00342 | 0.0574 |\n", + "| img/Transformer/encoderblock_0/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 1.58e-05 | 0.0161 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_0/bias | (768,) | 768 | 0.00114 | 0.201 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_0/scale | (768,) | 768 | 0.577 | 0.342 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_1/bias | (768,) | 768 | -0.00644 | 0.252 |\n", + "| img/Transformer/encoderblock_1/LayerNorm_1/scale | (768,) | 768 | 0.93 | 0.62 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.94 | 1.13 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 1.62e-05 | 0.0219 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.0188 | 0.45 |\n", + "| img/Transformer/encoderblock_1/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -4.68e-05 | 0.0195 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.00409 | 0.577 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -5.47e-05 | 0.03 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.0102 | 0.522 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -8.04e-06 | 0.0188 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.00333 | 0.937 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -3.2e-05 | 0.029 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.000525 | 0.0589 |\n", + "| img/Transformer/encoderblock_1/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -3.73e-06 | 0.0187 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_0/bias | (768,) | 768 | 0.026 | 0.372 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_0/scale | (768,) | 768 | 1.52 | 0.266 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_1/bias | (768,) | 768 | 0.341 | 3.69 |\n", + "| img/Transformer/encoderblock_10/LayerNorm_1/scale | (768,) | 768 | 12.9 | 1.44 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -2.35 | 0.379 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -0.000734 | 0.0239 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00256 | 0.823 |\n", + "| img/Transformer/encoderblock_10/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 0.000145 | 0.029 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0212 | 0.483 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -3.03e-05 | 0.0234 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.00581 | 0.446 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 5.76e-07 | 0.0239 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.00684 | 0.722 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 2.64e-05 | 0.023 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.00383 | 0.112 |\n", + "| img/Transformer/encoderblock_10/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -1.78e-05 | 0.0251 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_0/bias | (768,) | 768 | 0.0345 | 0.563 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_0/scale | (768,) | 768 | 1.56 | 0.249 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_1/bias | (768,) | 768 | 0.00785 | 0.773 |\n", + "| img/Transformer/encoderblock_11/LayerNorm_1/scale | (768,) | 768 | 1.83 | 0.17 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.99 | 0.585 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -0.000184 | 0.0239 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_1/bias | (768,) | 768 | 0.0356 | 0.813 |\n", + "| img/Transformer/encoderblock_11/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.74e-05 | 0.0252 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | -0.0142 | 0.464 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 8.66e-06 | 0.0241 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | 0.0271 | 0.974 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -1.33e-06 | 0.0251 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.0283 | 0.608 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 1.1e-05 | 0.023 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0065 | 0.186 |\n", + "| img/Transformer/encoderblock_11/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 3.94e-05 | 0.0273 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_0/bias | (768,) | 768 | -0.00249 | 0.194 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_0/scale | (768,) | 768 | 0.721 | 0.304 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_1/bias | (768,) | 768 | 0.0129 | 0.228 |\n", + "| img/Transformer/encoderblock_2/LayerNorm_1/scale | (768,) | 768 | 1.14 | 0.448 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.81 | 0.824 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 5.89e-05 | 0.022 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00659 | 0.28 |\n", + "| img/Transformer/encoderblock_2/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 2.74e-06 | 0.0187 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.00162 | 0.536 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 2.21e-05 | 0.0274 |\n", + "INFO:absl:\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.00715 | 0.291 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 1.15e-05 | 0.0187 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.00435 | 0.808 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 2.73e-05 | 0.0278 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | -0.00516 | 0.0618 |\n", + "| img/Transformer/encoderblock_2/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 6.66e-06 | 0.0191 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_0/bias | (768,) | 768 | -0.000398 | 0.224 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_0/scale | (768,) | 768 | 0.859 | 0.275 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_1/bias | (768,) | 768 | 0.0115 | 0.263 |\n", + "| img/Transformer/encoderblock_3/LayerNorm_1/scale | (768,) | 768 | 1.12 | 0.257 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.81 | 0.753 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 7.85e-05 | 0.0216 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00283 | 0.194 |\n", + "| img/Transformer/encoderblock_3/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 5.27e-06 | 0.0188 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | -0.000427 | 0.543 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 5.47e-06 | 0.0257 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.0088 | 0.165 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 7.88e-07 | 0.019 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.0371 | 0.781 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 5.56e-06 | 0.0261 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0039 | 0.0784 |\n", + "| img/Transformer/encoderblock_3/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -3.02e-05 | 0.0198 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_0/bias | (768,) | 768 | 0.000815 | 0.21 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_0/scale | (768,) | 768 | 0.91 | 0.279 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_1/bias | (768,) | 768 | 0.0128 | 0.256 |\n", + "| img/Transformer/encoderblock_4/LayerNorm_1/scale | (768,) | 768 | 1.14 | 0.209 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.7 | 0.642 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 0.000135 | 0.0214 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00268 | 0.149 |\n", + "| img/Transformer/encoderblock_4/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -9.72e-07 | 0.0189 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0218 | 0.562 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 2e-05 | 0.025 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.0103 | 0.154 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 5.23e-06 | 0.019 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0216 | 0.725 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 1.28e-05 | 0.0253 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | -0.000919 | 0.0754 |\n", + "| img/Transformer/encoderblock_4/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -1.21e-05 | 0.0195 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_0/bias | (768,) | 768 | 0.004 | 0.199 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_0/scale | (768,) | 768 | 0.937 | 0.325 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_1/bias | (768,) | 768 | 0.00563 | 0.288 |\n", + "| img/Transformer/encoderblock_5/LayerNorm_1/scale | (768,) | 768 | 1.22 | 0.231 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.78 | 0.594 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 2.95e-05 | 0.0216 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00481 | 0.16 |\n", + "| img/Transformer/encoderblock_5/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -1.36e-06 | 0.0198 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0157 | 0.535 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 2.92e-05 | 0.0234 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.0106 | 0.149 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 1.08e-06 | 0.0204 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.00102 | 0.668 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 3.46e-05 | 0.0239 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0078 | 0.0672 |\n", + "| img/Transformer/encoderblock_5/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -7.83e-07 | 0.0212 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_0/bias | (768,) | 768 | 0.00578 | 0.175 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_0/scale | (768,) | 768 | 0.971 | 0.349 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_1/bias | (768,) | 768 | 0.00789 | 0.278 |\n", + "| img/Transformer/encoderblock_6/LayerNorm_1/scale | (768,) | 768 | 1.3 | 0.22 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.82 | 0.551 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | 4.07e-05 | 0.0216 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00527 | 0.191 |\n", + "| img/Transformer/encoderblock_6/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -7.42e-06 | 0.021 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0274 | 0.523 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | -9.41e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.00814 | 0.153 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 5.8e-06 | 0.0213 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | -0.0326 | 0.634 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | -4.62e-06 | 0.0231 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.00246 | 0.0661 |\n", + "| img/Transformer/encoderblock_6/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -3.42e-06 | 0.0221 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_0/bias | (768,) | 768 | 0.00394 | 0.19 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_0/scale | (768,) | 768 | 1.04 | 0.357 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_1/bias | (768,) | 768 | 0.0107 | 0.298 |\n", + "| img/Transformer/encoderblock_7/LayerNorm_1/scale | (768,) | 768 | 1.46 | 0.211 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -1.97 | 0.549 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -4.23e-05 | 0.0215 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00318 | 0.452 |\n", + "| img/Transformer/encoderblock_7/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.13e-05 | 0.0228 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | 0.0143 | 0.525 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 1.5e-05 | 0.0227 |\n", + "INFO:absl:\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.00723 | 0.188 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 9.01e-06 | 0.0218 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0222 | 0.576 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 7.02e-06 | 0.0229 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.0016 | 0.0952 |\n", + "| img/Transformer/encoderblock_7/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | -4.78e-06 | 0.0225 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_0/bias | (768,) | 768 | 0.00784 | 0.224 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_0/scale | (768,) | 768 | 1.13 | 0.296 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_1/bias | (768,) | 768 | 0.108 | 0.646 |\n", + "| img/Transformer/encoderblock_8/LayerNorm_1/scale | (768,) | 768 | 3.29 | 0.521 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -2.01 | 0.613 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -0.000557 | 0.0217 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00247 | 0.407 |\n", + "| img/Transformer/encoderblock_8/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | 1.16e-05 | 0.0236 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | -0.00489 | 0.508 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 9.53e-06 | 0.0228 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.00214 | 0.455 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | -3.41e-06 | 0.022 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.0533 | 0.646 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 2.5e-05 | 0.0225 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | -0.000388 | 0.125 |\n", + "| img/Transformer/encoderblock_8/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 1.94e-05 | 0.0229 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_0/bias | (768,) | 768 | 0.0155 | 0.265 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_0/scale | (768,) | 768 | 1.31 | 0.255 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_1/bias | (768,) | 768 | 0.297 | 1.35 |\n", + "| img/Transformer/encoderblock_9/LayerNorm_1/scale | (768,) | 768 | 7.01 | 0.96 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_0/bias | (3072,) | 3,072 | -2.14 | 0.514 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_0/kernel | (768, 3072) | 2,359,296 | -0.000968 | 0.0227 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_1/bias | (768,) | 768 | -0.00751 | 0.479 |\n", + "| img/Transformer/encoderblock_9/MlpBlock_0/Dense_1/kernel | (3072, 768) | 2,359,296 | -1.9e-06 | 0.0251 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/key/bias | (12, 64) | 768 | -0.00403 | 0.497 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/key/kernel | (768, 12, 64) | 589,824 | 1.15e-05 | 0.0224 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/out/bias | (768,) | 768 | -0.000966 | 0.233 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/out/kernel | (12, 64, 768) | 589,824 | 3.96e-06 | 0.0227 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/query/bias | (12, 64) | 768 | 0.00383 | 0.759 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/query/kernel | (768, 12, 64) | 589,824 | 2.61e-05 | 0.0221 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/value/bias | (12, 64) | 768 | 0.00333 | 0.111 |\n", + "| img/Transformer/encoderblock_9/MultiHeadDotProductAttention_0/value/kernel | (768, 12, 64) | 589,824 | 1.24e-05 | 0.024 |\n", + "| img/cls | (1, 1, 768) | 768 | 0.0472 | 0.959 |\n", + "| img/embedding/bias | (768,) | 768 | -0.00594 | 0.185 |\n", + "| img/embedding/kernel | (16, 16, 3, 768) | 589,824 | 7.7e-06 | 0.0157 |\n", + "| img/head/bias | (768,) | 768 | -0.00346 | 1.43 |\n", + "| img/head/kernel | (768, 768) | 589,824 | 5.02e-06 | 0.0398 |\n", + "| img/pos_embedding | (1, 196, 768) | 150,528 | -0.00125 | 0.344 |\n", + "| t | (1,) | 1 | 4.39 | 0.0 |\n", + "| txt/BertEncoder_0/embedder/embedders_position_ids/embedding | (512, 768) | 393,216 | -5.31e-05 | 0.0191 |\n", + "| txt/BertEncoder_0/embedder/embedders_segment_ids/embedding | (2, 768) | 1,536 | -0.00186 | 0.0445 |\n", + "| txt/BertEncoder_0/embedder/embedders_token_ids/embedding | (30522, 768) | 23,440,896 | -0.0278 | 0.0966 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/key/bias | (768,) | 768 | 0.00173 | 0.0804 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -5.01e-05 | 0.0453 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0172 | 0.487 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -8.24e-07 | 0.0453 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/value/bias | (768,) | 768 | 0.000783 | 0.0517 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -1.96e-05 | 0.031 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/dense_layer/bias | (768,) | 768 | -0.00214 | 0.0456 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -2.01e-05 | 0.0311 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/layer_norm/bias | (768,) | 768 | -0.023 | 0.352 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/attention_block/layer_norm/scale | (768,) | 768 | 0.982 | 0.172 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/dense_layer/bias | (768,) | 768 | -0.00283 | 0.0899 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -0.000185 | 0.0384 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/layer_norm/bias | (768,) | 768 | -0.0371 | 0.0578 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/layer_norm/scale | (768,) | 768 | 0.716 | 0.0649 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.209 | 0.0789 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_0/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000162 | 0.0395 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/key/bias | (768,) | 768 | -0.00332 | 0.0765 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 1.99e-05 | 0.0446 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/query/bias | (768,) | 768 | 0.018 | 0.266 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -0.000109 | 0.0444 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/value/bias | (768,) | 768 | 0.000419 | 0.0381 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -9.83e-06 | 0.0322 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/dense_layer/bias | (768,) | 768 | -0.000375 | 0.0332 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -1.98e-05 | 0.0311 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/layer_norm/bias | (768,) | 768 | -0.0277 | 0.177 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/attention_block/layer_norm/scale | (768,) | 768 | 0.929 | 0.107 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/dense_layer/bias | (768,) | 768 | -0.00157 | 0.054 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -7.92e-05 | 0.0389 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/layer_norm/bias | (768,) | 768 | -0.0381 | 0.0436 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/layer_norm/scale | (768,) | 768 | 0.85 | 0.0859 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.18 | 0.0889 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_1/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000555 | 0.0409 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/key/bias | (768,) | 768 | -0.00276 | 0.0785 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -1.02e-05 | 0.0451 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/query/bias | (768,) | 768 | -0.00648 | 0.277 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -4.15e-05 | 0.0446 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/value/bias | (768,) | 768 | 0.000935 | 0.0378 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -1.83e-06 | 0.0298 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/dense_layer/bias | (768,) | 768 | 0.000223 | 0.0434 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -7.34e-06 | 0.0299 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/layer_norm/bias | (768,) | 768 | -0.0451 | 0.204 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/attention_block/layer_norm/scale | (768,) | 768 | 0.689 | 0.109 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/dense_layer/bias | (768,) | 768 | -0.00139 | 0.107 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 6.03e-05 | 0.0434 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/layer_norm/bias | (768,) | 768 | -0.0359 | 0.0716 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/layer_norm/scale | (768,) | 768 | 0.88 | 0.0729 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.213 | 0.0613 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_10/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.00169 | 0.0427 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/key/bias | (768,) | 768 | -0.00566 | 0.0766 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 0.000108 | 0.0482 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/query/bias | (768,) | 768 | 0.0135 | 0.268 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -3.91e-05 | 0.0484 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/value/bias | (768,) | 768 | -2.05e-05 | 0.016 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -1.32e-05 | 0.0346 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/dense_layer/bias | (768,) | 768 | -4.59e-05 | 0.0201 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 9.95e-08 | 0.0344 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/layer_norm/bias | (768,) | 768 | -0.0645 | 0.147 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/attention_block/layer_norm/scale | (768,) | 768 | 0.711 | 0.0417 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/dense_layer/bias | (768,) | 768 | -0.00111 | 0.0884 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -1.39e-05 | 0.0346 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/layer_norm/bias | (768,) | 768 | -0.0794 | 0.116 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/layer_norm/scale | (768,) | 768 | 0.616 | 0.0297 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.172 | 0.0627 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_11/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.0027 | 0.0366 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/key/bias | (768,) | 768 | 0.00158 | 0.0787 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 3.48e-05 | 0.0444 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/query/bias | (768,) | 768 | 0.00346 | 0.193 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 2.12e-06 | 0.0442 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/value/bias | (768,) | 768 | 0.00371 | 0.0626 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -4.83e-06 | 0.0303 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/dense_layer/bias | (768,) | 768 | -2.89e-05 | 0.0633 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -9.67e-06 | 0.0292 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/layer_norm/bias | (768,) | 768 | -0.0236 | 0.166 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/attention_block/layer_norm/scale | (768,) | 768 | 0.905 | 0.0763 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/dense_layer/bias | (768,) | 768 | -0.00248 | 0.0592 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -7.06e-05 | 0.0394 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/layer_norm/bias | (768,) | 768 | -0.036 | 0.0455 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/layer_norm/scale | (768,) | 768 | 0.802 | 0.0567 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.18 | 0.0884 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_2/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000542 | 0.0415 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/key/bias | (768,) | 768 | -0.00127 | 0.0755 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | 3.12e-05 | 0.0414 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/query/bias | (768,) | 768 | -0.0105 | 0.204 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 3.34e-05 | 0.0416 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/value/bias | (768,) | 768 | 0.00178 | 0.0306 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -1.34e-05 | 0.0303 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/dense_layer/bias | (768,) | 768 | -0.00116 | 0.0364 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 1.66e-05 | 0.0297 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/layer_norm/bias | (768,) | 768 | -0.0146 | 0.149 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/attention_block/layer_norm/scale | (768,) | 768 | 0.897 | 0.217 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/dense_layer/bias | (768,) | 768 | -0.00258 | 0.0632 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -4.58e-05 | 0.0396 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/layer_norm/bias | (768,) | 768 | -0.0281 | 0.0433 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/layer_norm/scale | (768,) | 768 | 0.736 | 0.047 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.18 | 0.0783 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_3/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000188 | 0.0418 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/key/bias | (768,) | 768 | 0.0025 | 0.0828 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -3.43e-06 | 0.0411 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/query/bias | (768,) | 768 | 0.00266 | 0.215 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -3.55e-05 | 0.0414 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/value/bias | (768,) | 768 | -9.8e-05 | 0.0278 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -2.48e-05 | 0.0328 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/dense_layer/bias | (768,) | 768 | 0.000161 | 0.021 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -5.22e-06 | 0.0317 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/layer_norm/bias | (768,) | 768 | -0.0255 | 0.148 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/attention_block/layer_norm/scale | (768,) | 768 | 0.87 | 0.186 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/dense_layer/bias | (768,) | 768 | -0.00135 | 0.0519 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -2.25e-05 | 0.0397 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/layer_norm/bias | (768,) | 768 | -0.0268 | 0.0392 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/layer_norm/scale | (768,) | 768 | 0.757 | 0.0459 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.182 | 0.0968 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_4/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000773 | 0.0423 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/key/bias | (768,) | 768 | 0.000957 | 0.0788 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -0.000109 | 0.0416 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/query/bias | (768,) | 768 | -0.00894 | 0.199 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 0.000138 | 0.0417 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/value/bias | (768,) | 768 | 0.000613 | 0.0285 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | -8.91e-05 | 0.0347 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/dense_layer/bias | (768,) | 768 | 0.000115 | 0.019 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 1.99e-06 | 0.0335 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/layer_norm/bias | (768,) | 768 | -0.0237 | 0.134 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/attention_block/layer_norm/scale | (768,) | 768 | 0.872 | 0.159 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/dense_layer/bias | (768,) | 768 | -0.000342 | 0.053 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -1.83e-05 | 0.0404 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/layer_norm/bias | (768,) | 768 | -0.0257 | 0.0449 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/layer_norm/scale | (768,) | 768 | 0.748 | 0.0402 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.181 | 0.093 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_5/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000795 | 0.0425 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/key/bias | (768,) | 768 | 0.00393 | 0.0801 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -7.02e-05 | 0.0423 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/query/bias | (768,) | 768 | -0.0112 | 0.209 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 0.000111 | 0.0423 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/value/bias | (768,) | 768 | 0.000317 | 0.0351 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 5.99e-06 | 0.0351 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/dense_layer/bias | (768,) | 768 | 0.000102 | 0.0233 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 1.2e-05 | 0.0342 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/layer_norm/bias | (768,) | 768 | -0.026 | 0.14 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/attention_block/layer_norm/scale | (768,) | 768 | 0.849 | 0.125 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/dense_layer/bias | (768,) | 768 | -0.000724 | 0.0653 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -3.74e-06 | 0.0416 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/layer_norm/bias | (768,) | 768 | -0.0266 | 0.0467 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/layer_norm/scale | (768,) | 768 | 0.771 | 0.0392 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.178 | 0.0903 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_6/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.000836 | 0.0433 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/key/bias | (768,) | 768 | 0.00154 | 0.0742 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -3.73e-05 | 0.043 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/query/bias | (768,) | 768 | -0.0066 | 0.23 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 3.89e-05 | 0.0429 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/value/bias | (768,) | 768 | -0.00204 | 0.0397 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 2.6e-05 | 0.0357 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/dense_layer/bias | (768,) | 768 | 0.000126 | 0.0222 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -3.72e-06 | 0.035 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/layer_norm/bias | (768,) | 768 | -0.0382 | 0.145 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/attention_block/layer_norm/scale | (768,) | 768 | 0.805 | 0.105 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/dense_layer/bias | (768,) | 768 | -0.000484 | 0.0755 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | -4.84e-06 | 0.0436 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/layer_norm/bias | (768,) | 768 | -0.0271 | 0.0477 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/layer_norm/scale | (768,) | 768 | 0.787 | 0.0368 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.183 | 0.0784 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_7/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.00155 | 0.0443 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/key/bias | (768,) | 768 | -0.00364 | 0.0721 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -8.37e-05 | 0.0435 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/query/bias | (768,) | 768 | -0.0128 | 0.212 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | 0.000144 | 0.044 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/value/bias | (768,) | 768 | -0.00064 | 0.0414 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 3.29e-05 | 0.036 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/dense_layer/bias | (768,) | 768 | 0.000144 | 0.0389 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/dense_layer/kernel | (768, 768) | 589,824 | -1.05e-05 | 0.0366 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/layer_norm/bias | (768,) | 768 | -0.0412 | 0.159 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/attention_block/layer_norm/scale | (768,) | 768 | 0.75 | 0.106 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/dense_layer/bias | (768,) | 768 | -0.000383 | 0.0733 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 2.33e-06 | 0.0465 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/layer_norm/bias | (768,) | 768 | -0.0255 | 0.103 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/layer_norm/scale | (768,) | 768 | 0.782 | 0.0419 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.188 | 0.0604 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_8/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.00191 | 0.046 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/key/bias | (768,) | 768 | 0.00177 | 0.0769 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/key/kernel | (768, 768) | 589,824 | -2.25e-05 | 0.0445 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/query/bias | (768,) | 768 | 0.00698 | 0.221 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/query/kernel | (768, 768) | 589,824 | -9.69e-05 | 0.0451 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/value/bias | (768,) | 768 | -0.000478 | 0.0379 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/attention_layer/value/kernel | (768, 768) | 589,824 | 9.23e-06 | 0.0272 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/dense_layer/bias | (768,) | 768 | 0.000851 | 0.0372 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/dense_layer/kernel | (768, 768) | 589,824 | 6.89e-06 | 0.0278 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/layer_norm/bias | (768,) | 768 | -0.0508 | 0.206 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/attention_block/layer_norm/scale | (768,) | 768 | 0.66 | 0.0814 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/dense_layer/bias | (768,) | 768 | -0.00159 | 0.1 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/dense_layer/kernel | (3072, 768) | 2,359,296 | 3.22e-05 | 0.0476 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/layer_norm/bias | (768,) | 768 | -0.0281 | 0.0884 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/layer_norm/scale | (768,) | 768 | 0.782 | 0.0604 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/mlp/dense_layer/bias | (3072,) | 3,072 | -0.202 | 0.0655 |\n", + "| txt/BertEncoder_0/encoder_block/layer_sequence/layers_9/mlp_block/mlp/dense_layer/kernel | (768, 3072) | 2,359,296 | 0.00262 | 0.0463 |\n", + "INFO:absl:\n", + "| txt/BertEncoder_0/layer_norm/bias | (768,) | 768 | -0.0183 | 0.0679 |\n", + "| txt/BertEncoder_0/layer_norm/scale | (768,) | 768 | 0.836 | 0.125 |\n", + "| txt/head/bias | (768,) | 768 | -0.0015 | 0.0879 |\n", + "| txt/head/kernel | (768, 768) | 589,824 | 7.58e-05 | 0.0409 |\n", + "+-------------------------------------------------------------------------------------------------------+------------------+------------+-----------+--------+\n", + "Total: 195,870,721\n", + "INFO:absl:NOTE: Replicating...\n", + "INFO:absl:NOTE: Initializing evaluator: disclf...\n", + "INFO:absl:Using 81 prompts_templates: ['a bad photo of a {}', 'a photo of many {}', 'a sculpture of a {}', 'a photo of the hard to see {}', 'a low resolution photo of the {}', 'a rendering of a {}', 'graffiti of a {}', 'a bad photo of the {}', 'a cropped photo of the {}', 'a tattoo of a {}', 'the embroidered {}', 'a photo of a hard to see {}', 'a bright photo of a {}', 'a photo of a clean {}', 'a photo of a dirty {}', 'a dark photo of the {}', 'a drawing of a {}', 'a photo of my {}', 'the plastic {}', 'a photo of the cool {}', 'a closeup photo of a {}', 'a black and white photo of the {}', 'a painting of the {}', 'a painting of a {}', 'a pixelated photo of the {}', 'a sculpture of the {}', 'a bright photo of the {}', 'a cropped photo of a {}', 'a plastic {}', 'a photo of the dirty {}', 'a jpeg corrupted photo of a {}', 'a blurry photo of the {}', 'a photo of the {}', 'a good photo of the {}', 'a rendering of the {}', 'a {} in a video game', 'a photo of one {}', 'a doodle of a {}', 'a closeup photo of the {}', 'a photo of a {}', 'the origami {}', 'the {} in a video game', 'a sketch of a {}', 'a doodle of the {}', 'a origami {}', 'a low resolution photo of a {}', 'the toy {}', 'a rendition of the {}', 'a photo of the clean {}', 'a photo of a large {}', 'a rendition of a {}', 'a photo of a nice {}', 'a photo of a weird {}', 'a blurry photo of a {}', 'a cartoon {}', 'art of a {}', 'a sketch of the {}', 'a embroidered {}', 'a pixelated photo of a {}', 'itap of the {}', 'a jpeg corrupted photo of the {}', 'a good photo of a {}', 'a plushie {}', 'a photo of the nice {}', 'a photo of the small {}', 'a photo of the weird {}', 'the cartoon {}', 'art of the {}', 'a drawing of the {}', 'a photo of the large {}', 'a black and white photo of a {}', 'the plushie {}', 'a dark photo of a {}', 'itap of a {}', 'graffiti of the {}', 'a toy {}', 'itap of my {}', 'a photo of a cool {}', 'a photo of a small {}', 'a tattoo of the {}', '{}']\n", + "INFO:absl:Load dataset info from /root/tensorflow_datasets/oxford_iiit_pet/3.2.0\n", + "INFO:absl:Using 37 class_names: ['abyssinian', 'american bulldog', 'american pit bull terrier', 'basset hound', 'beagle', 'bengal', 'birman', 'bombay', 'boxer', 'british shorthair', 'chihuahua', 'egyptian mau', 'english cocker spaniel', 'english setter', 'german shorthaired', 'great pyrenees', 'havanese', 'japanese chin', 'keeshond', 'leonberger', 'maine coon', 'miniature pinscher', 'newfoundland', 'persian', 'pomeranian', 'pug', 'ragdoll', 'russian blue', 'saint bernard', 'samoyed', 'scottish terrier', 'shiba inu', 'siamese', 'sphynx', 'staffordshire bull terrier', 'wheaten terrier', 'yorkshire terrier']\n", + "INFO:absl:Load dataset info from /root/tensorflow_datasets/oxford_iiit_pet/3.2.0\n", + "INFO:absl:Constructing tf.data.Dataset oxford_iiit_pet for split _EvenSplit(split='test', index=0, count=1, drop_remainder=False), from /root/tensorflow_datasets/oxford_iiit_pet/3.2.0\n", + "INFO:absl:Data before pre-processing:\n", + "{'file_name': \u003ctf.Tensor 'args_0:0' shape=() dtype=string\u003e, 'image': \u003ctf.Tensor 'args_1:0' shape=(None, None, 3) dtype=uint8\u003e, 'label': \u003ctf.Tensor 'args_2:0' shape=() dtype=int64\u003e, 'segmentation_mask': \u003ctf.Tensor 'args_3:0' shape=(None, None, 1) dtype=uint8\u003e, 'species': \u003ctf.Tensor 'args_4:0' shape=() dtype=int64\u003e}\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "INFO:absl:Data after pre-processing:\n", + "{'image': \u003ctf.Tensor 'add:0' shape=(224, 224, 3) dtype=float32\u003e, 'label': \u003ctf.Tensor 'args_2:0' shape=() dtype=int64\u003e}\n", + "INFO:absl:Data before pre-processing:\n", + "{'label': \u003ctf.Tensor 'args_0:0' shape=() dtype=int64\u003e, 'texts': \u003ctf.Tensor 'args_1:0' shape=() dtype=string\u003e}\n", + "INFO:absl:Data after pre-processing:\n", + "{'label': \u003ctf.Tensor 'args_0:0' shape=() dtype=int64\u003e, 'labels': \u003ctf.Tensor 'strided_slice_4:0' shape=(16,) dtype=int32\u003e}\n", + "WARNING:tensorflow:From /usr/local/lib/python3.9/dist-packages/tensorflow/python/autograph/pyct/static_analysis/liveness.py:83: Analyzer.lamba_check (from tensorflow.python.autograph.pyct.static_analysis.liveness) is deprecated and will be removed after 2023-09-23.\n", + "Instructions for updating:\n", + "Lambda fuctions will be no more assumed to be used in the statement where they are used, or at least in the same block. https://github.com/tensorflow/tensorflow/issues/56089\n", + "INFO:absl:Initialized evaluator in 3.0 seconds\n", + "INFO:absl:NOTE: disclf evaluation step 0...\n", + "INFO:absl:Starting text embedding...\n", + "INFO:absl:Compiled text embeddings in 7.6s\n", + "INFO:absl:Embedded oxford_iiit_pet text in 7 steps - ...[512 512 512 512 512 437 0]\n", + "INFO:absl:Totalling 2997 text in 2.6s\n", + "INFO:absl:Total texts embeddings size 9.2M\n", + "INFO:absl:Starting image embedding...\n", + "INFO:absl:Compiled image embeddings in 17.5s\n", + "INFO:absl:Embedded oxford_iiit_pet image in 9 steps - ...[512 512 512 512 512 512 512 85 0]\n", + "INFO:absl:Totalling 3669 image in 46.1s\n", + "INFO:absl:Dataset oxford_iiit_pet, results {'accuracy': 0.8103025347506132, 'correct': 2973, 'count': 3669}\n", + "INFO:absl:\u001b[35m[0]\u001b[0m z/0shot/oxford_iiit_pet_accuracy = 0.8103025347506132\n", + "INFO:absl:\u001b[35m[0]\u001b[0m z/secs/eval/disclf = 73.91012993699997\n", + "INFO:absl:TIMING[z/secs/eval/disclf]: 73.91012993699997\n", + "INFO:absl:NOTE: Done!\n" + ] + } + ], + "source": [ + "# Should run in ~5 minutes on a T4 GPU...\n", + "set_max_height(444)\n", + "eval_only.main([])" + ] + }, + { + "cell_type": "code", + "source": [ + "# ... and yield a final 81% accuracy.\n", + "import json\n", + "json.loads(open(flags.FLAGS.workdir + '/big_vision_metrics.txt').readline())" + ], + "metadata": { + "id": "J14NHGBAGXYp", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "0d5af276-a60f-44cd-c25f-0bf54f0e7a93" + }, + "execution_count": 26, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "{'step': 0,\n", + " 'z/0shot/oxford_iiit_pet_accuracy': 0.8103025347506132,\n", + " 'z/secs/eval/disclf': 73.91012993699997}" + ] + }, + "metadata": {}, + "execution_count": 26 + } + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "name": "lit OSS", + "provenance": [] + }, + "gpuClass": "standard", + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/big_vision/configs/proj/image_text/siglip_lit_coco.py b/big_vision/configs/proj/image_text/siglip_lit_coco.py new file mode 100644 index 0000000000000000000000000000000000000000..f1dbd472a1d440351b3e176696873fc566797ffd --- /dev/null +++ b/big_vision/configs/proj/image_text/siglip_lit_coco.py @@ -0,0 +1,115 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Minimal SigLIP (https://arxiv.org/abs/2303.15343) example. + +Example training: + +big_vision.trainers.proj.image_text.siglip \ + --config big_vision/configs/proj/image_text/lit_coco.py:batch_size=512 \ + --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%Y-%m-%d_%H%M'` +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.image_text import common +from ml_collections import ConfigDict + + +def get_config(arg=None): + """The base configuration.""" + arg = bvcc.parse_arg( + arg, res=224, runlocal=False, token_len=16, txt='bert_base', img='B/16', + init='', img_head=False, batch_size=512) + img_name, img_init = common.inits[arg.img] + txt_name, txt_init = common.inits[arg.txt] + config = ConfigDict() + + config.input = {} + config.input.data = dict(name='coco_captions', split='train') + config.input.batch_size = arg.batch_size if not arg.runlocal else 32 + config.input.shuffle_buffer_size = 250_000 if not arg.runlocal else 50 + + config.total_steps = 5_000 if not arg.runlocal else 1 + + config.init_shapes = [(1, arg.res, arg.res, 3), (1, arg.token_len,)] + config.init_types = ['float32', 'int32'] + + if arg.init: + vocab_path = arg.init.rsplit('.', 1)[0] + '.txt' + else: + vocab_path = f'{txt_init}/vocab.txt' + tokenizer = lambda inkey: ( + f'bert_tokenize(inkey="{inkey}", max_len={arg.token_len}, ' + f'vocab_path="{vocab_path}")') + config.input.pp = ( + f'decode|resize({arg.res})|flip_lr|randaug(2,10)|value_range(-1,1)' + f'|flatten|{tokenizer("captions/text")}|keep("image", "labels")' + ) + config.pp_modules = ['ops_general', 'ops_image', 'ops_text', + 'proj.flaxformer.bert_ops', 'archive.randaug'] + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'proj.image_text.two_towers' + config.model_load = {} + if arg.init: + config.model_init = arg.init + else: + config.model_init = {'image': img_init, 'text': txt_init} + config.model_load['txt_load_kw'] = {'dont_load': ['head/kernel', 'head/bias']} + if not arg.img_head: + config.model_load['img_load_kw'] = {'dont_load': ['head/kernel', 'head/bias']} + config.model = ConfigDict() + config.model.image_model = 'vit' + config.model.text_model = 'proj.flaxformer.bert' + config.model.image = ConfigDict({ + 'variant': img_name, + 'pool_type': 'tok', + 'head_zeroinit': False, + }) + config.model.text = ConfigDict({ + 'config': txt_name, + 'head_zeroinit': False, + }) + config.model.temperature_init = 10.0 + dim = {'B': 768, 'L': 1024}[arg.img[0]] + config.model.out_dim = (dim if arg.img_head else None, dim) # (image_out_dim, text_out_dim) + config.model.bias_init = -2.71 + + if txt_name == 'base': + config.optax_name = 'scale_by_adam' + else: + config.optax_name = 'big_vision.scale_by_adafactor' + + config.lr = 0.001 + config.wd = 0.01 + warmup_steps = max(int(0.03 * config.total_steps), 100) + config.schedule = [ + ('img/.*', None), # Freezes image tower. + ('.*', dict(decay_type='cosine', warmup_steps=warmup_steps)), + ] + + config.grad_clip_norm = 1.0 + + config.evals = {} + config.evals.retrieval_coco = common.get_coco( + pp_img=f'resize({arg.res})|value_range(-1, 1)', + pp_txt=tokenizer('texts'), + log_steps=1000, + ) + + return config diff --git a/big_vision/configs/proj/paligemma/README.md b/big_vision/configs/proj/paligemma/README.md new file mode 100644 index 0000000000000000000000000000000000000000..1ebe888c65eb4602e0e6849821cddd901e510669 --- /dev/null +++ b/big_vision/configs/proj/paligemma/README.md @@ -0,0 +1,270 @@ +# PaliGemma model README + +PaliGemma is an open vision-language model (VLM) inspired by PaLI-3, built with +open components, such as +the [SigLIP vision model](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/SigLIP_demo.ipynb) +and +the [Gemma language model](https://ai.google.dev/gemma). +PaliGemma is designed as a versatile model for transfer to a wide range of +vision-language tasks such as image and short video caption, visual question +answering, text reading, object detection and object segmentation. Together with +the pretrained and transfer checkpoints at multiple resolutions, we provide a +checkpoint transferred to a mixture of tasks that can be used for off-the-shelf +exploration. + +## Quick Reference + +This is the reference repository of the model, you may also want to check out the resources on + + - [ArXiv](https://arxiv.org/abs/2407.07726): Technical report. + - [Kaggle](https://www.kaggle.com/models/google/paligemma): + All pre-trained / mix checkpoints and model card. + - [Kaggle-FT](https://www.kaggle.com/models/google/paligemma-ft): + All fine-tuned checkpoints and model card. + - [VertexAI Model Garden](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/363): + Paligemma models on GCP. + - [Hugging Face](https://huggingface.co/google/paligemma-3b-pt-224): + PyTorch port of paligemma models. + - [Light finetuning colab](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb): + Lightweight colab for fine-tuning PaliGemma. It can be run on a single T4 GPU (16GB) + available on free Colab. + - [HuggingFace demo](https://hf.co/spaces/google/paligemma): live demo. + +### Citation BibTeX + +``` +@article{beyer2024paligemma, + title={{PaliGemma: A versatile 3B VLM for transfer}}, + author={Lucas Beyer and Andreas Steiner and André Susano Pinto and Alexander Kolesnikov and Xiao Wang and Daniel Salz and Maxim Neumann and Ibrahim Alabdulmohsin and Michael Tschannen and Emanuele Bugliarello and Thomas Unterthiner and Daniel Keysers and Skanda Koppula and Fangyu Liu and Adam Grycner and Alexey Gritsenko and Neil Houlsby and Manoj Kumar and Keran Rong and Julian Eisenschlos and Rishabh Kabra and Matthias Bauer and Matko Bošnjak and Xi Chen and Matthias Minderer and Paul Voigtlaender and Ioana Bica and Ivana Balazevic and Joan Puigcerver and Pinelopi Papalampidi and Olivier Henaff and Xi Xiong and Radu Soricut and Jeremiah Harmsen and Xiaohua Zhai}, + year={2024}, + journal={arXiv preprint arXiv:2407.07726} +} +``` + +## Model description + +### Overview + +PaliGemma-3B is Vision-Language model that was inspired by the PaLI-3 recipe. +It is built on SigLIP visual encoder (specifically, SigLIP-So400m/14) and the +Gemma 2B language model. PaliGemma takes as input one or more images, +which are turned into "soft tokens" by the SigLIP encoder, and input text +(codenamed the "prefix") that is tokenized by Gemma's tokenizer. The image +tokens and prefix tokens are concatenated (in this order) and passed to the +Gemma decoder with full block-attention, which then generates an output text +(the "suffix") auto-regressively with masked attention. + +![PaliGemma model](paligemma.png) + +### Training stages + +Similar to PaLI-3, PaliGemma's training consists of multiple stages: + + - Stage 0: the unimodal pre-training. We use publicly available off-the-shelf + SigLIP and Gemma models which have been pre-trained unimodally by their + respective authors. + - Stage 1: multimodal pre-training. The combined PaliGemma model is now + pre-trained on a fully multimodal training dataset, this at a low resolution + of 224px² and prefix+suffix sequence length of 128 tokens. This results in + the first base model that we release. + - Stage 2: high-resolution pre-training. We continue pre-training of the + Stage 1 model at resolution 448px² with sequence length 512 tokens for a short + duration on the same multimodal training data, but re-weighted with more + emphasis on examples that make use of higher resolution or longer sequence + length. We repeat this once more at resolution 896px². This results in two + further "high res" base models that we also release. + - Stage 3: fine-tune. The base models are transferred to + specific tasks by fine-tuning. To facilitate further research and + reproducibility, we release checkpoints fine-tuned on most of the benchmarks + we evaluate on. We also provide a "mix" transfer model, fine-tuned on a wide + variety of data, for use in interactive demos. + +Most of the code examples, use-cases, and code release are about Stage 3: +transferring to a task or dataset of interest to the user. + +### Tokenizer + +PaliGemma uses the Gemma tokenizer with 256'000 tokens, but we further extend +its vocabulary with 1024 entries that represent coordinates in normalized +image-space (\...\), and another with 128 entries +(\...\) that are codewords used by a lightweight +referring-expression segmentation vector-quantized variational auto-encoder +(VQ-VAE) with the architecture of [Ning et al. (2023)](https://arxiv.org/abs/2301.02229) and trained on OpenImages +as in PaLI-3. While the `big_vision` codebase is flexible enough to extend +tokenizers on-the-fly, we also provide a SentencePiece model file of the Gemma +tokenizer with these additional tokens baked in, for the convenience of +other codebases. + +## Checkpoints + +The PaliGemma models are released under the same open license as the Gemma +models, and hence require manual acknowledgement of the license terms on kaggle: +https://www.kaggle.com/models/google/paligemma. The reference checkpoints are +available on +[Kaggle](https://www.kaggle.com/models/google/paligemma), +[VertexAI Model Garden](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/363) and +[Hugging Face](https://huggingface.co/google/paligemma-3b-pt-224). + +### Pretrained checkpoints + +Use one of these checkpoints as initialization for fine-tuning: + + - pt-224: Versatile pretrained model for tasks that do not require seeing + small details in the image. + Examples: natural image captioning and question-answering, detection and + segmentation of medium-large objects. This model was trained with + sequence length 128. + - pt-448: Versatile base model for mid/higher resolution tasks with access + to smaller details. Besides higher resolution, it has gotten more weight on + text reading, detection, and segmentation during its pre-training. Examples: + as above, plus detection, segmentation, text/diagram reading. This model was + trained with sequence length 512. + - pt-896: Further scaled-up version of pt-448, especially good at reading + very small texts as often found in documents and infographics. This model + was trained with sequence length 512. + +Besides the reference float32 checkpoint (11GB), we further provide +bfloat16 and float16 variants of each, to reduce download and storage time. +These are good for inference and frozen transfers, but full fine-tuning +should happen in float32 or mixed precision. + +### Mixture checkpoint + +This checkpoint is trained on a mixture of all our transfer tasks, +with a balancing intended to make it "nice to use" out of the box for +predictions. This model is multilingual and should +understand prompts in various languages, although English +is still its "mother tongue". +Questions can be asked in a natural way (including asking for a caption or +reading the text), and detection and segmentation should still work with the +structured `detect {things}` and `segment {things}` prompts as in the base model. + + - mix-224: Similarly to pt-224, this model is good at many natural image + tasks that do not require high resolution. Unlike the raw pre-trained model, + however, it can be interacted with more freely. For example, ask it to + "describe this image in great detail, please" or "How many coins do you see + in the picture?". This model was trained with sequence length 256. + - mix-448: As above, but it is better at tasks that require higher-resolution + input. For example, one could ask it "what is written in the "sum" field?", + to "describe this figure", or to "what is the GDP of France?" when shown an + infographic of countries' GDPs. This model was trained with + sequence length 512. + +### Transfers results and checkpoints + +We provide checkpoints transferred to most of the tasks we evaluated +transfer on, see the [kaggle page](https://www.kaggle.com/models/google/paligemma). +These are intended for use when a specialised model corresponding +to one of the tasks is needed, for academic research purposes only. +Depending on the task, they may require a specialised preprocessing format. + +The transfer setup is reasonably unified, with the main factors of variation +being the training duration, learning-rate, and whether or not to use dropout +and label-smoothing. Details can be found in the corresponding config files or +in an upcoming tech report. + +Importantly, none of these tasks or datasets are part of the pre-training data +mixture, and their images are explicitly removed from the web-scale +pretraining data. + +#### Captioning + +Benchmark (train split) | Metric (split) | pt-224 | pt-448 | pt-896 +-----------------------|----------------|--------|--------|-------- +[COCO captions](https://cocodataset.org/#home) (train+restval) | CIDEr (val) | 141.92 | 144.60 | +[NoCaps](https://nocaps.org/) (Eval of COCO captions transfer) | CIDEr (val) | 121.72 | 123.58 | +[COCO-35L](https://arxiv.org/abs/2205.12522) (train) | CIDEr dev (en / avg-34 / avg) | 139.2 / 115.8 / 116.4 | 141.2 / 118.0 / 118.6 | +[XM3600](https://arxiv.org/abs/2205.12522) (Eval of COCO-35L transfer) | CIDEr test (en / avg-35 / avg) | 78.1 / 41.3 / 42.4 | 80.0 / 41.9 / 42.9 | +[TextCaps](https://textvqa.org/textcaps/) (train) | CIDEr (val) | 127.48 | 153.94 | +[SciCap](https://arxiv.org/abs/2110.11624) (first sentence, no subfigure) (train+val) | CIDEr / BLEU-4 (test) | 162.25 / 0.192 | 181.49 / 0.211 | +[Screen2words](https://arxiv.org/abs/2108.03353) (train+dev) | CIDEr (test) | 117.57 | 119.59 | +[Widget Captioning](https://arxiv.org/abs/2010.04295) (train+dev) | CIDEr (test) | 136.07 | 148.36 | + +#### Question Answering + +Benchmark (train split) | Metric (split) | pt-224 | pt-448 | pt-896 +-----------------------|----------------|--------|--------|-------- +[VQAv2](https://visualqa.org/index.html) (train+validation) | Accuracy (Test server - std) | 83.19 | 85.64 | +[MMVP](https://arxiv.org/abs/2401.06209) (Eval of VQAv2 transfer) | Paired Accuracy | 47.33 | 45.33 | +[POPE](https://arxiv.org/abs/2305.10355) (Eval of VQAv2 transfer) | Accuracy (random / popular / adversarial) | 87.80 / 85.87 / 84.27 | 88.23 / 86.77 / 85.90 | +[Objaverse Multiview](https://arxiv.org/abs/2311.17851) (Eval of VQAv2 transfer) | Cosine Similarity (USEv4) | 62.7 | 62.8 | +[OKVQA](https://okvqa.allenai.org/) (train) | Accuracy (val) | 63.54 | 63.15 | +[A-OKVQA](https://allenai.org/project/a-okvqa/home) (MC) (train+val) | Accuracy (Test server) | 76.37 | 76.90 | +[A-OKVQA](https://allenai.org/project/a-okvqa/home) (DA) (train+val) | Accuracy (Test server) | 61.85 | 63.22 | +[GQA](https://cs.stanford.edu/people/dorarad/gqa/about.html) (train_balanced+val_balanced) | Accuracy (testdev balanced) | 65.61 | 67.03 | +[xGQA](https://aclanthology.org/2022.findings-acl.196/) (Eval of GQA transfer) | Mean Accuracy (bn,de,en,id,ko,pt,ru,zh) | 58.37 | 59.07 | +[NLVR2](https://lil.nlp.cornell.edu/nlvr/) (train+dev) | Accuracy (test) | 90.02 | 88.93 | +[MaRVL](https://marvl-challenge.github.io/) (Eval of NLVR2 transfer) | Mean Accuracy (test) (id,sw,ta,tr,zh) | 80.57 | 76.78 | +[AI2D](https://allenai.org/data/diagrams) (train) | Accuracy (test) | 72.12 | 73.28 | +[ScienceQA](https://scienceqa.github.io/) (Img subset, no CoT) (train+val) | Accuracy (test) | 95.39 | 95.93 | +[RSVQA-LR](https://zenodo.org/records/6344334) (Non numeric) (train+val) | Mean Accuracy (test) | 92.65 | 93.11 | +[RSVQA-HR](https://zenodo.org/records/6344367) (Non numeric) (train+val) | Mean Accuracy (test/test2) | 92.61 / 90.58 | 92.79 / 90.54 | +[ChartQA](https://arxiv.org/abs/2203.10244) (human+aug)x(train+val) | Mean Relaxed Accuracy (test_human, test_aug) | 57.08 | 71.36 | +[VizWiz](https://vizwiz.org/tasks-and-datasets/vqa/) VQA (train+val) | Accuracy (Test server - std) | 73.7 | 75.52 | +[TallyQA](https://arxiv.org/abs/1810.12440) (train) | Accuracy (test_simple/test_complex) | 81.72 / 69.56 | 84.86 / 72.27 | +[OCR-VQA](https://ocr-vqa.github.io/) (train+val) | Accuracy (test) | 73.24 | 75.60 | 75.90 +[TextVQA](https://textvqa.org/) (train+val) | Accuracy (Test server - std) | 55.47 | 73.15 | 76.48 +[DocVQA](https://www.docvqa.org/) (train+val) | ANLS (Test server) | 43.74 | 78.02 | 84.77 +[Infographic VQA](https://openaccess.thecvf.com/content/WACV2022/papers/Mathew_InfographicVQA_WACV_2022_paper.pdf) (train+val) | ANLS (Test server) | 28.46 | 40.47 | 47.75 +[SceneText VQA](https://arxiv.org/abs/1905.13648) (train+val) | ANLS (Test server) | 63.29 | 81.82 | 84.40 + +#### Segmentation + +Benchmark (train split) | Metric (split) | pt-224 | pt-448 | pt-896 +-----------------------|----------------|--------|--------|-------- +[RefCOCO](https://arxiv.org/abs/1608.00272) (combined refcoco, refcoco+, refcocog excluding val and test images) | MIoU (validation) refcoco / refcoco+ / refcocog | 73.40 / 68.32 / 67.65 | 75.57 / 69.76 / 70.17 | 76.94 / 72.18 / 72.22 + +#### Video tasks (Caption/QA) + +Benchmark (train split) | Metric (split) | pt-224 | pt-448 | pt-896 +-----------------------|----------------|--------|--------|-------- +[MSR-VTT](https://www.microsoft.com/en-us/research/publication/msr-vtt-a-large-video-description-dataset-for-bridging-video-and-language/) (Captioning) | CIDEr (test) | 70.54 | +[MSR-VTT](https://www.microsoft.com/en-us/research/publication/msr-vtt-a-large-video-description-dataset-for-bridging-video-and-language/) (QA) | Accuracy (test) | 50.09 | +[ActivityNet](http://activity-net.org/) (Captioning)] | CIDEr (test) | 34.62 | +[ActivityNet](http://activity-net.org/) (QA) | Accuracy (test) | 50.78 | +[VATEX](https://eric-xw.github.io/vatex-website/about.html) (Captioning) | CIDEr (test) | 79.73 | +[MSVD](https://www.cs.utexas.edu/users/ml/clamp/videoDescription/) (QA) | Accuracy (test) | 60.22 | + +#### Mix model (finetune on mixture of transfer tasks) + +Benchmark | Metric (split) | mix-224 | mix-448 +----------|----------------|---------|--------- +[MMVP](https://arxiv.org/abs/2401.06209) | Paired Accuracy | 46.00 | 45.33 +[POPE](https://arxiv.org/abs/2305.10355) | Accuracy (random / popular / adversarial) | 88.00 / 86.63 / 85.67 | 89.37 / 88.40 / 87.47 + + +## How to run PaliGemma fine-tuning + +To run PaliGemma fine-tuning, set up the `big_vision` repository by following the +main README file. Here we provide PaliGemma-specific instructions. + +Checkpoints can be downloaded from Kaggle. You need to create an account and acknowledge checkpoint usage policy. You can then download any checkpoint: + +``` +export KAGGLE_USERNAME= +export KAGGLE_KEY= + +# See https://www.kaggle.com/models/google/paligemma for a full list of models. +export MODEL_NAME=paligemma-3b-pt-224 +export CKPT_FILE=paligemma-3b-pt-224.npz + +mkdir ckpts/ +cd ckpts/ + +curl -L -u $KAGGLE_USERNAME:$KAGGLE_KEY\ + -o pt_224.npz \ + https://www.kaggle.com/api/v1/models/google/paligemma/jax/$MODEL_NAME/1/download/$CKPT_FILE +``` + +As an example, we provide the `forkme.py` config that is based on the easily-adjustable jsonl data source: + +``` +BV_GEMMA_DIR=ckpts/ python -m big_vision.trainers.proj.paligemma.train --config big_vision/configs/proj/paligemma/transfers/forkme.py --workdir workdirs/`date '+%m-%d_%H%M'` +``` + +If you want to use TFDS-based data, check out other transfer configs. Remember to set `TFDS_DATA_DIR` to point to the folder with data (can be GCP data bucket). + + +## Model Development Contributions + +See the [technical report](https://arxiv.org/abs/2407.07726)'s Appendix. diff --git a/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb b/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3047d666827681dda62f01c5b95df35c1ad95bfe --- /dev/null +++ b/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb @@ -0,0 +1,1167 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "wR53lePHuiP-" + }, + "source": [ + "# Finetune PaliGemma\n", + "\n", + "> *These models and code are not official Google products and were trained and released for research purposes.*\n", + "\n", + "\n", + "**This notebook shows how to finetune PaliGemma on a vision-language task.**\n", + "The training data consists of 90 pairs of images and long captions describing them.\n", + "To make it runnable on a T4 colab runtime with 16GB HBM and 12GB RAM, we opt to only finetune the attention layers of the language model and freeze the other parameters.\n", + "\n", + " **This setup is illustrative**. In a real usecase, the amount of data, trainable parameters, training steps and hyper-parameters and obtained results could be significantly different.\n", + "\n", + "This notebook uses the model reference implementation from [big_vision](https://github.com/google-research/big_vision).\n", + "and shows how to:\n", + "\n", + " * Install deps, download model checkpoint and training data.\n", + " * Load the model onto GPU devices.\n", + " * Prepare the input to the model for training and inference.\n", + " * Finetune the model and inspect output in validation split." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6U0QUFveqSP2" + }, + "source": [ + "## Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "id": "DfxKb3F839Ks", + "outputId": "d02e98d5-8334-463f-f529-6292dd73b04b", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 kB\u001b[0m \u001b[31m3.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m43.2/43.2 kB\u001b[0m \u001b[31m6.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Building wheel for ml_collections (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + ] + } + ], + "source": [ + "# @title Fetch big_vision code and install dependencies.\n", + "import os\n", + "import sys\n", + "\n", + "# TPUs with\n", + "if \"COLAB_TPU_ADDR\" in os.environ:\n", + " raise \"It seems you are using Colab with remote TPUs which is not supported.\"\n", + "\n", + "# Fetch big_vision repository if python doesn't know about it and install\n", + "# dependencies needed for this notebook.\n", + "if not os.path.exists(\"big_vision_repo\"):\n", + " !git clone --quiet --branch=main --depth=1 \\\n", + " https://github.com/google-research/big_vision big_vision_repo\n", + "\n", + "# Append big_vision code to python import path\n", + "if \"big_vision_repo\" not in sys.path:\n", + " sys.path.append(\"big_vision_repo\")\n", + "\n", + "# Install missing dependencies. Assume jax~=0.4.25 with GPU available.\n", + "!pip3 install -q \"overrides\" \"ml_collections\" \"einops~=0.7\" \"sentencepiece\"\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "azmRZvgGyhAb" + }, + "source": [ + "### Configure your API key to access Kaggle\n", + "\n", + "To use PaliGemma, you must provide your Kaggle username and a Kaggle API key.\n", + "\n", + "1. To generate a Kaggle API key, go to the **Account** tab of your Kaggle user profile and select **Create New Token**. This will trigger the download of a `kaggle.json` file containing your API credentials.\n", + "1. In Colab, select **Secrets** (🔑) in the left pane and add your Kaggle username and Kaggle API key. Store your username under the name `KAGGLE_USERNAME` and your API key under the name `KAGGLE_KEY`.\n", + "\n", + "To be able to download, you will also need to acknowledge the Terms and Conditions of the PaliGemma on:\n", + "\n", + "* https://www.kaggle.com/models/google/paligemma/\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "zGLIp1Cx3_CX" + }, + "outputs": [], + "source": [ + "import os\n", + "from google.colab import userdata\n", + "\n", + "# Note: `userdata.get` is a Colab API. If you're not using Colab, set the env\n", + "# vars as appropriate or make your credentials available in ~/.kaggle/kaggle.json\n", + "\n", + "os.environ[\"KAGGLE_USERNAME\"] = userdata.get('KAGGLE_USERNAME')\n", + "os.environ[\"KAGGLE_KEY\"] = userdata.get('KAGGLE_KEY')" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gQNOTfF24AV4", + "outputId": "54f8aeed-bdbd-4ab3-941b-373392591505" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading the checkpoint from Kaggle, this could take a few minutes....\n" + ] + }, + { + "output_type": "stream", + "name": "stderr", + "text": [ + "Downloading from https://www.kaggle.com/api/v1/models/google/paligemma/jax/paligemma-3b-pt-224/1/download/paligemma-3b-pt-224.f16.npz...\n", + "100%|██████████| 5.45G/5.45G [01:00<00:00, 95.9MB/s]\n" + ] + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Model path: /root/.cache/kagglehub/models/google/paligemma/jax/paligemma-3b-pt-224/1/./paligemma-3b-pt-224.f16.npz\n", + "Downloading the model tokenizer...\n", + "Copying gs://big_vision/paligemma_tokenizer.model...\n", + "- [1 files][ 4.1 MiB/ 4.1 MiB] \n", + "Operation completed over 1 objects/4.1 MiB. \n", + "Tokenizer path: ./paligemma_tokenizer.model\n", + "Downloading the dataset...\n", + "Data path: ./longcap100\n" + ] + } + ], + "source": [ + "# @title Download checkpoint, tokenizer and dataset to local filesystem.\n", + "#\n", + "import os\n", + "import kagglehub\n", + "\n", + "MODEL_PATH = \"./paligemma-3b-pt-224.f16.npz\"\n", + "if not os.path.exists(MODEL_PATH):\n", + " print(\"Downloading the checkpoint from Kaggle, this could take a few minutes....\")\n", + " # Note: kaggle archive contains the same checkpoint in multiple formats.\n", + " # Download only the float16 model.\n", + " MODEL_PATH = kagglehub.model_download('google/paligemma/jax/paligemma-3b-pt-224', MODEL_PATH)\n", + " print(f\"Model path: {MODEL_PATH}\")\n", + "\n", + "TOKENIZER_PATH = \"./paligemma_tokenizer.model\"\n", + "if not os.path.exists(TOKENIZER_PATH):\n", + " print(\"Downloading the model tokenizer...\")\n", + " !gsutil cp gs://big_vision/paligemma_tokenizer.model {TOKENIZER_PATH}\n", + " print(f\"Tokenizer path: {TOKENIZER_PATH}\")\n", + "\n", + "DATA_DIR=\"./longcap100\"\n", + "if not os.path.exists(DATA_DIR):\n", + " print(\"Downloading the dataset...\")\n", + " !gsutil -m -q cp -n -r gs://longcap100/ .\n", + " print(f\"Data path: {DATA_DIR}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "zDoq0O77GF30" + }, + "source": [ + "## Notebook" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dTfe2k8J4Bw0", + "outputId": "b9864437-9e35-493a-bf52-019c18d5dfd9" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "JAX version: 0.4.26\n", + "JAX platform: gpu\n", + "JAX devices: 1\n" + ] + } + ], + "source": [ + "import base64\n", + "import functools\n", + "import html\n", + "import io\n", + "import os\n", + "import warnings\n", + "\n", + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "import ml_collections\n", + "\n", + "import tensorflow as tf\n", + "import sentencepiece\n", + "\n", + "from IPython.core.display import display, HTML\n", + "from PIL import Image\n", + "\n", + "# Import model definition from big_vision\n", + "from big_vision.models.proj.paligemma import paligemma\n", + "from big_vision.trainers.proj.paligemma import predict_fns\n", + "\n", + "# Import big vision utilities\n", + "import big_vision.datasets.jsonl\n", + "import big_vision.utils\n", + "import big_vision.sharding\n", + "\n", + "# Don't let TF use the GPU or TPUs\n", + "tf.config.set_visible_devices([], \"GPU\")\n", + "tf.config.set_visible_devices([], \"TPU\")\n", + "\n", + "backend = jax.lib.xla_bridge.get_backend()\n", + "print(f\"JAX version: {jax.__version__}\")\n", + "print(f\"JAX platform: {backend.platform}\")\n", + "print(f\"JAX devices: {jax.device_count()}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "1aghcULcEdtv" + }, + "outputs": [], + "source": [ + "# @title Construct model and load params into RAM.\n", + "\n", + "# Define model\n", + "model_config = ml_collections.FrozenConfigDict({\n", + " \"llm\": {\"vocab_size\": 257_152},\n", + " \"img\": {\"variant\": \"So400m/14\", \"pool_type\": \"none\", \"scan\": True, \"dtype_mm\": \"float16\"}\n", + "})\n", + "model = paligemma.Model(**model_config)\n", + "tokenizer = sentencepiece.SentencePieceProcessor(TOKENIZER_PATH)\n", + "\n", + "# Load params - this can take up to 1 minute in T4 colabs.\n", + "params = paligemma.load(None, MODEL_PATH, model_config)\n", + "\n", + "# Define `decode` function to sample outputs from the model.\n", + "decode_fn = predict_fns.get_all(model)['decode']\n", + "decode = functools.partial(decode_fn, devices=jax.devices(), eos_token=tokenizer.eos_id())" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "RWOdf_fw2SAO", + "outputId": "6d48433f-7410-480d-b889-e2b679caa8a6" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " == Model params == \n", + "img/Transformer/encoder_norm/bias (1152,) float16\n", + "img/Transformer/encoder_norm/scale (1152,) float16\n", + "img/Transformer/encoderblock/LayerNorm_0/bias (27, 1152) float16\n", + "img/Transformer/encoderblock/LayerNorm_0/scale (27, 1152) float16\n", + "img/Transformer/encoderblock/LayerNorm_1/bias (27, 1152) float16\n", + "img/Transformer/encoderblock/LayerNorm_1/scale (27, 1152) float16\n", + "img/Transformer/encoderblock/MlpBlock_0/Dense_0/bias (27, 4304) float16\n", + "img/Transformer/encoderblock/MlpBlock_0/Dense_0/kernel (27, 1152, 4304) float16\n", + "img/Transformer/encoderblock/MlpBlock_0/Dense_1/bias (27, 1152) float16\n", + "img/Transformer/encoderblock/MlpBlock_0/Dense_1/kernel (27, 4304, 1152) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/key/bias (27, 16, 72) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/key/kernel (27, 1152, 16, 72) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/out/bias (27, 1152) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/out/kernel (27, 16, 72, 1152) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/query/bias (27, 16, 72) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/query/kernel (27, 1152, 16, 72) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/value/bias (27, 16, 72) float16\n", + "img/Transformer/encoderblock/MultiHeadDotProductAttention_0/value/kernel (27, 1152, 16, 72) float16\n", + "img/embedding/bias (1152,) float16\n", + "img/embedding/kernel (14, 14, 3, 1152) float16\n", + "img/head/bias (2048,) float16\n", + "img/head/kernel (1152, 2048) float16\n", + "img/pos_embedding (1, 256, 1152) float16\n", + "llm/embedder/input_embedding (257152, 2048) float16\n", + "llm/final_norm/scale (2048,) float16\n", + "llm/layers/attn/attn_vec_einsum/w (18, 8, 256, 2048) float32\n", + "llm/layers/attn/kv_einsum/w (18, 2, 1, 2048, 256) float32\n", + "llm/layers/attn/q_einsum/w (18, 8, 2048, 256) float32\n", + "llm/layers/mlp/gating_einsum (18, 2, 2048, 16384) float16\n", + "llm/layers/mlp/linear (18, 16384, 2048) float16\n", + "llm/layers/pre_attention_norm/scale (18, 2048) float16\n", + "llm/layers/pre_ffw_norm/scale (18, 2048) float16\n" + ] + } + ], + "source": [ + "# @title Move params to GPU/TPU memory.\n", + "#\n", + "# To keep HBM usage low and fit in a T4 GPU (16GB HBM) we opt to only finetune\n", + "# a part of the parameters. Additionally we keep the frozen params in float16\n", + "# and cast trainable to float32.\n", + "\n", + "# Create a pytree mask of the trainable params.\n", + "def is_trainable_param(name, param): # pylint: disable=unused-argument\n", + " if name.startswith(\"llm/layers/attn/\"): return True\n", + " if name.startswith(\"llm/\"): return False\n", + " if name.startswith(\"img/\"): return False\n", + " raise ValueError(f\"Unexpected param name {name}\")\n", + "trainable_mask = big_vision.utils.tree_map_with_names(is_trainable_param, params)\n", + "\n", + "#\n", + "# If more than one device is available (e.g. multiple GPUs) the parameters can\n", + "# be sharded across them to reduce HBM usage per device.\n", + "mesh = jax.sharding.Mesh(jax.devices(), (\"data\"))\n", + "\n", + "data_sharding = jax.sharding.NamedSharding(\n", + " mesh, jax.sharding.PartitionSpec(\"data\"))\n", + "\n", + "params_sharding = big_vision.sharding.infer_sharding(\n", + " params, strategy=[('.*', 'fsdp(axis=\"data\")')], mesh=mesh)\n", + "\n", + "# Yes: Some donated buffers are not usable.\n", + "warnings.filterwarnings(\n", + " \"ignore\", message=\"Some donated buffers were not usable\")\n", + "\n", + "@functools.partial(jax.jit, donate_argnums=(0,), static_argnums=(1,))\n", + "def maybe_cast_to_f32(params, trainable):\n", + " return jax.tree.map(lambda p, m: p.astype(jnp.float32) if m else p,\n", + " params, trainable)\n", + "\n", + "# Loading all params in simultaneous - albeit much faster and more succinct -\n", + "# requires more RAM than the T4 colab runtimes have by default (12GB RAM).\n", + "# Instead we do it param by param.\n", + "params, treedef = jax.tree.flatten(params)\n", + "sharding_leaves = jax.tree.leaves(params_sharding)\n", + "trainable_leaves = jax.tree.leaves(trainable_mask)\n", + "for idx, (sharding, trainable) in enumerate(zip(sharding_leaves, trainable_leaves)):\n", + " params[idx] = big_vision.utils.reshard(params[idx], sharding)\n", + " params[idx] = maybe_cast_to_f32(params[idx], trainable)\n", + " params[idx].block_until_ready()\n", + "params = jax.tree.unflatten(treedef, params)\n", + "\n", + "# Print params to show what the model is made of.\n", + "def parameter_overview(params):\n", + " for path, arr in big_vision.utils.tree_flatten_with_names(params)[0]:\n", + " print(f\"{path:80s} {str(arr.shape):22s} {arr.dtype}\")\n", + "\n", + "print(\" == Model params == \")\n", + "parameter_overview(params)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "8SRW0NuU4UcW" + }, + "outputs": [], + "source": [ + "# @title Define preprocess functions to create inputs to the model.\n", + "\n", + "def preprocess_image(image, size=224):\n", + " # Model has been trained to handle images of different aspects ratios\n", + " # resized to 224x224 in the range [-1, 1]. Bilinear and antialias resize\n", + " # options are helpful to improve quality in some tasks.\n", + " image = np.asarray(image)\n", + " if image.ndim == 2: # Convert image without last channel into greyscale.\n", + " image = np.stack((image,)*3, axis=-1)\n", + " image = image[..., :3] # Remove alpha layer.\n", + " assert image.shape[-1] == 3\n", + "\n", + " image = tf.constant(image)\n", + " image = tf.image.resize(image, (size, size), method='bilinear', antialias=True)\n", + " return image.numpy() / 127.5 - 1.0 # [0, 255]->[-1,1]\n", + "\n", + "def preprocess_tokens(prefix, suffix=None, seqlen=None):\n", + " # Model has been trained to handle tokenized text composed of a prefix with\n", + " # full attention and a suffix with causal attention.\n", + " separator = \"\\n\"\n", + " tokens = tokenizer.encode(prefix, add_bos=True) + tokenizer.encode(separator)\n", + " mask_ar = [0] * len(tokens) # 0 to use full attention for prefix.\n", + " mask_loss = [0] * len(tokens) # 0 to not use prefix tokens in the loss.\n", + "\n", + " if suffix:\n", + " suffix = tokenizer.encode(suffix, add_eos=True)\n", + " tokens += suffix\n", + " mask_ar += [1] * len(suffix) # 1 to use causal attention for suffix.\n", + " mask_loss += [1] * len(suffix) # 1 to use suffix tokens in the loss.\n", + "\n", + " mask_input = [1] * len(tokens) # 1 if its a token, 0 if padding.\n", + " if seqlen:\n", + " padding = [0] * max(0, seqlen - len(tokens))\n", + " tokens = tokens[:seqlen] + padding\n", + " mask_ar = mask_ar[:seqlen] + padding\n", + " mask_loss = mask_loss[:seqlen] + padding\n", + " mask_input = mask_input[:seqlen] + padding\n", + "\n", + " return jax.tree.map(np.array, (tokens, mask_ar, mask_loss, mask_input))\n", + "\n", + "def postprocess_tokens(tokens):\n", + " tokens = tokens.tolist() # np.array to list[int]\n", + " try: # Remove tokens at and after EOS if any.\n", + " eos_pos = tokens.index(tokenizer.eos_id())\n", + " tokens = tokens[:eos_pos]\n", + " except ValueError:\n", + " pass\n", + " return tokenizer.decode(tokens)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "whzWOojGOtzi" + }, + "outputs": [], + "source": [ + "# @title Function to iterate over train and validation examples.\n", + "SEQLEN = 128\n", + "\n", + "# TODO: Consider data iterators skipping big_vision and tf.data?\n", + "train_dataset = big_vision.datasets.jsonl.DataSource(\n", + " os.path.join(DATA_DIR, \"data_train90.jsonl\"),\n", + " fopen_keys={\"image\": DATA_DIR})\n", + "\n", + "val_dataset = big_vision.datasets.jsonl.DataSource(\n", + " os.path.join(DATA_DIR, \"data_val10.jsonl\"),\n", + " fopen_keys={\"image\": DATA_DIR})\n", + "\n", + "\n", + "def train_data_iterator():\n", + " \"\"\"Never ending iterator over training examples.\"\"\"\n", + " # Shuffle examples and repeat so one can train for many epochs.\n", + " dataset = train_dataset.get_tfdata().shuffle(1_000).repeat()\n", + " for example in dataset.as_numpy_iterator():\n", + " image = Image.open(io.BytesIO(example[\"image\"]))\n", + " image = preprocess_image(image)\n", + "\n", + " prefix = \"caption en\" # Could also be a different prefix per example.\n", + " suffix = example[\"suffix\"].decode().lower()\n", + " tokens, mask_ar, mask_loss, _ = preprocess_tokens(prefix, suffix, SEQLEN)\n", + "\n", + " yield {\n", + " \"image\": np.asarray(image),\n", + " \"text\": np.asarray(tokens),\n", + " \"mask_ar\": np.asarray(mask_ar),\n", + " \"mask_loss\": np.asarray(mask_loss),\n", + " }\n", + "\n", + "\n", + "def validation_data_iterator():\n", + " \"\"\"Single iterator over validation examples.\"\"\"\n", + " for example in val_dataset.get_tfdata(ordered=True).as_numpy_iterator():\n", + " image = Image.open(io.BytesIO(example[\"image\"]))\n", + " image = preprocess_image(image)\n", + "\n", + " prefix = \"caption en\" # Could also be a different prefix per example.\n", + " tokens, mask_ar, _, mask_input = preprocess_tokens(prefix, seqlen=SEQLEN)\n", + "\n", + " yield {\n", + " \"image\": np.asarray(image),\n", + " \"text\": np.asarray(tokens),\n", + " \"mask_ar\": np.asarray(mask_ar),\n", + " \"mask_input\": np.asarray(mask_input),\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 516 + }, + "id": "BzJfb5t0nsLq", + "outputId": "1f6640f7-09b4-41a3-c713-62966b0df7e7" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Training examples\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a tall crane stands in the heart of the city, casting long shadows across the streets below. the sky is clear and blue, with fluffy white clouds drifting lazily. a tall white building dominates the skyline, its windows reflecting the afternoon sun. a tall black building casts a long shadow on the ground.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a neon sign on a brick wall reads "this is the sign you ve been looking for." the sign is lit up and the letters are white. there are several pillows on the wall, including a pillow with a skull and crossbones. the wall is made of bricks and the sign is on the wall. the sign is neon and the letters are white.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a tool box filled with a variety of tools, including a wrench with a silver head, a screwdriver with a gray handle,a wrench with a gray head, a screwdriver with a gray handle, a metal socket with a silver head...

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person stands on a sidewalk, their shoes and legs visible. the ground is made of concrete. the person wears black pants, with a white sole on their shoe and a white lace on their shoe. the shoes are black and white. the person's legs are visible. the words "passion led us here" are written on the ground in red. the concrete has a shadow on it, and the sun shines on the ground

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a bowl of steaming instant noodle soup with a spoon resting in the center. the broth is clear and the vegetables, including carrots, peas, and green beans, are floating gently in the liquid. the spoon is long and silver, with a reflection of light on its handle. the overall image is simple and straightforward, with a focus on the deliciousness of the soup.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a kitchen with a stove and a window. the room has a green floor and a white wall. there is a white towel hanging on the oven door. the stove has a black oven door, a black knob on the stove, and a black and white oven. there is a a silver pot on the stove. the window has a yellow frame and there is a white plastic bag hanging on the oven door. the door is open.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a plate of colorful desserts and a cup of coffee. the plate features a variety of sweet treats, including a macaron with a bite taken out, a green macaron, a pink macaron, and a white macaron. the plate is adorned with a white flower on a tree branch and a green leaf on a plant. the coffee cup has a white handle. the table is covered in a white and gold tablecloth.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a car is parked in front of a building with a green umbrella. the building has a green and white sign, a green and white umbrella, and a white sign with black lettering. there is a tall palm tree and a tall tree. the car is parked next to a yellow car and a black car. the road is grey and the sky is white.

\n", + "
\n", + " " + ] + }, + "metadata": {} + } + ], + "source": [ + "# @title Inspect training examples.\n", + "def render_inline(image, resize=(128, 128)):\n", + " \"\"\"Convert image into inline html.\"\"\"\n", + " image = Image.fromarray(image)\n", + " image.resize(resize)\n", + " with io.BytesIO() as buffer:\n", + " image.save(buffer, format='jpeg')\n", + " image_b64 = str(base64.b64encode(buffer.getvalue()), \"utf-8\")\n", + " return f\"data:image/jpeg;base64,{image_b64}\"\n", + "\n", + "def render_example(image, caption):\n", + " image = ((image + 1)/2 * 255).astype(np.uint8) # [-1,1] -> [0, 255]\n", + " return f\"\"\"\n", + "
\n", + " \n", + "

{html.escape(caption)}

\n", + "
\n", + " \"\"\"\n", + "\n", + "html_out = \"\"\n", + "for idx, example in zip(range(8), train_data_iterator()):\n", + " caption = postprocess_tokens(example[\"text\"]) # detokenize model input.\n", + " caption = caption[len(\"caption en\\n\"):] # strip prefix\n", + " html_out += render_example(example[\"image\"], caption)\n", + "\n", + "print(\"Training examples\")\n", + "display(HTML(html_out))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "dwUV_imW3WQJ" + }, + "outputs": [], + "source": [ + "# @title Define the training step and evaluation loop.\n", + "#\n", + "# The main update_fn using simple SGD.\n", + "#\n", + "@functools.partial(jax.jit, donate_argnums=(0,))\n", + "def update_fn(params, batch, learning_rate):\n", + " imgs, txts, mask_ar = batch[\"image\"], batch[\"text\"], batch[\"mask_ar\"]\n", + "\n", + " def loss_fn(params):\n", + " text_logits, _ = model.apply({\"params\": params}, imgs, txts[:, :-1], mask_ar[:, :-1], train=True)\n", + " logp = jax.nn.log_softmax(text_logits, axis=-1)\n", + "\n", + " # The model takes as input txts[:, :-1] but the loss is defined as predicting\n", + " # next tokens txts[:, 1:]. Additionally, mask_loss[:, 1:] indicates which tokens\n", + " # are part of the loss (e.g. prefix and padded tokens are not included).\n", + " mask_loss = batch[\"mask_loss\"][:, 1:]\n", + " targets = jax.nn.one_hot(txts[:, 1:], text_logits.shape[-1])\n", + "\n", + " # Compute the loss per example. i.e. the mean of per token pplx.\n", + " # Since each example has a different number of tokens we normalize it.\n", + " token_pplx = jnp.sum(logp * targets, axis=-1) # sum across vocab_size.\n", + " example_loss = -jnp.sum(token_pplx * mask_loss, axis=-1) # sum across seq_len.\n", + " example_loss /= jnp.clip(jnp.sum(mask_loss, -1), 1) # weight by num of tokens.\n", + "\n", + " # batch_loss: mean of per example loss.\n", + " return jnp.mean(example_loss)\n", + "\n", + " loss, grads = jax.value_and_grad(loss_fn)(params)\n", + "\n", + " # Apply gradients to trainable params using SGD.\n", + " def apply_grad(param, gradient, trainable):\n", + " if not trainable: return param\n", + " return param - learning_rate * gradient\n", + "\n", + " params = jax.tree_util.tree_map(apply_grad, params, grads, trainable_mask)\n", + "\n", + " return params, loss\n", + "\n", + "# Evaluation/inference loop.\n", + "def make_predictions(data_iterator, *, num_examples=None,\n", + " batch_size=4, seqlen=SEQLEN, sampler=\"greedy\"):\n", + " outputs = []\n", + " while True:\n", + " # Construct a list of examples in the batch.\n", + " examples = []\n", + " try:\n", + " for _ in range(batch_size):\n", + " examples.append(next(data_iterator))\n", + " examples[-1][\"_mask\"] = np.array(True) # Indicates true example.\n", + " except StopIteration:\n", + " if len(examples) == 0:\n", + " return outputs\n", + "\n", + " # Not enough examples to complete a batch. Pad by repeating last example.\n", + " while len(examples) % batch_size:\n", + " examples.append(dict(examples[-1]))\n", + " examples[-1][\"_mask\"] = np.array(False) # Indicates padding example.\n", + "\n", + " # Convert list of examples into a dict of np.arrays and load onto devices.\n", + " batch = jax.tree.map(lambda *x: np.stack(x), *examples)\n", + " batch = big_vision.utils.reshard(batch, data_sharding)\n", + "\n", + " # Make model predictions\n", + " tokens = decode({\"params\": params}, batch=batch,\n", + " max_decode_len=seqlen, sampler=sampler)\n", + "\n", + " # Fetch model predictions to device and detokenize.\n", + " tokens, mask = jax.device_get((tokens, batch[\"_mask\"]))\n", + " tokens = tokens[mask] # remove padding examples.\n", + " responses = [postprocess_tokens(t) for t in tokens]\n", + "\n", + " # Append to html output.\n", + " for example, response in zip(examples, responses):\n", + " outputs.append((example[\"image\"], response))\n", + " if num_examples and len(outputs) >= num_examples:\n", + " return outputs" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "067wj_6bZAG3", + "outputId": "e1aa2df0-502e-4a70-c88d-db98739c01d5" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 1/64 lr: 0.00500 loss: 2.7898\n", + "Model predictions at step 1\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

the beauty of a puff sleeve

\n", + "
\n", + " \n", + "
\n", + " \n", + "

how to wear a maxi dress for summer

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a red blazer and a black bag

\n", + "
\n", + " \n", + "
\n", + " \n", + "

how to wear boyfriend jeans like a fashion blogger

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 2/64 lr: 0.01000 loss: 2.1176\n", + "step: 3/64 lr: 0.01500 loss: 1.7491\n", + "step: 4/64 lr: 0.02000 loss: 1.5594\n", + "step: 5/64 lr: 0.02500 loss: 1.6047\n", + "step: 6/64 lr: 0.03000 loss: 1.3865\n", + "step: 7/64 lr: 0.02998 loss: 1.4946\n", + "step: 8/64 lr: 0.02992 loss: 1.6175\n", + "step: 9/64 lr: 0.02981 loss: 1.3377\n", + "step: 10/64 lr: 0.02966 loss: 1.4888\n", + "step: 11/64 lr: 0.02947 loss: 1.3479\n", + "step: 12/64 lr: 0.02924 loss: 1.3211\n", + "step: 13/64 lr: 0.02897 loss: 1.0806\n", + "step: 14/64 lr: 0.02866 loss: 1.1590\n", + "step: 15/64 lr: 0.02831 loss: 1.1158\n", + "step: 16/64 lr: 0.02792 loss: 1.1702\n", + "Model predictions at step 16\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman wearing a pink blouse with a large puffy sleeve stands on a white wall. the woman's hand rests on the wall, and her fingers are intertwined. the wall is white, and the light is shining on the woman's hand. the sky is clear, and the sun is shining. the woman is wearing a pink blouse, and her hair is in a bun.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a white floral dress sits on a stone wall overlooking the ocean. the dress is flowing in the wind and the hat is on her head. the sky is clear and the sun is shining. the woman is wearing a hat and a bag.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person wearing a red blazer and a black belt bag. the person is standing in the woods and the grass is green. the person is wearing a black belt bag and a black belt. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag. the person is wearing a red blazer and a black belt bag

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a pink shirt and jeans stands on a stone staircase. she is holding a pink bag and wearing a bracelet. the stairs are made of stone and the woman is wearing a bracelet.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 17/64 lr: 0.02750 loss: 1.1610\n", + "step: 18/64 lr: 0.02704 loss: 1.1972\n", + "step: 19/64 lr: 0.02655 loss: 1.1947\n", + "step: 20/64 lr: 0.02602 loss: 1.3566\n", + "step: 21/64 lr: 0.02546 loss: 1.1505\n", + "step: 22/64 lr: 0.02488 loss: 1.1470\n", + "step: 23/64 lr: 0.02426 loss: 0.9735\n", + "step: 24/64 lr: 0.02362 loss: 1.1087\n", + "step: 25/64 lr: 0.02296 loss: 0.9770\n", + "step: 26/64 lr: 0.02227 loss: 1.0618\n", + "step: 27/64 lr: 0.02156 loss: 0.9121\n", + "step: 28/64 lr: 0.02083 loss: 0.9501\n", + "step: 29/64 lr: 0.02009 loss: 0.9369\n", + "step: 30/64 lr: 0.01933 loss: 1.0276\n", + "step: 31/64 lr: 0.01856 loss: 0.9005\n", + "step: 32/64 lr: 0.01778 loss: 0.8751\n", + "Model predictions at step 32\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a person wearing a pink blouse with a white wall in the background. the blouse has a white collar and a white wall. the person is standing and wearing a white shirt. the wall is white and the person is standing on a white step. the person is wearing a white shirt and a white wall. the person is wearing a pink blouse and a white wall.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a white floral dress with a brown belt, holding a white bag. the dress has a v-neckline and short sleeves. the woman is standing on a stone wall overlooking the ocean. the sky is clear and blue, with a few white clouds. the water is calm and blue, with a few white boats on the horizon. the woman is wearing a brown belt and a brown bag. the woman's hair is in a ponytail.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person wearing a red blazer with a black fanny pack around their waist. the blazer has a white button down and a black belt. the person is standing in front of a green plant. the plant has a green leaf and a green stem. the person is wearing a black shirt and black pants. the person is wearing a black belt and a black fanny pack. the person is wearing a red blazer and a red shirt. the person is wearing a black jacket and a black pants. the person is wearing a black jacket and a black pants. the person is wearing a black jacket and a black pants. the person is wearing a

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a pink shirt and blue jeans, holding a pink bag. the woman is standing on a stone staircase, wearing a white cardigan and a gold bracelet. the bag is pink and has a black strap. the woman is wearing a gold bracelet and a gold bracelet on her wrist. the woman is standing on a stone staircase.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 33/64 lr: 0.01699 loss: 0.8570\n", + "step: 34/64 lr: 0.01620 loss: 1.0866\n", + "step: 35/64 lr: 0.01540 loss: 0.7469\n", + "step: 36/64 lr: 0.01460 loss: 0.8605\n", + "step: 37/64 lr: 0.01380 loss: 0.6830\n", + "step: 38/64 lr: 0.01301 loss: 0.7631\n", + "step: 39/64 lr: 0.01222 loss: 0.7814\n", + "step: 40/64 lr: 0.01144 loss: 0.8579\n", + "step: 41/64 lr: 0.01067 loss: 0.7825\n", + "step: 42/64 lr: 0.00991 loss: 0.6906\n", + "step: 43/64 lr: 0.00917 loss: 0.7922\n", + "step: 44/64 lr: 0.00844 loss: 0.7030\n", + "step: 45/64 lr: 0.00773 loss: 0.7501\n", + "step: 46/64 lr: 0.00704 loss: 0.6384\n", + "step: 47/64 lr: 0.00638 loss: 0.6309\n", + "step: 48/64 lr: 0.00574 loss: 0.6149\n", + "Model predictions at step 48\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a person wearing a pink blouse with a puffy sleeve. the blouse has a white wall in the background. the person is standing on a white step, and the sun is shining on the wall. the person is wearing a bracelet on their wrist, and their hand is on the step. the person is wearing a watch on their wrist, and their nails are painted red.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wears a white floral dress with a brown belt. the dress has a v-neckline and short sleeves. the woman is standing on a stone wall overlooking the ocean. the sky is clear and blue, and the boats are visible on the horizon. the woman is holding a white wicker bag. the woman's hair is tied back.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person wearing a red blazer with a black fanny pack around their waist. the blazer has a white button down and a black belt. the person is standing and wearing a black shirt. the fanny pack is black and has a white loading on it. the person is wearing black pants and black shoes. the person is standing and the grass is green.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wears a pink shirt and blue jeans. she has a pink bag on her shoulder and a bracelet on her wrist. the stairs are made of stone and the woman is standing on the stairs.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 49/64 lr: 0.00512 loss: 0.7896\n", + "step: 50/64 lr: 0.00454 loss: 0.6380\n", + "step: 51/64 lr: 0.00398 loss: 0.6263\n", + "step: 52/64 lr: 0.00345 loss: 0.6160\n", + "step: 53/64 lr: 0.00296 loss: 0.6626\n", + "step: 54/64 lr: 0.00250 loss: 0.5598\n", + "step: 55/64 lr: 0.00208 loss: 0.5567\n", + "step: 56/64 lr: 0.00169 loss: 0.7069\n", + "step: 57/64 lr: 0.00134 loss: 0.5293\n", + "step: 58/64 lr: 0.00103 loss: 0.5725\n", + "step: 59/64 lr: 0.00076 loss: 0.5477\n", + "step: 60/64 lr: 0.00053 loss: 0.6153\n", + "step: 61/64 lr: 0.00034 loss: 0.5260\n", + "step: 62/64 lr: 0.00019 loss: 0.5673\n", + "step: 63/64 lr: 0.00008 loss: 0.5721\n", + "step: 64/64 lr: 0.00002 loss: 0.6681\n", + "Model predictions at step 64\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a person wearing a pink blouse with a puffy sleeve. the blouse has a white wall in the background. the person is standing and their hand is on the wall. the wall has a white line on it. the person is wearing a pink blouse with a puffy sleeve.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a white floral dress stands on a pier overlooking the ocean. she is wearing a brown bag and a brown hat. the dress has a v-neckline and a tie belt. the woman has her hand in her pocket and her leg is visible. the sky is clear and blue.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person wearing a red blazer with a black fanny pack around their waist. the blazer has a white button down and a black belt. the person is standing and wearing a black shirt. the fanny pack is black and has a white loading on it. the person is wearing black pants and black shoes. the tree behind them is green.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wears a pink shirt and blue jeans. she has a pink bag on her shoulder and a bracelet on her wrist. the stairs are made of gray stone. the woman is standing on the stairs.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 12min 34s, sys: 6.31 s, total: 12min 40s\n", + "Wall time: 13min 5s\n" + ] + } + ], + "source": [ + "# @title Run training loop.\n", + "#\n", + "# Run a short training loop with cosine learning rate schedule.\n", + "#\n", + "# Note: the first step can be quite slow on some machines (up to several minutes)\n", + "# due to XLA compilation of the jax.jit'd function.\n", + "#\n", + "%%time\n", + "\n", + "BATCH_SIZE = 8\n", + "TRAIN_EXAMPLES = 512\n", + "LEARNING_RATE = 0.03\n", + "\n", + "TRAIN_STEPS = TRAIN_EXAMPLES // BATCH_SIZE\n", + "EVAL_STEPS = TRAIN_STEPS // 4\n", + "\n", + "train_data_it = train_data_iterator()\n", + "\n", + "sched_fn = big_vision.utils.create_learning_rate_schedule(\n", + " total_steps=TRAIN_STEPS+1, base=LEARNING_RATE,\n", + " decay_type=\"cosine\", warmup_percent=0.10)\n", + "\n", + "for step in range(1, TRAIN_STEPS+1):\n", + " # Make list of N training examples.\n", + " examples = [next(train_data_it) for _ in range(BATCH_SIZE)]\n", + "\n", + " # Convert list of examples into a dict of np.arrays and load onto devices.\n", + " batch = jax.tree.map(lambda *x: np.stack(x), *examples)\n", + " batch = big_vision.utils.reshard(batch, data_sharding)\n", + "\n", + " # Training step and report training loss\n", + " learning_rate = sched_fn(step)\n", + " params, loss = update_fn(params, batch, learning_rate)\n", + "\n", + " loss = jax.device_get(loss)\n", + " print(f\"step: {step:2d}/{TRAIN_STEPS:2d} lr: {learning_rate:.5f} loss: {loss:.4f}\")\n", + "\n", + " if step == 1 or (step % EVAL_STEPS) == 0:\n", + " print(f\"Model predictions at step {step}\")\n", + " html_out = \"\"\n", + " for image, caption in make_predictions(\n", + " validation_data_iterator(), num_examples=4, batch_size=4):\n", + " html_out += render_example(image, caption)\n", + " display(HTML(html_out))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 699 + }, + "id": "hgUhEKjzPdMQ", + "outputId": "63037cd6-151c-4802-9de8-be2cb7818d12" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Model predictions\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a person wearing a pink blouse with a puffy sleeve. the blouse has a white wall in the background. the person is standing and their hand is on the wall. the wall has a white line on it. the person is wearing a pink blouse with a puffy sleeve.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wearing a white floral dress stands on a pier overlooking the ocean. she is wearing a brown bag and a brown hat. the dress has a v-neckline and a tie belt. the woman has her hand in her pocket and her leg is visible. the sky is clear and blue.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a person wearing a red blazer with a black fanny pack around their waist. the blazer has a white button down and a black belt. the person is standing and wearing a black shirt. the fanny pack is black and has a white loading on it. the person is wearing black pants and black shoes. the tree behind them is green.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wears a pink shirt and blue jeans. she has a pink bag on her shoulder and a bracelet on her wrist. the stairs are made of gray stone. the woman is standing on the stairs.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a pink sweatshirt with a red slogan lies on a bed next to a pair of jeans and a pair of white sneakers. the sweatshirt features long sleeves and a crew neck. the text on the sweatshirt reads "love well, save us." the sneakers are white and have white laces. the hand on the sweatshirt is gentle.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a man with long blonde hair covers his face with his hand. he wears a navy sweater and a black and white checkered shirt. the sweater has long sleeves and a collar. the man has a beard and mustache. the hair on his face is long and wavy. the man is standing and his hands are on his head. the background is pink.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a white metal rack with a white metal pole and a white metal pole. the rack has a white metal pole and a white metal pole. the rack has a white metal pole and a white metal pole.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a white hoodie hangs on a black coat rack, with a white drawstring on the left side of the hoodie. the coat rack is black, and the wall is white. there is a black circle on the wall, and a black circle on the wall. the hoodie has a white drawstring on the left side of the hoodie.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a woman wears a pair of blue jeans with a black bag. the jeans have a gray wall behind them. the woman is wearing a black bag with a gold chain. the bag has a silver chain and a silver lock. the woman is wearing black boots with black socks. the bag has a silver chain and a silver lock. the woman is wearing a black bracelet on her wrist. the woman is standing and the bag is on her shoulder.

\n", + "
\n", + " \n", + "
\n", + " \n", + "

a man stands on a sidewalk wearing a blue denim jacket with a white t-shirt, brown pants, and white shoes. he has his hands in his pockets and his legs are stretched out. the jacket has a blue collar and a white t-shirt. the pants have a brown stripe on the side and a white shoe. the man is wearing a white t-shirt and brown pants.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 26.2 s, sys: 112 ms, total: 26.3 s\n", + "Wall time: 32.9 s\n" + ] + } + ], + "source": [ + "# @title Evaluate the model on all examples.\n", + "#\n", + "# The validation data consists of 10 images in a different domain than training\n", + "# data.\n", + "%%time\n", + "\n", + "print(\"Model predictions\")\n", + "html_out = \"\"\n", + "for image, caption in make_predictions(validation_data_iterator(), batch_size=4):\n", + " html_out += render_example(image, caption)\n", + "display(HTML(html_out))\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Ai0NMbAwsr0j" + }, + "source": [ + "# Save the final checkpoint" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5H_3CV33_JkV" + }, + "outputs": [], + "source": [ + "def npsave(pytree, path):\n", + " names_and_vals, _ = big_vision.utils.tree_flatten_with_names(pytree)\n", + " with open(path, \"wb\") as f:\n", + " np.savez(f, **{k:v for k, v in names_and_vals})\n", + "\n", + "# Takes around 4 minutes\n", + "npsave(params, 'my-custom-paligemma-ckpt.npz')" + ] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/big_vision/configs/proj/paligemma/paligemma.png b/big_vision/configs/proj/paligemma/paligemma.png new file mode 100644 index 0000000000000000000000000000000000000000..949d4af23958646ff18899028c906022e9aa1357 Binary files /dev/null and b/big_vision/configs/proj/paligemma/paligemma.png differ diff --git a/big_vision/configs/proj/paligemma/transfers/activitynet_cap.py b/big_vision/configs/proj/paligemma/transfers/activitynet_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..2176595c357eeab9c2a4f57f819513409e97be23 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/activitynet_cap.py @@ -0,0 +1,209 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to ActivityNet Video captioning. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 64 +DATASET_NAME = 'activitynet_captions_mr' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 30545, 'valid': 14338, 'test': 13982} + + +def training_data(res, *, final_split, num_frames=8, stride=None): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # pick one caption at random during training (there is actually just one!) + 'strfmt("caption en", outkey="prefix")', + 'video_choice(inkey="caption/string", outkey="suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames=8, stride=None): # pylint: disable=unused-argument + """Captioning evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + 'strfmt("caption en", outkey="prefix")', + 'strfmt("{example/video_id}[{segment_start}-{segment_end}]", outkey="image/id")', + 'copy("caption/string", "captions")', + combine_and_keep_eval(TEXT_LEN, keep=('image/id', 'captions')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 2, False), # To gauge memorization. + (1/4, 'minival', 'valid', 2, False), # To monitor val progress. + (1, 'val', 'valid', None, False), # To tune hparams. + (1, 'eval', 'test', None, False), # final metric + ]: + c.evals[f'{DATASET_NAME}/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames=8, stride=None): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 2), # To gauge memorization. + ]: + c.evals[f'{DATASET_NAME}/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=1, **bvcc.arg(freeze_vit=True, res=224, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=16, stride=30, res=224, + freeze_vit=False, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 3 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append(('epoch', f'{DATASET_NAME}/{split}/cider')) + for split in ('minitrain', 'minival'): + m.append(('epoch', f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/activitynet_qa.py b/big_vision/configs/proj/paligemma/transfers/activitynet_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..556ce79d3dc6ee855e0e1c33d1c94605972f3a1e --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/activitynet_qa.py @@ -0,0 +1,213 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to ActivityNet Video QA. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 64 +DATASET_NAME = 'activitynet_qa' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 27610, 'valid': 15760, 'test': 6900} + + +def training_data(res, *, final_split, num_frames, stride): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy("answer", "suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames, stride): # pylint: disable=unused-argument + """QA evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{id}#{example/video_id}: {question}", "question_id")', + combine_and_keep_eval(TEXT_LEN, keep=('question_id', 'answer')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 2, False), # To gauge memorization. + (1/4, 'minival', 'valid', 2, False), # To monitor val progress. + (1, 'val', 'valid', None, True), # To tune hparams. + (1, 'eval', 'test', None, True), # final metric + ]: + c.evals[f'activitynet_qa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames, stride): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 2), # To gauge memorization. + ('minival', 'valid', 2), + ]: + c.evals[f'activitynet_qa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=1, **bvcc.arg(num_frames=16, stride=70, res=224, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=16, stride=70, res=224, + freeze_vit=False, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 3 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 1e-6 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append(('epoch', f'{DATASET_NAME}/{split}/acc')) + for split in ('minitrain', 'minival'): + m.append(('epoch', f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/ai2d.py b/big_vision/configs/proj/paligemma/transfers/ai2d.py new file mode 100644 index 0000000000000000000000000000000000000000..221091e795815493acf6ea58eadbbc2f5cda2cee --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/ai2d.py @@ -0,0 +1,170 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to AI2D. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +PREFIX = 'answer en ' +PROMPT = 'choose from:' +PROMPT_SEP = ' \t ' + + +def training_data(res, final_split, text_len=128): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: whether to use all train data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='ai2d', + # 12k training examples. + split='train' if final_split else 'train[:-1024]', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + f'strjoin("{PROMPT_SEP}", inkey="possible_answers", outkey="ansstr")', + f'strfmt("{PREFIX} {{question}} {PROMPT} {{ansstr}}", outkey="prefix")', + 'copy(inkey="answer", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=128, **kw): + """AI2D evaluators.""" + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + f'strjoin("{PROMPT_SEP}", inkey="possible_answers", outkey="ansstr")', + f'strfmt("{PREFIX} {{question}} {PROMPT} {{ansstr}}", outkey="prefix")', + 'copy(inkey="id",outkey="question_id")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for name, split in [ + ('minitrain', 'train[:1024]'), # To gauge memorization. + ('minival', 'train[-1024:]'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + c.evals[f'ai2d/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/ai2d_{name}.json', + to_lower=False, # Model sees options in prompt and can match the case. + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=1/8, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'ai2d/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=128): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + + for name, split in [ + ('minitrain', 'train[:1024]'), # To gauge memorization. + ('minival', 'train[-1024:]'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + c.evals[f'ai2d/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=10, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=10, **bvcc.arg(res=448, **c)) + # 896 was not better than 448 ((internal link)). + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 1e-5 * 0.1 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'ai2d/{split}/pplx/avg') + m.append(f'ai2d/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/aokvqa_da.py b/big_vision/configs/proj/paligemma/transfers/aokvqa_da.py new file mode 100644 index 0000000000000000000000000000000000000000..62c7664967de378a8edc3969e9e5b4135311a8d9 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/aokvqa_da.py @@ -0,0 +1,161 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to A-OK-VQA using Direct Answer mode. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use train and validation data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='aokvqa', + split='train + val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="direct_answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """We can use the normal VQA evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy(inkey="direct_answers", outkey="answers")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (1/4, 'minitrain', 'train[:5%]'), # To gauge memorization. + (1/4, 'eval', 'val'), # To tune hparams. + (1.0, 'test', 'test'), # To compute final predictions. + ]: + c.evals[f'aokvqa_da/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/aokvqa_da_{name}.json', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'aokvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('eval', 'val'), # To tune hparams. + ]: + c.evals[f'aokvqa_da/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=5e-6, wd=0.0, **bvcc.arg(res=224, **c)) + add(lr=5e-6, wd=0.0, **bvcc.arg(res=448, **c)) + # not better: add(lr=5e-6, wd=0.0, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 5e-6 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'aokvqa/{split}/pplx/avg') + m.append(f'aokvqa/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/aokvqa_mc.py b/big_vision/configs/proj/paligemma/transfers/aokvqa_mc.py new file mode 100644 index 0000000000000000000000000000000000000000..d5d9fadb4149b3c398aa28684ee4cbfb02489189 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/aokvqa_mc.py @@ -0,0 +1,169 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to A-OK-VQA using multiple choice answers. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +PREFIX = 'answer en ' +PROMPT = 'choose from:' +PROMPT_SEP = ' \t ' + + +def training_data(res, final_split, text_len=128): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use train and validation data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='aokvqa', + split='train + val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + f'strjoin("{PROMPT_SEP}", inkey="multiple_choice_possible_answers", outkey="ansstr")', + f'strfmt("{PREFIX} {{question}} {PROMPT} {{ansstr}}", outkey="prefix")', + 'getidx(inkey="multiple_choice_possible_answers", index_key="multiple_choice_correct_idx", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=128, **kw): + """VQAv2 evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + f'strjoin("{PROMPT_SEP}", inkey="multiple_choice_possible_answers", outkey="ansstr")', + f'strfmt("{PREFIX} {{question}} {PROMPT} {{ansstr}}", outkey="prefix")', + 'getidx(inkey="multiple_choice_possible_answers", index_key="multiple_choice_correct_idx", outkey="answer")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for freq, name, split in [ + (1/4, 'minitrain', 'train[:5%]'), # To gauge memorization. + (1/4, 'eval', 'val'), # To tune hparams. + (1.0, 'test', 'test'), # To compute final predictions. + ]: + c.evals[f'aokvqa_mc/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/aokvqa_mc_{name}.json', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'aokvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=128): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('eval', 'val'), # To tune hparams. + ('test', 'test'), # To compute final predictions. + ]: + c.evals[f'aokvqa_mc/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=5e-6, wd=0.0, **bvcc.arg(res=224, **c)) + add(lr=5e-6, wd=0.0, **bvcc.arg(res=448, **c)) + # add(lr=5e-6, wd=0.0, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 15 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 5e-6 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'aokvqa/{split}/pplx/avg') + m.append(f'aokvqa/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/chartqa.py b/big_vision/configs/proj/paligemma/transfers/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..df10fc301de8ae21e25fd1cc2ada520fc34dcbf2 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/chartqa.py @@ -0,0 +1,180 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to chartqa. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +_DATASETS = ('chartqa/human', 'chartqa/augmented') +# We use the true dataset sizes from https://arxiv.org/pdf/2203.10244.pdf. +_WEIGHTS = (7_398, 20_901) + + +def training_data(res, *, final_split=False, text_len=48): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+val data. + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'copy(inkey="question", outkey="prefix")', + 'copy(inkey="answer", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + c.data = {ds: weight for ds, weight in zip(_DATASETS, _WEIGHTS)} + for ds in c.data: + c[ds] = dict( + shuffle_buffer_size=50_000, + pp=pp, + data=dict( + name=ds, + split='train+val' if final_split else 'train', + ), + ) + return c + + +def add_eval(c, res, text_len=48, **kw): + """Add eval configs.""" + c_train = training_data(res, final_split=True, text_len=text_len) + + pp_eval = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'copy(inkey="question", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for name, split in [ + ('minitrain', 'train[:5%]'), + ('minival', 'val'), + ('eval', 'test'), + ]: + for ds in _DATASETS: + c.evals[f'{ds}/{name}'] = dict( + type='proj.paligemma.transfers.chartqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + to_lower=True, + outfile=f'{{workdir}}/{ds.replace("/", "_")}_{name}.json', + data={**c_train[ds].data, 'split': split}, + log_percent=0.1, tokenizer=TOKENIZER, pp_fn=pp_eval) + c.evals[f'{ds}/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=48): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'val'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + for ds in _DATASETS: + c.evals[f'{ds}/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train[ds].data, 'split': split}, + pp_fn=c_train[ds].pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # TODO: Update once latest numbers are in and have only 1 setup. + # Based on (internal link) (relaxed_accuracy). + add(lr=1e-5, wd=1e-6, total_epochs=30, **bvcc.arg(res=224, **c)) + # Based on sweep (internal link) and on (internal link) (relaxed_accuracy). + add(lr=1e-5, wd=1e-6, total_epochs=30, **bvcc.arg(res=448, **c)) + # Based on (internal link) (relaxed_accuracy). + # Not better: add(lr=1e-5, wd=1e-6, total_epochs=30, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=896, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 30 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 1e-6 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.2 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.1) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for ds in _DATASETS: + c.input[ds].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + for ds in _DATASETS: + m.append(f'{ds}/{split}/relaxed_acc') + m.append(f'{ds}/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/coco35l.py b/big_vision/configs/proj/paligemma/transfers/coco35l.py new file mode 100644 index 0000000000000000000000000000000000000000..ba8aedcf2e721f88c32a652764f49a20e3f1e9be --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/coco35l.py @@ -0,0 +1,235 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to COCO-35L captions. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +LANGUAGES = ( + 'ar', 'bn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', + 'he', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'mi', 'nl', 'no', 'pl', + 'pt', 'ro', 'ru', 'sv', 'sw', 'te', 'th', 'tr', 'uk', 'vi', 'zh', +) + +LANGUAGES_XM3600 = ( + 'ar', 'bn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', + 'he', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'mi', 'nl', 'no', 'pl', + 'pt', 'quz', 'ro', 'ru', 'sv', 'sw', 'te', 'th', 'tr', 'uk', 'vi', 'zh' +) + +# A subset for more frequent evals. +LANGUAGES_SUBSET = ('ar', 'bn', 'en', 'id', 'sw', 'tr', 'zh') + + +def training_data(res, lang=None, text_len=32, crop='rs'): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224) + lang: language code + text_len: sequence length + crop: one of {'ic', 'rc', 'rs'} + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='coco35l', + split=f'train_{lang}' if lang else '+'.join((f'train_{l}' for l in LANGUAGES)), + ) + + if crop == 'ic': + crop = f'inception_crop({res}, area_min=50)' + elif crop == 'rc': + crop = f'resize_small({res*8//7})|random_crop({res})' + elif crop == 'rs': + crop = f'resize({res})' + else: + raise ValueError(f'Unknown crop: {crop}') + + c.pp = '|'.join([ + 'flatten', + 'decode', crop, 'value_range(-1, 1)', + 'choice_no_replacement(inkey="captions", outkey="suffix")', + 'strfmt("caption {language}", outkey="prefix")', + combine_and_keep_train(text_len), + ]) + return c + + +def _get_eval_pp(res, lang, text_len=32): + return '|'.join([ + 'flatten', + 'decode', f'resize({res})', 'value_range(-1, 1)', + f'strfmt("caption {lang}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + +def add_eval(c, res, text_len=32, langs=None, **kw): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + for lang in (langs or LANGUAGES): + # Frequent evals on a subset of representative languages, final eval on all. + freq = 0.25 if lang in LANGUAGES_SUBSET else 1.0 + + c.evals[f'coco35l/{lang}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=freq, skip_first=(freq == 1.0), tokenizer=TOKENIZER, + data=dict( + name='coco35l', + split=f'dev_{lang}', + ), + cache='none', + pp_fn=_get_eval_pp(res, lang, text_len), + ) + c.evals[f'coco35l/{lang}'].update(kw) + + +def add_eval_xm(c, res, text_len=32, langs=None, **kw): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + for lang in (langs or LANGUAGES_XM3600): + # Frequent evals on a subset of representative languages, final eval on all. + freq = 0.25 if lang in LANGUAGES_SUBSET else 1.0 + + c.evals[f'xm3600/{lang}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=freq, skip_first=(freq == 1.0), tokenizer=TOKENIZER, + data=dict( + name='xm3600', + split=lang, + ), + pp_fn=_get_eval_pp(res, lang, text_len) + ) + c.evals[f'xm3600/{lang}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train_en[:2%]'), + ('minival', 'dev_en[:5%]'), + ('eval', 'dev_en'), + ]: + c.evals[f'coco35l/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', crop='rs', res=224, eval_xm3600=True, beam_size=0) + + c.input = { + lang: training_data(c.res, lang=lang, crop=c.crop) + for lang in LANGUAGES + } + c.input.data = {lang: 1 for lang in LANGUAGES} + for k in c.input.data: + c.input[k].shuffle_buffer_size = 10_000 + + c.total_examples = 566_435 # We need to go a looot longer here. + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-4 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval_pplx(c, c.res) + + if c.beam_size: + decode_kw = {'pred': 'beam_decode', 'pred_kw': {'beam_size': c.beam_size}} + else: + decode_kw = {} + + add_eval(c, c.res, batch_size=1024, **decode_kw) + if c.eval_xm3600: + add_eval_xm(c, c.res, batch_size=1024, **decode_kw) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + # c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def sweep_hyper(add): + """sweep over hyper-params.""" + for lr in (1e-5, 3e-6, 1e-6): + for wd in (0.0, 0.1*lr): + for ep in (1, 3, 5, 10, 20): + # One language COCO is 566_435 examples (5 captions, 100k examples). + add(lr=lr, wd=wd, total_examples=ep * 566_435, **bvcc.arg(res=224)) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, eval_xm3600=True) + ep = 566_435 + add(lr=1e-5, wd=1e-6, total_examples=5 * ep, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_examples=5 * ep, **bvcc.arg(res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def metrics(arg=None): # pylint: disable=unused-argument + c = bvcc.parse_arg(arg, eval_xm3600=True) + m = [('epoch', f'coco35l/{lang}/cider') for lang in LANGUAGES] + if c.eval_xm3600: + for lang in LANGUAGES: + m.append(('epoch', f'xm3600/{lang}/cider')) + return m diff --git a/big_vision/configs/proj/paligemma/transfers/cococap.py b/big_vision/configs/proj/paligemma/transfers/cococap.py new file mode 100644 index 0000000000000000000000000000000000000000..95202bb3105665932ff554a7d6300e7b71c626b8 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/cococap.py @@ -0,0 +1,194 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to COCO captions. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=32, crop='rs'): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+restval data or train[:98%]+restval. + text_len: sequence length + crop: one of {'ic', 'rc', 'rs'} + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='coco_captions', + split='train+restval' if final_split else 'train[:98%]+restval', + ) + + if crop == 'ic': + crop = f'inception_crop({res}, area_min=50)' + elif crop == 'rc': + crop = f'resize_small({res*8//7})|random_crop({res})' + elif crop == 'rs': + crop = f'resize({res})' + else: + raise ValueError(f'Unknown crop: {crop}') + + c.pp = '|'.join([ + 'flatten', + 'decode', crop, 'value_range(-1, 1)', + 'choice_no_replacement(inkey="captions/text", outkey="suffix")', + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + # Input eval pp without ground truth text and random crop. + pp_eval = '|'.join([ + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + for name, split in [ + ('minitrain', 'train[:2%]'), + ('minival', 'train[-2%:]'), + ('eval', 'val'), + ]: + c.evals[f'cococap/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=0.1, tokenizer=TOKENIZER, + data={'name': 'coco_captions', 'split': split}, + pp_fn='|'.join([ + 'flatten', 'copy("captions/text", "captions")', # GT for evaluator. + pp_eval, + ]), + ) + c.evals[f'cococap/{name}'].update(kw) + + c.evals['nocaps/eval'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=0.1, tokenizer=TOKENIZER, + data={'name': 'nocaps', 'split': 'val'}, + pp_fn='|'.join([ + 'copy("texts", "captions")', # GT for evaluator. + pp_eval, + ]), + ) + c.evals['nocaps/eval'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len, crop='rs') # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:2%]'), + ('minival', 'train[-2%:]'), + ('eval', 'val'), + ]: + c.evals[f'cococap/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', crop='rs', res=224, beam_size=2, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split, crop=c.crop) + + c.total_epochs = 5 # One epoch of captions (each image has 5 captions). + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-4 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval_pplx(c, c.res) + + if c.beam_size: + decode_kw = {'pred': 'beam_decode', 'pred_kw': {'beam_size': c.beam_size}} + else: + decode_kw = {} + + add_eval(c, c.res, batch_size=1024, **decode_kw) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Note: wd=0.0 works as good. + add(lr=1e-5, wd=1e-6, total_epochs=5, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=5, **bvcc.arg(res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(('epoch', f'cococap/{split}/cider')) + m.append(('epoch', f'cococap/{split}/pplx/avg')) + return m diff --git a/big_vision/configs/proj/paligemma/transfers/common.py b/big_vision/configs/proj/paligemma/transfers/common.py new file mode 100644 index 0000000000000000000000000000000000000000..c584ff7f166d7eb9428dd0df039b65f4ecd7c2df --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/common.py @@ -0,0 +1,65 @@ +# Copyright 2024 Big Vision 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. + +"""Common things across all transfer configs.""" + + +TOKENIZER = 'gemma(tokensets=("loc", "seg"))' + + +def tok(**kw): + """Creates the tokenization preprocessing string.""" + # Single entry point so that it's consistent everywhere and easier to switch. + kw.setdefault('model', TOKENIZER) + kw = ', '.join(f'{k}={repr(v)}' for k, v in kw.items()) + return f'tok({kw})' + + +def combine_and_keep_train(text_len, before=(), sep='\n'): + return '|'.join([ + *before, + tok(key='prefix', bos='yes'), + tok(key='suffix', eos='yes'), + tok(key='septok', text=sep), + # If masks confuse you, see (internal link) + 'masked_concat(["prefix", "septok", "suffix"], mask_ar=[0, 0, 1], mask_loss=[0, 0, 1])', # pylint: disable=line-too-long + # For training, we +1 since the trainer removes EOS. + f'tolen({text_len+1}, pad_value=0, key="text")', # Value doesn't matter. + f'tolen({text_len+1}, pad_value=1, key="mask_ar")', + f'tolen({text_len+1}, pad_value=0, key="mask_loss")', + 'keep("image", "text", "mask_ar", "mask_loss")', + ]) + + +def combine_and_keep_eval(text_len, keep=tuple(), before=(), sep='\n'): + return '|'.join([ + *before, + # Same as training, except that suffix is now the empty string. + # Meaning, we create text as [prefix separator pad], + # and the mask accordingly as [0 0 1] (with repeats of respective lengths) + tok(key='prefix', bos='yes'), + tok(key='septok', text=sep), + # At eval time, there can be also a suffix key in the data. If so it is + # tokenized without EOS and decoding will continue from it. + 'setdefault("suffix", "")', + tok(key='suffix', eos='no'), + # If masks confuse you, see (internal link) + 'masked_concat(["prefix", "septok", "suffix"], mask_ar=[0, 0, 1], mask_input=[1, 1, 1])', # pylint: disable=line-too-long + f'tolen({text_len}, pad_value=0, key="text")', # value doesn't matter. + f'tolen({text_len}, pad_value=1, key="mask_ar")', + f'tolen({text_len}, pad_value=0, key="mask_input")', + # And we need to keep everything that makes our evaluator happy. + 'keep(' + ', '.join(f'"{x}"' for x in ( + 'image', 'text', 'mask_ar', 'mask_input') + tuple(keep)) + ')', + ]) diff --git a/big_vision/configs/proj/paligemma/transfers/docvqa.py b/big_vision/configs/proj/paligemma/transfers/docvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..2bb9ea9f660820d8b2c7255813560bc8dfab766a --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/docvqa.py @@ -0,0 +1,163 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to docvqa. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224) + final_split: Train on all train+val data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='docvqa', + split='train+val' if final_split else 'train[:-5%]', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'copy(inkey="question", outkey="prefix")', + 'choice(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Add eval configs.""" + pp_eval = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'copy(inkey="question", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (1/8, 'minitrain', 'train[:5%]'), + (1/8, 'minival', 'train[-5%:]'), + (1/8, 'eval', 'val'), + (1.0, 'test', 'test'), + ]: + c.evals[f'docvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + to_lower=True, + outfile=f'{{workdir}}/docvqa_{name}.json', out_question_key='questionId', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp_eval) + c.evals[f'docvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'train[-5%:]'), # To tune hparams. + ('eval', 'val'), # To compute final publishable scores. + ]: + c.evals[f'docvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Based on http://(internal link)/ZCwPqz0b3tE and (internal link) + add(lr=1e-5, wd=1e-6, total_epochs=10, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=10, **bvcc.arg(res=448, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=10, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=896, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 1e-6 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'docvqa/{split}/anls') + m.append(f'docvqa/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/forkme.py b/big_vision/configs/proj/paligemma/transfers/forkme.py new file mode 100644 index 0000000000000000000000000000000000000000..3041fdaaa9c2a3218dafc42964a389a1a5196b47 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/forkme.py @@ -0,0 +1,151 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Example config for finetuning PaliGemma to a task stored in the JSON-L file, designed to fit on four L4 GPU. + +Can be used as a starting point to finetune PaliGemma model. If you prefer to +use tfds-based data input, check out other transfer configs as examples. + +Command to run this config: + +``` +env BV_GEMMA_DIR=ckpts/ python -m big_vision.trainers.proj.paligemma.train \ + --config big_vision/configs/proj/paligemma/transfers/forkme.py \ + --workdir workdirs/`date '+%m-%d_%H%M'` +``` +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, text_len): + """Creates training data config.""" + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='bv:jsonl', + fname='gs://longcap100/data_train90.jsonl', + fopen_keys={'image': 'gs://longcap100/'}, + # See docstring in datasets/jsonl.py for further details. + # download_keys=['image'], # If jsonl contains external paths. + ) + c.pp = '|'.join([ + # Read and prepare the image by just resizing it: + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # The texts are already prepared in `prefix` and `suffix` keys. + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_train(text_len), + ]) + # Keep the whole dataset in RAM after first pass. Useful optimization for + # small/mid-size datasets, but risks a host OOM for large datasets. + c.cache_raw = True + return c + + +def add_eval_pplx(c, res, text_len): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_data = training_data(res, text_len) # Use mostly same settings as training. + c_data.pp = '|'.join([ + # Read and prepare the image by just resizing it: + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # The texts are already prepared in `prefix` and `suffix` keys. + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len), + ]) + + c.evals['val/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/10, + data={**c_data.data, + 'fname': 'gs://longcap100/data_val10.jsonl', + }, + pp_fn=c_data.pp + ) + + +def add_eval_store(c, res, text_len=32): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + c_data = training_data(res, text_len) # Use mostly same settings as training. + c_data.pp = '|'.join([ + # Read and prepare the image by just resizing it: + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # The texts are already prepared in `prefix` and `suffix` keys. + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('id',)), + ]) + + c.evals['val/store'] = dict( + type='proj.paligemma.transfers.storepreds', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=0.5, tokenizer=TOKENIZER, + data={**c_data.data, + 'fname': 'gs://longcap100/data_val10.jsonl', + }, + pp_fn=c_data.pp, + ) + + +def get_config(arg=None): + """Config for training.""" + # You probably do NOT want to add settings here. The `arg` way of settings is + # really only for things you'd want to sweep and which affect MULTIPLE config + # settings at once or go into the pp string. + c = bvcc.parse_arg(arg, res=224, text_len=128, batch_size=32, + freeze_vit=False, freeze_llm=False, + run_local=False) + + c.input = training_data(c.res, c.text_len) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 15 + c.input.batch_size = c.batch_size + c.optax_name = 'scale_by_adam' + c.lr = 1e-5 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. Probably is fine like this. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + c.evals = {} + add_eval_pplx(c, c.res, c.text_len) + add_eval_store(c, c.res, c.text_len) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + c.input.shuffle_buffer_size = 1000 + c.log_training_steps = 1 + c.ckpt_steps = 200 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + c.seed = 0 + + return c diff --git a/big_vision/configs/proj/paligemma/transfers/gqa.py b/big_vision/configs/proj/paligemma/transfers/gqa.py new file mode 100644 index 0000000000000000000000000000000000000000..531b0964d581c5dac68252cffae4c5eb44f3cffb --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/gqa.py @@ -0,0 +1,197 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to GQA (https://arxiv.org/abs/1902.09506). +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +XGQA_LANGUAGES = ('bn', 'de', 'en', 'id', 'ko', 'pt', 'ru', 'zh') + + +def training_data(res, *, final_split, prefix, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to train on train+val. + prefix: The prefix to use for the input. E.g. "answer en {question}" + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='gqa', + split='train_balanced+val_balanced' if final_split else 'train_balanced', + ) + c.pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + f'strfmt("{prefix}", outkey="prefix")', + 'copy(inkey="answer", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, *, text_len=32, prefix, **kw): + """GQA evaluators.""" + c_train = training_data(res, final_split=True, prefix=prefix, text_len=text_len) + + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + 'copy(inkey="example_id", outkey="question_id")', + # GQA: both questions and answers are always in english. + # xGQA: questions in different languages. Answers always in english. + f'strfmt("{prefix}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for freq, name, split, skip_first in [ + # TODO: adjust the proportion of dataset seen in these minivals + # based speed on hardware. + (1/8, 'minitrain', 'train_balanced[:10000]', False), # To gauge memorization. + (1/8, 'val_balanced', 'val_balanced', True), # To tune hparams. + (1.0, 'testdev_balanced', 'testdev_balanced', True), # To compute final publishable scores. + ]: + c.evals[f'gqa/{name}/decode'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/gqa_{name}.json', + out_question_key='question_id', out_answer_key='prediction', + data={**c_train.data, 'split': split}, + log_percent=freq, skip_first=skip_first, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'gqa/{name}/decode'].update(kw) + + # Add XGQA evaluators. Zero shot since the model is trained only in GQA (en). + for lang in XGQA_LANGUAGES: + c.evals[f'xgqa/test_zs_{lang}/decode'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/xgqa_test_{lang}.json', + data=dict( + name='xgqa', + split=f'test_zs_{lang}', # Zero-shot split + ), + log_percent=1/8, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'xgqa/test_zs_{lang}/decode'].update(kw) + + +def add_eval_pplx(c, res, *, text_len=32, prefix): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len, prefix=prefix) + for name, split in [ + ('minitrain', 'train_balanced[:5%]'), # To gauge memorization. + ('minival', 'val_balanced[:5%]'), # To tune hparams. + ]: + c.evals[f'gqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Based on (internal link), (internal link), (internal link). + # TODO: Is there a more compreensive sweep and can we use + # freeze_vit=False for all resolutions (and more common in other configs)? + add(lr=1e-5, wd=0.0, **bvcc.arg(res=224, freeze_vit=False, **c)) + add(lr=1e-5, wd=0.0, **bvcc.arg(res=448, freeze_vit=True, **c)) + # Not better: add(lr=1e-5, wd=0.0, **bvcc.arg(res=896, freeze_vit=True, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False, + freeze_vit=True, freeze_llm=False, + prefix='answer en {question}') + + c.name = '' + c.input = training_data(c.res, final_split=c.final_split, prefix=c.prefix) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 1 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. Probably is fine like this. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024, prefix=c.prefix) + add_eval_pplx(c, c.res, prefix=c.prefix) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(): + m = ['training_loss'] + m.append('gqa/minitrain/pplx/avg') + m.append('gqa/minival/pplx/avg') + m.append('gqa/minitrain/decode/acc') + m.append('gqa/val_balanced/decode/acc') + m.append('gqa/testdev_balanced/decode/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/infovqa.py b/big_vision/configs/proj/paligemma/transfers/infovqa.py new file mode 100644 index 0000000000000000000000000000000000000000..a9ea80adfbcc1acb82c9307f107f110bd648f896 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/infovqa.py @@ -0,0 +1,172 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to InfoVQA. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=48): + """Creates training data config. + + See Colab: + http://(internal link)#scrollTo=0yIyusCLhdDy + + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all train data. + text_len: The maximum text length (in tokens). + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='infovqa', + split='train+val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=48, **kw): + """VQA evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (0.1, 'minitrain', 'train[:5%]'), # To gauge memorization. ~1.2k samples. + (0.1, 'minival', 'val'), # To tune hparams. ~1.2k samples. + (1.0, 'test', 'test'), # Also stores predictions for the test set (3.3k). + ]: + c.evals[f'infovqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + # Answers in reference evaluation are converted to lower case. + to_lower=True, + # Test server expects two fields: 'questionId' and 'answer'. + out_question_key='questionId', out_answer_key='answer', + outfile=f'{{workdir}}/infovqa_{name}.json', + data={**training_data(res, False, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'infovqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=48): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, False, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'val'), # To tune hparams. + ]: + c.evals[f'infovqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + # Based on http://(internal link)/ZCwPqz0b3tE and (internal link) + # In InfoVQA: max prefix: 51, suffix: 42, prefix+sep+suffix: 60. + c = bvcc.parse_arg(arg, mode='xm', final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=3, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=3, **bvcc.arg(res=448, **c)) + add(lr=3e-6, wd=3e-7, total_epochs=3, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=896, text_len=48, final_split=False) + + c.input = training_data(c.res, c.final_split, c.text_len) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 3 + c.input.batch_size = {224: 256, 448: 128, 896: 32}[c.res] + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.4 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.text_len, batch_size=256) + add_eval_pplx(c, c.res, c.text_len) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + del c.total_epochs + c.total_steps = 10 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'minitrain'): + m.append(f'infovqa/{split}/anls') + m.append(f'infovqa/{split}/acc') + m.append(f'infovqa/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/msrvtt_cap.py b/big_vision/configs/proj/paligemma/transfers/msrvtt_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..6b81ae293deb99c85aac40366f41f83a555f2fed --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/msrvtt_cap.py @@ -0,0 +1,210 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to MSRVTT Video captioning. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 32 +DATASET_NAME = 'msrvtt' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 4663, 'valid': 316, 'test': 2094, 'test_v2': 2271} + + +def training_data(res, *, final_split, num_frames, stride): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # pick one caption at random during training + 'strfmt("caption en", outkey="prefix")', + 'video_choice(inkey="caption/string", outkey="suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames, stride): # pylint: disable=unused-argument + """Captioning evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + 'strfmt("caption en", outkey="prefix")', + 'strfmt("{example/video_id}[{clip/start/timestamp}-{clip/end/timestamp}]", outkey="image/id")', + 'copy("caption/string", "captions")', + combine_and_keep_eval(TEXT_LEN, keep=('image/id', 'captions')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 2, False), # To gauge memorization. + (1/4, 'minival', 'valid', 2, False), # To monitor val progress. + (1, 'val', 'valid', None, False), # To tune hparams. + (1, 'eval', 'test', None, False), # final metric + ]: + c.evals[f'msrvtt/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames, stride): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 2), # To gauge memorization. + ('minival', 'valid', 2), + ]: + c.evals[f'msrvtt/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=0.0, total_epochs=20, **bvcc.arg(res=224, freeze_vit=True, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=16, stride=9, res=224, + freeze_vit=False, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 10 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 1e-6 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append(('epoch', f'{DATASET_NAME}/{split}/cider')) + for split in ('minitrain', 'minival'): + m.append(('epoch', f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/msrvtt_qa.py b/big_vision/configs/proj/paligemma/transfers/msrvtt_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..08df9c56245b27db1d31a8f07de1b5967381e78c --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/msrvtt_qa.py @@ -0,0 +1,213 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to MSRVTT Video QA. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 64 +DATASET_NAME = 'msrvtt_qa' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 114080, 'valid': 7936, 'test': 51680} + + +def training_data(res, *, final_split, num_frames, stride): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy("answer", "suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames, stride): # pylint: disable=unused-argument + """QA evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{id}#{example/video_id}: {question}", "question_id")', + combine_and_keep_eval(TEXT_LEN, keep=('question_id', 'answer')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 2, False), # To gauge memorization. + (1/4, 'minival', 'valid', 2, False), # To monitor val progress. + (1, 'val', 'valid', None, False), # To tune hparams. + (1, 'eval', 'test', None, False), # final metric + ]: + c.evals[f'msrvtt_qa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames, stride): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 2), # To gauge memorization. + ('minival', 'valid', 2), # To tune hparams. + ]: + c.evals[f'msrvtt_qa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=0.0, total_epochs=1, **bvcc.arg(freeze_vit=True, res=224, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=16, stride=9, res=224, + freeze_vit=True, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 1 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 0.00001 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append(('epoch', f'{DATASET_NAME}/{split}/acc')) + for split in ('minitrain', 'minival'): + m.append(('epoch', f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/msvd_qa.py b/big_vision/configs/proj/paligemma/transfers/msvd_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..0832ad7185c714056bb39c7390c711a5e03d3b48 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/msvd_qa.py @@ -0,0 +1,214 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to MSVD Video QA. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 64 +DATASET_NAME = 'msvd_qa' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 24670, 'valid': 5107, 'test': 10136} # 2024-04-28 + + +def training_data(res, *, final_split, num_frames=8, stride=None): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + # ensure_shape is needed for tf to figure out the tensor shape + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy("answer", "suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames=8, stride=None): + """QA evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # only one question/answer per example. + 'reshape([], key="question")|reshape([], key="answer")', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{id}#{example/video_id}[{clip/start/timestamp}-{clip/end/timestamp}]: {question}", outkey="question_id")', + combine_and_keep_eval(TEXT_LEN, keep=('question_id', 'answer')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 1, False), # To gauge memorization. + (1/4, 'minival', 'valid', 1, False), # To monitor val progress. + (1, 'val', 'valid', None, False), # To tune hparams. + (1, 'eval', 'test', None, False), # final metric + ]: + c.evals[f'{DATASET_NAME}/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames=8, stride=None): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 1), # To gauge memorization. + ('minival', 'valid', 1), # To tune hparams. + ]: + c.evals[f'{DATASET_NAME}/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=3e-6, wd=3e-7, total_epochs=1, **bvcc.arg(res=224, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=8, stride=26, res=224, + freeze_vit=False, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 3 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append(('epoch', f'{DATASET_NAME}/{split}/acc')) + for split in ('minitrain', 'minival'): + m.append(('epoch', f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/nlvr2.py b/big_vision/configs/proj/paligemma/transfers/nlvr2.py new file mode 100644 index 0000000000000000000000000000000000000000..3e76a158ba26986e890e9ab6b2e38d0499ebc34b --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/nlvr2.py @@ -0,0 +1,169 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to NLVR2 captions, including evaluation on MaRVL. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +LANGS = ('id', 'sw', 'ta', 'tr', 'zh') + + +def training_data(res, *, final_split, text_len=64): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+dev data. + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='nlvr2', + split='train+dev' if final_split else 'train', + ) + + num_frames = 2 + c.pp = '|'.join([ + f'resize({res}, key="image_left")|resize({res}, key="image_right")', + 'stack_images(inkeys=["image_left", "image_right"], outkey="image")', + 'value_range(-1, 1)', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + 'strfmt("answer en {sentence}", outkey="prefix")', + 'copy(inkey="label", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=64, **kw): + """QA evaluator.""" + # Input eval pp without ground truth text and random crop. + num_frames = 2 + pp_eval = '|'.join([ + f'resize({res}, key="image_left")|resize({res}, key="image_right")', + 'stack_images(inkeys=["image_left", "image_right"], outkey="image")', + 'value_range(-1, 1)', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + 'strfmt("answer en {sentence}", outkey="prefix")', + 'copy(inkey="label", outkey="answer")', + 'copy(inkey="example_id", outkey="question_id")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for name, split in [ + ('minitrain', 'train[:10000]'), + ('dev', 'dev'), + ('test', 'test'), + ]: + c.evals[f'nlvr2/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/nlvr2_{name}.json', + data={**training_data(res, final_split=True, text_len=text_len).data, 'split': split}, + log_percent=0.1, tokenizer=TOKENIZER, pp_fn=pp_eval) + c.evals[f'nlvr2/{name}'].update(kw) + + for lang in LANGS: + c.evals[f'marvl/test_{lang}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/marvl_test_{lang}.json', + data=dict( + name='marvl', + split=f'test_{lang}', + ), + log_percent=0.1, tokenizer=TOKENIZER, pp_fn=pp_eval) + c.evals[f'marvl/test_{lang}'].update(kw) + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 3 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-4 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=3, **bvcc.arg(res=224, **c)) + add(lr=3e-6, wd=3e-7, total_epochs=10, **bvcc.arg(res=448, **c)) + + +sweep = sweep_best + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'dev', 'test'): + m.append(f'nlvr2/{split}/acc') + for lang in LANGS: + m.append(f'marvl/test_{lang}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/ocrvqa.py b/big_vision/configs/proj/paligemma/transfers/ocrvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..3ce0efa8bcdf8aad9dd544301bab5586d1898552 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/ocrvqa.py @@ -0,0 +1,171 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to OCR-VQA, see (internal link) for details and notes. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+val data or train+val[20000:]. + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + # Note that the dataset is "unbatched" here, meaning each image_question pair + # is one example. So while there are ~800k training examples, there's only + # ~200k unique images, each one having on average 4 questions, and the + # questions are highly regular: + # - What is the title of this book? + # - What type of book is this? OR What is the genre of this book? + # - Who wrote this book? OR Who is the author of this book? + # - Is this book related to [GENRE]? OR Is this a [GENRE] book? "yes" + # - Same but with answer "no" + # So one obvious thing we could do in training is randomize the negative genre + # question more using a custom pp op. + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='ocrvqa_id', + split='train+val' if final_split else 'train + val[20_000:]', # Val is 100k, we don't need that much! + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy(inkey="answer", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """OCR-VQA evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy(inkey="int_id", outkey="question_id")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for freq, name, split in [ + (1/8, 'minitrain', 'train[:5120]'), # To gauge memorization. + (1/4, 'minival', 'val[:20_000]'), # To tune hparams. SLOW! + (1.0, 'eval', 'test'), # Final number to report. Big => rare. + ]: + c.evals[f'ocrvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + to_lower=True, + pred='decode', pred_kw={'max_decode_len': text_len}, + data={**training_data(res, final_split=True, text_len=text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'ocrvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5120]'), # To gauge memorization. + ('minival', 'val[:20_000]'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + c.evals[f'ocrvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.1, # Eval ~10x per run; + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(**bvcc.arg(res=224, **c), lr=3e-6) + add(**bvcc.arg(res=448, **c), lr=3e-6) + add(**bvcc.arg(res=896, **c), lr=1e-5) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 3 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'ocrvqa/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/okvqa.py b/big_vision/configs/proj/paligemma/transfers/okvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..cc5090362b3bf9ac8acfddbe8f02f279f80b4d2b --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/okvqa.py @@ -0,0 +1,161 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to OK-VQA. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all train data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='okvqa', + split='train' if final_split else 'train[:-10%]', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """We can use the normal VQA evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (1/4, 'minitrain', 'train[:5%]'), # To gauge memorization. + (1/4, 'minival', 'train[-10%:]'), # To tune hparams. + (1/4, 'eval', 'val'), # To compute final publishable scores. + ]: + c.evals[f'okvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/okvqa_{name}.json', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'okvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'train[-10%:]'), # To tune hparams. + ('eval', 'val'), # To compute final publishable scores. + ]: + c.evals[f'okvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(**bvcc.arg(res=224, **c)) + add(**bvcc.arg(res=448, **c)) + # Not better: add(**bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 5e-6 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'okvqa/{split}/pplx/avg') + m.append(f'okvqa/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/pope.py b/big_vision/configs/proj/paligemma/transfers/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..26610551504b5ecba78cecb0fd6903b1d9200f52 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/pope.py @@ -0,0 +1,118 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma (0-shot) evaluation in POPE. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +_DATASETS = ('pope_random', 'pope_popular', 'pope_adversarial') + + +# Note that POPE does not have training data, only test data. +# We're defining training_data() here anyway for symmetry with the other +# transfers. We will train for 0 steps on this data, i.e. not at all. +def training_data(res, *, text_len, prefix): + """Creates training data config. + + See (internal link) + + Args: + res: The requested image resolution (eg 224). + text_len: sequence length. + prefix: prefix to use in the prompt: (e.g. 'answer en {question}') + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + f'strfmt("{prefix}", outkey="prefix")', + 'copy(inkey="answer", outkey="suffix")', + combine_and_keep_train(text_len=text_len), + ]) + c.data = {} + for dataset in _DATASETS: + c.data[dataset] = 1 # Set weight to 1. + c[dataset] = dict( + pp=pp, + data=dict( + name=dataset, + split='test', + ), + ) + return c + + +def add_eval(c, res, *, text_len, prefix): + """Add eval configs.""" + pp_eval = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + f'strfmt("{prefix}", outkey="prefix")', + combine_and_keep_eval(text_len=text_len, keep=('question_id', 'answer')), + ]) + for dataset in _DATASETS: + c.evals[f'pope/{dataset}'] = dict( + type='proj.paligemma.transfers.pope', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=1, tokenizer=TOKENIZER, + data=dict( + name=dataset, + split='test', + ), + pp_fn=pp_eval, + ) + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, text_len=48, prefix='{question}') + + c.name = '' # Help to track experiments. + c.input = training_data(c.res, text_len=c.text_len, prefix=c.prefix) + + # Make the config eval-only by setting some dummies. + c.total_steps = 0 + c.input.batch_size = 256 + c.optax_name = 'identity' + c.lr = 0.0 + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, text_len=c.text_len, prefix=c.prefix) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + return c \ No newline at end of file diff --git a/big_vision/configs/proj/paligemma/transfers/refcoco_seg.py b/big_vision/configs/proj/paligemma/transfers/refcoco_seg.py new file mode 100644 index 0000000000000000000000000000000000000000..4e2725f2c4eee478a5799b607378e5615598b937 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/refcoco_seg.py @@ -0,0 +1,198 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to RefCOCO (with segmentation). +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, text_len=48, crop='rs'): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224) + text_len: sequence length + crop: What way to do random cropping to get to res. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='ref_coco_bv/refcocox_combined:1.4.0', + split='train', + ) + + if crop == 'rs': + crop_ops = f'resize({res})' + elif crop == 'zic_mild': + crop_ops = '|'.join([ + 'zoomout(max_f=1.5, key="image", bboxkey="objects/bbox", auxkeys=["objects/mask"])', + f'inception_box(area=(0.1,1.0), aspect=({3/4},{4/3}), min_obj_cover=1.0, bboxkey="objects/bbox")', + 'box_crop_bbox', + 'box_crop_img(key="objects/mask")', + 'box_crop_img(key="image")', + f'resize({res})', + ]) + else: + raise ValueError(crop) + + c.pp = '|'.join([ + 'flatten', + 'choice_no_replacement(key=["objects/mask", "objects/bbox", "objects/refs/sentence"])', + 'choice(key=["objects/refs/sentence"])', + 'decode', + crop_ops, + 'value_range(-1, 1)', + 'refcoco_mask2str', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=48, **kw): + """Segmentation evaluator computing mIoU.""" + # NOTE: we verified that squeezing to square at eval time is no worse than + # padding with black borders. The actual evaluation is still done in original + # full resolution of the mask, of course. + pp_eval_squeeze = '|'.join([ + 'flatten', + # (choice simply removes a dimension since it's already flattened) + 'choice(key=["objects/mask", "objects/bbox", "objects/refs/sentence"])', + 'choice(key=["objects/refs/sentence"], outkey="prefix")', + # 'refcoco_mask2str', # TODO: b/lbeyer - also eval decoded GT mask? + f'decode|resize({res})|value_range(-1, 1)', + combine_and_keep_eval(text_len, keep=('objects/mask', 'objects/bbox', 'width', 'height')), + ]) + + for freq, name, ds_name, split in [ + (0.2, 'refcoco/val', 'ref_coco_bv/refcoco_unc:1.4.0', 'validation_flat'), + (1.0, 'refcoco/testA', 'ref_coco_bv/refcoco_unc:1.4.0', 'testA_flat'), + (1.0, 'refcoco/testB', 'ref_coco_bv/refcoco_unc:1.4.0', 'testB_flat'), + (1.0, 'refcocop/val', 'ref_coco_bv/refcocoplus_unc:1.4.0', 'validation_flat'), + (1.0, 'refcocop/testA', 'ref_coco_bv/refcocoplus_unc:1.4.0', 'testA_flat'), + (1.0, 'refcocop/testB', 'ref_coco_bv/refcocoplus_unc:1.4.0', 'testB_flat'), + (1.0, 'refcocog/val', 'ref_coco_bv/refcocog_umd:1.4.0', 'validation_flat'), + (1.0, 'refcocog/test', 'ref_coco_bv/refcocog_umd:1.4.0', 'test_flat'), + ]: + c.evals[f'seg/{name}'] = dict( + type='proj.paligemma.transfers.segmentation', + pred='decode', pred_kw={'max_decode_len': text_len}, + data={'name': ds_name, 'split': split}, + log_percent=freq, skip_first=freq == 1, + tokenizer=TOKENIZER, pp_fn=pp_eval_squeeze) + c.evals[f'seg/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=48): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # 1_220 + ('val', 'validation'), # 2_738 + ]: + c.evals[f'refcoco_seg/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): # pylint: disable=unused-argument + """Train with best hyper-params per resolution.""" + # Based on (internal link) + add(**bvcc.arg(res=224), lr=3e-5, total_epochs=100, label_smoothing=0.3, + **{'model.llm.dropout': 0.1, 'input.batch_size': 256}) + add(**bvcc.arg(res=448), lr=1e-5, total_epochs=100, label_smoothing=0.3, + **{'model.llm.dropout': 0.0, 'input.batch_size': 256}) + # Takes 2d on 16 TPUv5e, gives overall +0.5-+1 over 448. + add(**bvcc.arg(res=896), lr=1e-5, total_epochs=100, label_smoothing=0.3, + **{'model.llm.dropout': 0.0, 'input.batch_size': 64}) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Default "reasonably good" training config, gets about 75/67/70.""" + c = bvcc.parse_arg(arg, mode='xm', res=448, crop='rs') + + c.input = training_data(c.res, crop=c.crop) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 5 + c.input.batch_size = 64 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.3 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True, dropout=0.0) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = [ + 'ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.segmentation', + ] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for thing in ('miou', 'boxacc/0.5', 'invalid'): + m.append(f'seg/refcoco/val/{thing}') + for split in ('/testA', '/testB', 'p/val', 'p/testA', 'p/testB', 'g/val', 'g/test'): + m.append(f'seg/refcoco{split}/miou') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/rsvqa_hr.py b/big_vision/configs/proj/paligemma/transfers/rsvqa_hr.py new file mode 100644 index 0000000000000000000000000000000000000000..bfe5b1058db8f2d6962fb0d3442e33c920942219 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/rsvqa_hr.py @@ -0,0 +1,182 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to RSVQA-HR. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +ONLY_NON_NUMERIC_ANSWERS = True +# text_len: max prefix: 31, suffix: 5, prefix+sep+suffix: 32. + + +def training_data(res, *, final_split, text_len=32): + """Creates training data config. + + See Colab: + http://(internal link)#scrollTo=1jZ-9FMPVD-q + + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all of the validation data. + text_len: The maximum text length (in tokens). + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='rsvqa_hr/nonum' if ONLY_NON_NUMERIC_ANSWERS else 'rsvqa_hr/all', + split='train + val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Add evaluators.""" + c_train = training_data(res, final_split=True, text_len=text_len) + + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_type', 'question_id')), + ]) + + for freq, name, split in [ + (0.1, 'minitrain', 'train[:1280]'), + (1.0, 'minival', 'val'), # Very slow (large) + (1.0, 'test', 'test'), # Very slow (large) + (1.0, 'test2', 'test_2'), # Very slow (large) + ]: + c.evals[f'rsvqa_hr/{name}'] = dict( + type='proj.paligemma.transfers.rsvqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + to_lower=True, + outfile=f'{{workdir}}/rsvqa_hr_{name}.json', + data={**c_train.data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'rsvqa_hr/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) + for name, split in [ + ('minitrain', 'train[:1280]'), + ('minival', 'val'), + ]: + c.evals[f'rsvqa_hr/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.1, + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=1e-5, wd=0.0, total_epochs=1, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=0.0, total_epochs=1, **bvcc.arg(res=448, **c)) + # 896 not better: (internal link) + # add(lr=1e-5, wd=0.0, total_epochs=1, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 1 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + del c.total_epochs + c.total_steps = 10 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'test', 'test2', 'minitrain'): + m.append(f'rsvqa_hr/{split}/acc_any') + m.append(f'rsvqa_hr/{split}/acc_avg') + m.append(f'rsvqa_hr/{split}/acc_avg_nonum') + m.append(f'rsvqa_hr/{split}/anls') + m.append(f'rsvqa_hr/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/rsvqa_lr.py b/big_vision/configs/proj/paligemma/transfers/rsvqa_lr.py new file mode 100644 index 0000000000000000000000000000000000000000..2e0f91cefa97688209036be13b8900553d26c613 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/rsvqa_lr.py @@ -0,0 +1,181 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to RSVQA-LR. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +ONLY_NON_NUMERIC_ANSWERS = True +# text_len: max prefix: 31, suffix: 5, prefix+sep+suffix: 32. + + +def training_data(res, *, final_split, text_len=32): + """Creates training data config. + + See Colab: + http://(internal link)#scrollTo=1jZ-9FMPVD-q + + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all of the validation data. + text_len: The maximum text length (in tokens). + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='rsvqa_lr/nonum' if ONLY_NON_NUMERIC_ANSWERS else 'rsvqa_lr/all', + split='train + val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Add evaluators.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_type', 'question_id')), + ]) + + for freq, name, split in [ + (0.1, 'minitrain', 'train[:1280]'), + (0.1, 'minival', 'val'), + (0.1, 'test', 'test'), + ]: + c.evals[f'rsvqa_lr/{name}'] = dict( + type='proj.paligemma.transfers.rsvqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + to_lower=True, + outfile=f'{{workdir}}/rsvqa_lr_{name}.json', + data={**c_train.data, 'split': split}, + log_percent=freq, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'rsvqa_lr/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:1280]'), + ('minival', 'val'), + ]: + c.evals[f'rsvqa_lr/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.1, + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Note: best performance achieved at 224. + add(lr=3e-6, wd=0.0, total_epochs=3, **bvcc.arg(res=224, **c)) + add(lr=3e-6, wd=0.0, total_epochs=3, **bvcc.arg(res=448, **c)) + # add(lr=3e-6, wd=0.0, total_epochs=3, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 3 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.2 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + del c.total_epochs + c.total_steps = 10 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'test', 'minitrain'): + m.append(f'rsvqa_lr/{split}/acc_any') + m.append(f'rsvqa_lr/{split}/acc_avg') + m.append(f'rsvqa_lr/{split}/acc_avg_nonum') + m.append(f'rsvqa_lr/{split}/anls') + m.append(f'rsvqa_lr/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/scicap.py b/big_vision/configs/proj/paligemma/transfers/scicap.py new file mode 100644 index 0000000000000000000000000000000000000000..51618033ef6d70db125b5e5cef092cc335cae9ae --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/scicap.py @@ -0,0 +1,167 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to SciCap. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +# This feature name is also hardcoded in the coco_captions evaluator. +CAPTION_FEATURE = 'caption/lowercase_and_token_and_remove_figure_index' + + +def training_data(res, *, final_split, text_len=96): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (e.g. 224). + final_split: Train on all train+val data. + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='scicap/first_sentence_subfig_no', + split='train+val' if final_split else 'train', + ) + c.pp = '|'.join([ + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + f'copy(inkey="{CAPTION_FEATURE}", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=96, **kw): + """SciCap evaluator (BLEU-4).""" + pp = '|'.join([ + f'reshape([1], inkey="{CAPTION_FEATURE}", outkey="captions")', # GT for evaluator. + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('val', 'val'), # To tune hparams + ('test', 'test'), + ]: + c.evals[f'scicap/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + metrics=('cider', 'bleu-4'), + data=dict( + name='scicap/first_sentence_subfig_no', + split=split, + ), + log_percent=0.3, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'scicap/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=96): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('val', 'val'), # To tune hparams. + ('test', 'test'), + ]: + c.evals[f'scicap/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(**bvcc.arg(res=224, **c)) + add(**bvcc.arg(res=448, **c)) + # 896 is too slow and not better than 448: (internal link) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 80 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.1 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.1) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('test', 'val', 'minitrain'): + m.append(f'scicap/{split}/pplx/avg') + m.append(f'scicap/{split}/cider') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/science_qa.py b/big_vision/configs/proj/paligemma/transfers/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..5b8777b63f707a98b922be9a6eb6ee90834d27de --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/science_qa.py @@ -0,0 +1,225 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to science_qa. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=512, qfmt='QCM', afmt='A'): + """Creates training data config. + + See + (internal link) + You can add more arguments beside `res`, but give them good defaults. + implemented based on : + https://github.com/lupantech/ScienceQA/blob/main/models/base_prompt.py + default prompt format baseline: QCM -> A (see paper) + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+val data. + text_len: sequence length. + qfmt: see config_prompt_format. + afmt: see config_prompt_format. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='science_qa', + split='train+val' if final_split else 'train', + ) + qfmt, afmt = config_prompt_format(qfmt, afmt) + c.pp = '|'.join([ + # Read and prepare the image by just resizing it: + f'decode|resize({res})|value_range(-1, 1)', + 'drop("indexed_choices","indexed_answer")', + "sci_qa_choices_shuffle(choice_str_inkey='choices', ans_inkey='answer')", + f'strfmt("{qfmt}", outkey="prefix")', + f'strfmt("{afmt}", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def config_prompt_format(qfmt='QCM', afmt='A'): + """Configure prompt format, default: QCM -> A. + + See https://github.com/lupantech/ScienceQA/blob/main/models/base_prompt.py + Args: + qfmt: input prompt format -> the question text (Q), the context text + (C), and multiple options (M) + afmt: out prompt format -> A = answer, AE = answer with + explanation, ALE = answer with lecture and explanation. + + Returns: + prompt format string for training data config to digest + """ + # TODO: b/keranrong - The \\nAnswer: is useless for our model. + if qfmt == 'simple': + qfmt = '{question}\\nOptions: {indexed_choices}' + elif qfmt == 'QM': + qfmt = 'Question: {question}\\nOptions: {indexed_choices}\\nAnswer:' + elif qfmt == 'CQM': + qfmt = 'Context: {hint}\\nQuestion: {question}\\nOptions: {indexed_choices}\\nAnswer:' + elif qfmt == 'QCM': + qfmt = 'Question: {question}\\nContext: {hint}\\nOptions: {indexed_choices}\\nAnswer:' + else: + raise ValueError(qfmt) + + if afmt == 'simple': + afmt = '{indexed_answer}' + elif afmt == 'A': + afmt = 'The answer is {indexed_answer}.' + elif afmt == 'AL': + afmt = 'The answer is {indexed_answer}. BECAUSE: {solution}' + elif afmt == 'AE': + afmt = 'The answer is {indexed_answer}. BECAUSE: {lecture}' + elif afmt == 'ALE': + afmt = 'The answer is {indexed_answer}. BECAUSE: {lecture} {solution}' + else: + raise ValueError(afmt) + + return qfmt, afmt + + +def add_eval(c, res, text_len=512, qfmt='QCM', afmt='A', **kw): + """Science QA evaluators.""" + prefix, suffix = config_prompt_format(qfmt, afmt) + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + f'strfmt("{prefix}", outkey="prefix")', + f'strfmt("{suffix}", outkey="answer")', + 'copy(inkey="_id",outkey="question_id")', + combine_and_keep_eval(text_len, keep=('answer', 'question_id')), + ]) + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'val'), # To tune hparams. + ('eval', 'test'), + ]: + c.evals[f'science_qa/{name}'] = dict( + type='proj.paligemma.transfers.science_qa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/science_{name}.json', + data={**training_data(res, final_split=True, text_len=text_len, qfmt=qfmt, afmt=afmt).data, 'split': split}, + log_percent=1/8, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'science_qa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=512, qfmt='QCM', afmt='A'): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len, qfmt=qfmt, afmt=afmt) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'val'), # To tune hparams. + ]: + c.evals[f'science_qa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Based on sweep xids/98045006 (science_qa/eval/acc). + # TODO: b/keranrong - try getting rid of freezing + add(lr=1e-5, wd=0, **bvcc.arg(freeze_vit=True, res=224, **c)) + add(lr=1e-5, wd=0, **bvcc.arg(freeze_vit=True, res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=448, final_split=False, freeze_vit=False, freeze_llm=False, qfmt='QCM', afmt='A') + + c.input = training_data(c.res, final_split=c.final_split, qfmt=c.qfmt, afmt=c.afmt) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 20 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 5e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. Probably is fine like this. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, qfmt=c.qfmt, afmt=c.afmt, batch_size=1024) + add_eval_pplx(c, c.res, qfmt=c.qfmt, afmt=c.afmt) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = [ + 'ops_general', + 'ops_image', + 'ops_text', + 'proj.paligemma.ops', + 'proj.paligemma.sciqa_ops', + ] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'science_qa/{split}/pplx/avg') + m.append(f'science_qa/{split}/acc') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/screen2words.py b/big_vision/configs/proj/paligemma/transfers/screen2words.py new file mode 100644 index 0000000000000000000000000000000000000000..da0665291e0ed1bab103429694bda53034348f57 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/screen2words.py @@ -0,0 +1,164 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to Screen2words. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, text_len=24, final_split=False): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (e.g. 224). + text_len: sequence length + final_split: Train also on val or not. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='screen2_words', + split='train+dev' if final_split else 'train', + ) + c.pp = '|'.join([ + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + 'copy(inkey="image/id", outkey="_id")', + 'choice_no_replacement(inkey="summary", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=24, **kw): + """Screen2words evaluator (CIDER).""" + pp = '|'.join([ + 'copy("summary", "captions")', # GT for evaluator. + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('dev', 'dev'), # To tune hparams + ('test', 'test'), + ]: + c.evals[f'screen2words/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + data=dict( + name='screen2_words', + split=split, + ), + log_percent=0.1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'screen2words/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=24): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('dev', 'dev'), # To tune hparams. + ('test', 'test'), + ]: + c.evals[f'screen2words/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(**bvcc.arg(res=224, **c)) + add(**bvcc.arg(res=448, **c)) + # 896 not better than 448. + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.2 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.3) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('test', 'dev', 'minitrain'): + m.append(('epoch', f'screen2words/{split}/cider')) + m.append(('epoch', f'screen2words/{split}/pplx/avg')) + return m diff --git a/big_vision/configs/proj/paligemma/transfers/stvqa.py b/big_vision/configs/proj/paligemma/transfers/stvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..1f3be64e82cad0c29960fa06959340b0ce2c1231 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/stvqa.py @@ -0,0 +1,175 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to Scene Text VQA (ST-VQA). +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=48): + """Creates training data config. + + See Colab: + http://(internal link)#scrollTo=1jZ-9FMPVD-q + + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all train data. + text_len: The maximum text length (in tokens). + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='stvqa', + split='train+val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=48, **kw): + """Add evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + 'lower(key="answers")', + 'strfmt("answer en {question}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (0.1, 'minitrain', 'train[:5%]'), # To gauge memorization. ~2.5k samples. + (0.1, 'minival', 'val'), # Pseudo-test. + (1.0, 'test', 'test'), # Also stores predictions for the test set (4.1k). + ]: + c.evals[f'stvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + # Answers in reference evaluation are converted to lower case. + # See https://rrc.cvc.uab.es/?ch=17&com=tasks or paper. + to_lower=True, + outfile=f'{{workdir}}/stvqa_{name}.json', + data={**training_data(res, False, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'stvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=48): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, False, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'val'), # To tune hparams. + ]: + c.evals[f'stvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + # Based on http://(internal link)/ZCwPqz0b3tE and (internal link) + c = bvcc.parse_arg(arg, mode='xm', final_split=False) + add(lr=1e-5, wd=1e-6, total_epochs=3, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=3, **bvcc.arg(res=448, **c)) + add(lr=3e-6, wd=3e-7, total_epochs=3, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + # max prefix: 29, suffix: 50, prefix+sep+suffix: 59 + c = bvcc.parse_arg(arg, mode='xm', res=896, text_len=48, final_split=False) + + c.input = training_data(c.res, c.final_split, c.text_len) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 5 + c.input.batch_size = {224: 256, 448: 128, 896: 32}[c.res] + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.1 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.text_len, batch_size=256) + add_eval_pplx(c, c.res, c.text_len) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + del c.total_epochs + c.total_steps = 10 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'minitrain'): + m.append(f'stvqa/{split}/anls') + m.append(f'stvqa/{split}/acc') + m.append(f'stvqa/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/tallyqa.py b/big_vision/configs/proj/paligemma/transfers/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..738a95226e4fe31a558799e39a03e3c67dac0300 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/tallyqa.py @@ -0,0 +1,191 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to TallyQA. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224) + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='tallyqa', + split='train', + ) + + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{answer}", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def countbenchqa_eval_data(res, text_len=32): + """Creates eval data config for CountBenchQA.""" + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='countbenchqa', + split='huggingface', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{number}", outkey="answer")', + combine_and_keep_eval(text_len, keep=('answer',)), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Add eval configs.""" + tallyqa_pp_eval = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'strfmt("{answer}", outkey="answer")', + combine_and_keep_eval(text_len, keep=('answer', 'issimple')), + ]) + + for freq, name, split in [ + (0.1, 'minitrain', 'train[:5%]'), + # (0.1, 'minival', 'train[-5%:]'), + (1/4, 'eval', 'test'), + ]: + c.evals[f'tallyqa/{name}'] = dict( + type='proj.paligemma.transfers.tallyqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + data={**training_data(res, text_len).data, 'split': split}, + log_percent=freq, tokenizer=TOKENIZER, pp_fn=tallyqa_pp_eval) + c.evals[f'tallyqa/{name}'].update(kw) + + # CountBenchQA eval. We use the TallyQA eval for this but just pass in + # different data. + c.evals['countbenchqa/eval'] = dict( + type='proj.paligemma.transfers.tallyqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + data=countbenchqa_eval_data(res, text_len).data, + log_percent=0.1, # This is a very small and cheap eval set. + tokenizer=TOKENIZER, + pp_fn=countbenchqa_eval_data(res, text_len).pp) + c.evals['countbenchqa/eval'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + # ('minival', 'train[-5%:]'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + c.evals[f'tallyqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.1, # Eval ~10x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): # pylint: disable=unused-argument + """Train with best hyper-params.""" + add(total_epochs=2, lr=1e-5, wd=0.00, **bvcc.arg(res=224)) + add(total_epochs=2, lr=1e-5, wd=1e-6, **bvcc.arg(res=448)) + add(total_epochs=2, lr=7e-6, wd=7e-7, **bvcc.arg(res=896)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224) + + c.input = training_data(c.res) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 2 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + """Returns a list of metric names.""" + return [ + 'training_loss', + 'countbenchqa/eval/acc', + 'tallyqa/minitrain/pplx/avg', + 'tallyqa/eval/pplx/avg', + 'tallyqa/eval/acc', + 'tallyqa/eval/acc/complex', + 'tallyqa/eval/acc/simple', + ] diff --git a/big_vision/configs/proj/paligemma/transfers/textcaps.py b/big_vision/configs/proj/paligemma/transfers/textcaps.py new file mode 100644 index 0000000000000000000000000000000000000000..7249df806010fe0fd0ec80697df44a9f1816c5b4 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/textcaps.py @@ -0,0 +1,181 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to TextCaps captioning task. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=32, crop='rs'): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train data or train[:98%]. + text_len: sequence length. + crop: one of {'ic', 'rc', 'rs'}. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='text_caps', + split='train' if final_split else 'train[:98%]', + ) + + if crop == 'ic': + crop = f'inception_crop({res}, area_min=50)' + elif crop == 'rc': + crop = f'resize_small({res*8//7})|random_crop({res})' + elif crop == 'rs': + crop = f'resize({res})' + else: + raise ValueError(f'Unknown crop: {crop}') + + c.pp = '|'.join([ + 'flatten', + 'decode', crop, 'value_range(-1, 1)', + 'choice_no_replacement(inkey="texts", outkey="suffix")', + 'strfmt("caption en", outkey="prefix")', + 'lower(key="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + # Input eval pp without ground truth text and random crop. + pp_eval = '|'.join([ + 'decode', f'resize({res})', 'value_range(-1, 1)', + 'flatten', 'copy("texts", "captions")', # GT for evaluator. + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + for name, split in [ + ('minitrain', 'train[:2%]'), + ('minival', 'train[-2%:]'), + ('eval', 'val'), + ]: + c.evals[f'textcaps/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + log_percent=0.1, tokenizer=TOKENIZER, + data={'name': 'text_caps', 'split': split}, + pp_fn=pp_eval, + ) + c.evals[f'textcaps/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len, crop='rs') # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:2%]'), + ('minival', 'train[-2%:]'), + ('eval', 'val'), + ]: + c.evals[f'textcaps/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', crop='rs', res=224, beam_size=3, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split, crop=c.crop) + + c.total_epochs = 5 # Note each example has 5 captions. + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 1e-5 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval_pplx(c, c.res) + + if c.beam_size: + decode_kw = {'pred': 'beam_decode', 'pred_kw': {'beam_size': c.beam_size}} + else: + decode_kw = {} + + add_eval(c, c.res, batch_size=1024, **decode_kw) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Note: wd=0.0 probably works as good. + add(lr=1e-5, wd=1e-6, total_epochs=5, **bvcc.arg(res=224, **c)) + add(lr=1e-5, wd=1e-6, total_epochs=5, **bvcc.arg(res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(('epoch', f'textcaps/{split}/cider')) + m.append(('epoch', f'textcaps/{split}/pplx/avg')) + return m diff --git a/big_vision/configs/proj/paligemma/transfers/textvqa.py b/big_vision/configs/proj/paligemma/transfers/textvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0ddbea6e01221a390012f362da7e9375329bfb7f --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/textvqa.py @@ -0,0 +1,163 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to TextVQA. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+val data. + text_len: sequence length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='textvqa', + split='train+val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """TextVQA evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + # TextVQA does not have a "answer_type", but if we use an answer_type + # that is used in VQAv2, we can just reuse that evaluator. Use "other". + 'strfmt("other", outkey="answer_type")', + combine_and_keep_eval(text_len, keep=('answers', 'answer_type', 'question_id')), + ]) + + for freq, name, split in [ + (1/8, 'minitrain', 'train[:5120]'), # To gauge memorization. + (1/8, 'eval', 'val'), # To tune hparams. Only 5k samples in val. + (1.0, 'test', 'test'), + ]: + c.evals[f'textvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqav2', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/textvqa_{name}.json', + data={**training_data(res, final_split=True, text_len=text_len).data, + 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'textvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('eval', 'val'), # To tune hparams. + ]: + c.evals[f'textvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=3e-6, wd=0, total_epochs=5, **bvcc.arg(res=224, **c)) + add(lr=3e-6, wd=3e-7, total_epochs=10, **bvcc.arg(res=448, **c)) + add(lr=3e-6, wd=0, total_epochs=10, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 5 # For 448, 10 epochs seems to work better. + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 0.0 # wd doesn't seem to matter much, sometimes 0.1*lr is a bit better. + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=256) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('eval', 'minitrain'): + m.append(f'textvqa/{split}/acc') + m.append(f'textvqa/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/vatex_cap.py b/big_vision/configs/proj/paligemma/transfers/vatex_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..78ae9f66da6bb2b4db2a6d3039b3d8f174f8de62 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/vatex_cap.py @@ -0,0 +1,210 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to VATEX Video captioning. + +IMPORTANT: This config is based on an unreleased version of DeepMind Video +Readers (DMVR). Users can either set up DMVR using the open source code from +GitHub (see below for details), or add their own data loader of choice. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + +TEXT_LEN = 64 +DATASET_NAME = 'vatex' +# Numbers might need to be updated due to wipeout. Current from 2024-04-28 +SPLIT_SIZE = {'train': 22315, 'valid': 2584, 'test': 5135} + + +def training_data(res, *, final_split, num_frames=8, stride=None): + """Creates training data config. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+valid data. + num_frames: number of sampled frames per video. + stride: stride at which the frames are sampled. + + Returns: + The ConfigDict for the input section. + """ + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + # pick one caption at random during training + 'strfmt("caption en", outkey="prefix")', + 'video_choice(inkey="caption/string", outkey="suffix")', + combine_and_keep_train(TEXT_LEN), + ]) + + c = bvcc.parse_arg('') + c.data = {} + splits = ['train', 'valid'] if final_split else ['train'] + raise NotImplementedError('Please implement a video reader of choice!') + # For example DMVR https://github.com/google-deepmind/dmvr + # The reader should support the following arguments: + # - name: Name of the reader. + # - dataset_name: Name of the data set. + # - split: Data set split. + # - num_frames: Number of frames sampled from the video. + # - stride: Stride at which the video frames are sampled. + # - deterministic_fs: Whether to sample the frames starting at the first + # frame or whether an offest should be chosen at random (if there are more + # frames than num_frames * stride) + # - first_k_shards: Whether to only use the first k shards of the data + # (optional but useful for speeding up intermediate evaluations). + for split in splits: + c.data[split] = SPLIT_SIZE[split] + c[split] = {'pp': pp} + c[split].data = dict( + # PLEASE ADD YOUR READER HERE: + name='', + dataset_name=DATASET_NAME, split=split, + num_frames=num_frames, stride=stride, + deterministic_fs=False) + return c + + +def add_eval(c, res, num_frames=8, stride=None): + """Captioning evaluator.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + pp = '|'.join([ + # prepare the frames by decoding, resizing, replicating, sampling: + f'video_decode({res})|video_replicate_img({num_frames},{num_frames})', + f'video_ensure_shape("image", {(num_frames, res, res, 3)})', + 'strfmt("caption en", outkey="prefix")', + 'copy("example/video_id", "image/id")', + 'copy("caption/string", "captions")', + combine_and_keep_eval(TEXT_LEN, keep=('image/id', 'captions')), + ]) + + for freq, name, split, first_k_shards, skip_first_eval in [ + (1/8, 'minitrain', 'train', 2, False), # To gauge memorization. + (1/4, 'minival', 'valid', 2, False), # To monitor val progress. + (1, 'val', 'valid', None, False), # To tune hparams. + (1, 'eval', 'test', None, False), # final metric + ]: + c.evals[f'{DATASET_NAME}/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': TEXT_LEN}, + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + log_percent=freq, tokenizer=TOKENIZER, + pp_fn=pp, skip_first=skip_first_eval) + + +def add_eval_pplx(c, res, num_frames=8, stride=None): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, num_frames=num_frames, stride=stride) + + for name, split, first_k_shards in [ + ('minitrain', 'train', 2), # To gauge memorization. + ]: + c.evals[f'{DATASET_NAME}/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, # Not too cheap, do 10x per run. + data={**c_train.train.data, 'split': split, + 'first_k_shards': first_k_shards, + 'deterministic_fs': True}, + pp_fn=c_train.train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(lr=3e-6, wd=3e-7, total_epochs=10, **bvcc.arg(res=224, **c)) + + +sweep = sweep_best + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', num_frames=16, stride=7, res=224, + freeze_vit=False, freeze_llm=False, final_split=False) + + c.input = training_data( + c.res, final_split=c.final_split, + num_frames=c.num_frames, stride=c.stride) + + c.total_epochs = 3 + c.input.batch_size = 128 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, c.num_frames, c.stride) + add_eval_pplx(c, c.res, c.num_frames, c.stride) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = 10_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops', + 'proj.paligemma.video'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + for split in c.input.data.keys(): + c.input[split].shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.first_k_shards = 1 + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minitrain', 'minival', 'val', 'eval'): + m.append((f'{DATASET_NAME}/{split}/cider')) + for split in ('minitrain', 'minival'): + m.append((f'{DATASET_NAME}/{split}/pplx/avg')) + return m + diff --git a/big_vision/configs/proj/paligemma/transfers/vertexai_l4.py b/big_vision/configs/proj/paligemma/transfers/vertexai_l4.py new file mode 100644 index 0000000000000000000000000000000000000000..503f2eebf5d9f0ff0e4c24b2ad343d519bb0fc95 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/vertexai_l4.py @@ -0,0 +1,115 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to a task stored in JSON-L, designed to fit on an L4 GPU. +""" + +import big_vision.configs.common as bvcc + + +def training_data(res, text_len): + """Creates training data config.""" + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='bv:jsonl', + fname='gs://longcap100/data_train90.jsonl', + fopen_keys={'image': 'gs://longcap100/'}, + # See docstring in datasets/jsonl.py for further details. + # download_keys=['image'], # If jsonl contains external paths. + ) + c.pp = '|'.join([ + # Read and prepare the image by just resizing it: + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + # The texts are already prepared in `prefix` and `suffix` keys. + 'strfmt("caption en", outkey="prefix")', + combine_and_keep(text_len), + ]) + # Keep the whole dataset in RAM after first pass. Useful optimization for + # small/mid-size datasets, but risks a host OOM for large datasets. + c.cache_raw = True + return c + + +def get_config(arg=None): + """Config for training.""" + # You probably do NOT want to add settings here. The `arg` way of settings is + # really only for things you'd want to sweep and which affect MULTIPLE config + # settings at once or go into the pp string. + c = bvcc.parse_arg(arg, res=224, text_len=128, batch_size=4, + freeze_vit=False, freeze_llm=False) + + c.input = training_data(c.res, c.text_len) + + # These settings are suited for fitting in a single L4. + c.total_epochs = 1 + c.input.batch_size = c.batch_size + c.optax_name = 'big_vision.sgd' # Without momentum, so really low-memory. + c.lr = 0.1 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + + # Learning-rate schedule. Probably is fine like this. + sched = dict(decay_type='cosine', warmup_percent=0.05) + c.schedule = [ + ('img/.*', None if c.freeze_vit else sched), + ('llm/.*', None if c.freeze_llm else sched), + ] + + c.evals = {} + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + # TODO: b/lbeyer - no scan and no remat might be better on 1-GPU machines? + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + c.input.shuffle_buffer_size = 1000 + c.log_training_steps = 1 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + c.seed = 0 + return c + + +def tok(**kw): + """Creates the tokenization preprocessing string.""" + # Single entry point so that it's consistent everywhere and easier to switch. + kw.setdefault('model', 'gemma(tokensets=("loc", "seg"))') + kw = ', '.join(f'{k}={repr(v)}' for k, v in kw.items()) + return f'tok({kw})' + + +def combine_and_keep(text_len): + return '|'.join([ + tok(key='prefix', bos='yes'), + tok(key='suffix', eos='yes'), + tok(key='septok', text='\n'), + # If masks confuse you, see (internal link) + 'masked_concat(["prefix", "septok", "suffix"], mask_ar=[0, 0, 1], mask_loss=[0, 0, 1])', + # For training, we +1 because the trainer removes EOS. + f'tolen({text_len+1}, pad_value=0, key="text")', # For text, value doesn't matter. + f'tolen({text_len+1}, pad_value=1, key="mask_ar")', + f'tolen({text_len+1}, pad_value=0, key="mask_loss")', + 'keep("image", "text", "mask_ar", "mask_loss")', + ]) diff --git a/big_vision/configs/proj/paligemma/transfers/vizwizvqa.py b/big_vision/configs/proj/paligemma/transfers/vizwizvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..58e361b61cf120ab329384e8cd7a1273b921e5ee --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/vizwizvqa.py @@ -0,0 +1,160 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to VQAv2. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=48): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224) + final_split: Train on combined train+val + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='vizwizvqa', + split='train+val' if final_split else 'train', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=48, **kw): + """VQAv2 evaluators.""" + pp = '|'.join([ + f'decode|resize({res})|value_range(-1, 1)', + 'strfmt("answer en {question}", outkey="prefix")', + 'copy("image/filename", "question_id")', + combine_and_keep_eval(text_len, keep=('answers', 'question_id')), + ]) + + for freq, name, split in [ + (1/8, 'minitrain', 'train[:5120]'), # To gauge memorization. 400s on 32v2 + (0.1, 'minival', 'val'), # To tune hparams. 4k samples, full eval is fine. + (1.0, 'test', 'test'), # For the test-server. SLOW. + ]: + c.evals[f'vizwizvqa/{name}'] = dict( + type='proj.paligemma.transfers.vqa', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/vizwiz_{name}.json', + out_question_key='image', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'vizwizvqa/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=48): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train'), # To gauge memorization. + ('minival', 'val'), # To tune hparams + ]: + c.evals[f'vizwizvqa/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/8, + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + add(**bvcc.arg(res=224, **c)) + add(**bvcc.arg(res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 0.00001 + c.wd = 0.0 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 25_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'minitrain'): + m.append(f'vizwizvqa/{split}/acc') + m.append(f'vizwizvqa/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/vqav2.py b/big_vision/configs/proj/paligemma/transfers/vqav2.py new file mode 100644 index 0000000000000000000000000000000000000000..0c7c00c7eef5b66664933600e240e2b556b9cd0b --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/vqav2.py @@ -0,0 +1,160 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to VQAv2. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Whether to use all of the validation data. + text_len: sequence length + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='vqa', + split='train + validation' if final_split else 'train + validation[:-10240]', + ) + c.pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question_text}", outkey="prefix")', + 'choice_no_replacement(inkey="answers", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """VQAv2 evaluators.""" + pp = '|'.join([ + f'decode|resize({res}, antialias=True)|value_range(-1, 1)', + 'strfmt("answer en {question_text}", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('answers', 'answer_type', 'question_type', 'question_id')), + ]) + + for freq, name, split in [ + (1/4, 'minitrain', 'train[:5120]'), # To gauge memorization. + (1/4, 'minival', 'validation[-10240:]'), # To tune hparams. + # To generate final predictions. Test sets combined since 2021 challenge. + (1.0, 'test', 'test + test-dev'), + ]: + c.evals[f'vqav2/{name}'] = dict( + type='proj.paligemma.transfers.vqav2', + pred='decode', pred_kw={'max_decode_len': text_len}, + outfile=f'{{workdir}}/vqav2_{name}.json', + data={**training_data(res, True, text_len).data, 'split': split}, + log_percent=freq, skip_first=freq == 1, tokenizer=TOKENIZER, pp_fn=pp) + c.evals[f'vqav2/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, True, text_len) # Use mostly same settings as training. + + for name, split in [ + ('minitrain', 'train[:20_864]'), # To gauge memorization. + ('minival', 'validation[-10240:]'), # To tune hparams. + ]: + c.evals[f'vqav2/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=1/4, # Not too cheap, do 4x per run. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # NOTE: lr was highest in sweep. + add(total_epochs=10, lr=1e-5, wd=1e-6, **bvcc.arg(res=224, **c)) + add(total_epochs=10, lr=1e-5, wd=0.00, **bvcc.arg(res=448, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 10 + c.input.batch_size = 256 + c.optax_name = 'scale_by_adam' + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.0 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.0) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops'] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + m = ['training_loss'] + for split in ('minival', 'minitrain'): + m.append(f'vqav2/{split}/acc') + m.append(f'vqav2/{split}/pplx/avg') + return m diff --git a/big_vision/configs/proj/paligemma/transfers/widgetcap.py b/big_vision/configs/proj/paligemma/transfers/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..d2dbfc4fcac63589ce2481a4331d9c403059f182 --- /dev/null +++ b/big_vision/configs/proj/paligemma/transfers/widgetcap.py @@ -0,0 +1,180 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""PaliGemma transfer to widgetcap (bbox drawn in the picture). +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.proj.paligemma.transfers.common import combine_and_keep_train, combine_and_keep_eval, TOKENIZER + + +def training_data(res, *, final_split, text_len=32): + """Creates training data config. + + See (internal link) + You can add more arguments beside `res`, but give them good defaults. + + Args: + res: The requested image resolution (eg 224). + final_split: Train on all train+dev data. + text_len: The max text length. + + Returns: + The ConfigDict for the input section. + """ + c = bvcc.parse_arg('') # Just make a configdict without extra import. + c.data = dict( + name='widgetcap', + split='train+dev' if final_split else 'train', + ) + c.pp = '|'.join([ + 'decode', + f'resize({res}, antialias=True)', + 'draw_bbox', + 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + 'choice_no_replacement(inkey="texts", outkey="suffix")', + combine_and_keep_train(text_len), + ]) + return c + + +def add_eval(c, res, text_len=32, **kw): + """Captioning evaluator with cider/bleu-4/meteor/rouge/spice metrics.""" + # Input eval pp without ground truth text and random crop. + pp_eval = '|'.join([ + 'copy("texts", "captions")', # GT for evaluator. + 'decode', + f'resize({res}, antialias=True)', + 'draw_bbox', + 'value_range(-1, 1)', + 'strfmt("caption en", outkey="prefix")', + combine_and_keep_eval(text_len, keep=('image/id', 'captions')), + ]) + + for name, split in [ + ('val', 'dev'), + ('eval', 'test'), + ]: + c.evals[f'widgetcap/{name}'] = dict( + type='proj.paligemma.transfers.coco_caption', + pred='decode', pred_kw={'max_decode_len': text_len}, + data=dict( + name='widgetcap', + split=split, + ), + log_percent=0.1, tokenizer=TOKENIZER, pp_fn=pp_eval) + c.evals[f'widgetcap/{name}'].update(kw) + + +def add_eval_pplx(c, res, text_len=32): + """Perplexity evaluator to test runs before implementing the real deal.""" + c_train = training_data(res, final_split=True, text_len=text_len) # Use mostly same settings as training. + for name, split in [ + ('minitrain', 'train[:5%]'), # To gauge memorization. + ('minival', 'dev'), # To tune hparams. + ('eval', 'test'), # To compute final publishable scores. + ]: + c.evals[f'widgetcap/{name}/pplx'] = dict( + type='proj.paligemma.perplexity', pred='logits', + key='text', shift_labels=True, + log_percent=0.05, # Eval ~20x per run; it's cheap. + data={**c_train.data, 'split': split}, + pp_fn=c_train.pp, + ) + + +def sweep_best(add, arg=None): + """Train with best hyper-params.""" + c = bvcc.parse_arg(arg, final_split=False) + # Based on sweeps (internal link) (widgetcap/val/cider). + # NOTE: dropout always on, see get_config. + add(lr=3e-6, wd=3e-7, total_epochs=4, **bvcc.arg(res=224, **c)) + add(lr=3e-6, wd=3e-7, total_epochs=4, **bvcc.arg(res=448, **c)) + # Not better: add(lr=3e-6, wd=3e-7, total_epochs=4, **bvcc.arg(res=896, **c)) + + +sweep = sweep_best # Choose which sweep to run. + + +def get_config(arg=None): + """Config for training.""" + c = bvcc.parse_arg(arg, mode='xm', res=224, final_split=False) + + c.input = training_data(c.res, final_split=c.final_split) + + # Instead of epochs, you can also use `total_examples` or `total_steps`. + c.total_epochs = 4 + c.input.batch_size = 64 + c.optax_name = 'scale_by_adam' + c.optax = dict(b2=0.999) + c.lr = 3e-6 + c.wd = 3e-7 + c.grad_clip_norm = 1.0 + c.label_smoothing = 0.1 + c.schedule = dict(decay_type='cosine', warmup_percent=0.05) + + # Add evaluators. + c.evals = {} + add_eval(c, c.res, batch_size=1024) + add_eval_pplx(c, c.res) + + # Model section. + c.model_name = 'proj.paligemma.paligemma' + c.model = {} + c.model.img = dict(variant='So400m/14', pool_type='none', scan=True) + c.model.llm = dict(vocab_size=256_000 + 1024 + 128, dropout=0.1) + c.model_init = f'pt_{c.res}' + + # FSDP strategy. + c.mesh = [('data', -1)] + c.sharding_strategy = [('.*', 'fsdp(axis="data")')] + c.sharding_rules = [('act_batch', ('data',))] + + # These probably do not need any change/tuning + c.input.shuffle_buffer_size = 50_000 + c.log_training_steps = 50 + c.ckpt_steps = 1_000 + c.pp_modules = [ + 'ops_general', + 'ops_image', + 'ops_text', + 'proj.paligemma.ops', + 'proj.paligemma.widgetcap', + ] + + # Update configs for quicker local runs and avoid swapping. + if c.mode in ('runlocal', 'mock'): + c.input.shuffle_buffer_size = None + for ev in c.evals.values(): + ev.data.split = ev.data.split.split('[')[0] + '[:16]' + + if c.mode == 'runlocal': + c.log_training_steps = 1 + c.input.batch_size = 2 + + c.seed = 0 + return c + + +def metrics(arg=None): # pylint: disable=unused-argument + # This function defines the default flatboard. If you want, it can be a lot + # fancier too, but the simplest way is a list of metric names. + m = ['training_loss'] + for split in ('eval', 'minival', 'minitrain'): + m.append(f'widgetcap/{split}/pplx/avg') + for split in ('val', 'eval'): + m.append(f'widgetcap/{split}/cider') + return m diff --git a/big_vision/configs/proj/reward_tune/detection_reward.py b/big_vision/configs/proj/reward_tune/detection_reward.py new file mode 100644 index 0000000000000000000000000000000000000000..73e96c2b75e90ea74a5ffff40187e66a00dd8b4b --- /dev/null +++ b/big_vision/configs/proj/reward_tune/detection_reward.py @@ -0,0 +1,232 @@ +# Copyright 2024 Big Vision 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. + +"""Object detection reward from "Tuning computer vision models with task rewards" (https://arxiv.org/abs/2302.08242). + +The `reward_fn` computes the reward for a batch of predictions and ground truth +annotations. When using it to optimize a model that outputs a prediction as a +sequence of tokens like [y0, x0, Y0, X0, class0, confidence0, y1, x1, Y1, ...] +the training loop may look like: + +``` +# Settings used in the paper. +config.max_level = 1000 # Coordinates are discretized into 1000 buckets. +config.max_conf = 2 # Two tokens are reserved to represent confidence. +config.num_cls = 80 # Number of classes in COCO. +config.nms_w = 0.3 # Weight for duplicate instances. +config.cls_smooth = 0.05 # Adjust the classes weights based on their frequency. +config.reward_thr = (0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9, 0.95) +config.correct_thr = 0.5 # Learn the IoU when matching with threshold=0.5. +config.conf_w = 0.3 # Weight for the confidence loss. + + +# 1) Sample N outputs for each input and compute rewards, use one sample to +# optimize and others to compute a reward baseline. +sample_seqs = sample_fn(params, images, num_samples) +sample_rewards, aux = reward_fn(sample_seqs, labels, config) +labels = sample_seqs[:, 0, ...] +rewards = sample_rewards[:, 0] +match_iou = aux["match_iou"][:, 0] +baselines = (jnp.sum(sample_rewards, axis=-1) - rewards) / (num_samples - 1) + +# 2) Optimizize the model. By using REINFORCE to adjust the likelihood of the +# sequence based on the reward and with supervision to teach the model to +# predict the expected IoU of each box in its own samples. +def loss_fn(params): + logits = model.apply(params, images, labels, train=True, rngs=rngs) + logits_softmax = jax.nn.log_softmax(logits) + + # Use reinforce to optimize the expected reward for the whole sequence. + seq_rewards = (rewards - baselines) + # Note: consider improve this code to skip this loss for confidence tokens. + # The paper did not do it due to a bug (and also does not seem to matter). + target = jax.nn.one_hot(labels, logits.shape[-1]) * seq_rewards[:, None, None] + loss_reward = -jnp.sum(target * logits_softmax, axis=-1) + + # Use supervision loss to tune the confidence tokens to predict IoU: + # - (1.0, 0.0, 0.0, ...) -> for padded boxes. + # - (0.0, 1-iou, iou, ...) -> for sampled boxes. + conf0 = (labels[:, 5::6] == 0) + conf1 = (labels[:, 5::6] > 0) * (1.0 - match_iou) + conf2 = (labels[:, 5::6] > 0) * match_iou + target_conf = jnp.stack([conf0, conf1, conf2], axis=-1) + logits_conf = logits_softmax[:, 5::6, :3] + loss_conf = -jnp.sum(target_conf * logits_conf, axis=-1) + + loss = jnp.mean(loss_reward) + config.conf_w * jnp.mean(loss_conf) + return loss +``` +""" +import functools + +import einops +import jax +import jax.numpy as jnp + + +# Frequency of COCO object detection classes as observed in the training set. +# pylint: disable=bad-whitespace,bad-continuation +CLS_COUNTS = [ + 262465, 7113, 43867, 8725, 5135, 6069, 4571, 9973, 10759, + 12884, 1865, 1983, 1285, 9838, 10806, 4768, 5508, 6587, + 9509, 8147, 5513, 1294, 5303, 5131, 8720, 11431, 12354, + 6496, 6192, 2682, 6646, 2685, 6347, 9076, 3276, 3747, + 5543, 6126, 4812, 24342, 7913, 20650, 5479, 7770, 6165, + 14358, 9458, 5851, 4373, 6399, 7308, 7852, 2918, 5821, + 7179, 6353, 38491, 5779, 8652, 4192, 15714, 4157, 5805, + 4970, 2262, 5703, 2855, 6434, 1673, 3334, 225, 5610, + 2637, 24715, 6334, 6613, 1481, 4793, 198, 1954 +] +# pylint: enable=bad-whitespace,bad-continuation + + +def seq2box(seq, max_level, max_conf, num_cls): + """Extract boxes encoded as sequences.""" + # Reshape to instances of boxes + dim_per_box = 6 + seq_len = seq.shape[-1] + seq = seq[..., :(seq_len - seq_len % dim_per_box)] + seq = einops.rearrange(seq, "... (n d) -> ... n d", d=dim_per_box) + + # Unpack box fields + boxes, labels, confs = seq[..., 0:4], seq[..., 4], seq[..., 5] + boxes = boxes - max_conf - 1 + labels = labels - max_conf - 1 - max_level - 1 + boxes = jnp.clip(boxes, 0, max_level) / max_level + labels = jnp.clip(labels, 0, num_cls - 1) + confs = jnp.clip(confs, 0, max_conf) + + return boxes, labels, confs + + +def iou_fn(box1, box2): + """Compute IoU of two boxes.""" + ymin1, xmin1, ymax1, xmax1 = box1 + ymin2, xmin2, ymax2, xmax2 = box2 + + a1 = jnp.abs((ymax1 - ymin1) * (xmax1 - xmin1)) + a2 = jnp.abs((ymax2 - ymin2) * (xmax2 - xmin2)) + + yl = jnp.maximum(ymin1, ymin2) + yr = jnp.minimum(ymax1, ymax2) + yi = jnp.maximum(0, yr - yl) + + xl = jnp.maximum(xmin1, xmin2) + xr = jnp.minimum(xmax1, xmax2) + xi = jnp.maximum(0, xr - xl) + + inter = xi * yi + return inter / (a1 + a2 - inter + 1e-9) + +iou_fn_batched = jax.vmap( + jax.vmap(iou_fn, in_axes=(None, 0)), in_axes=(0, None) +) + + +def _reward_fn_thr(seq_pred, seq_gt, + thr, nms_w, max_level, max_conf, num_cls, cls_smooth): + """Compute detection reward function for a given IoU threshold.""" + # Weight matches of each label inversely proportional to the percentage of + # GT instances with such label in the whole train dataset. Additionally + # smooth out the observed distribution. + cls_counts = jnp.array(CLS_COUNTS) + weights = 1.0 / (cls_counts + cls_smooth*jnp.sum(cls_counts)) + weights = num_cls * weights / jnp.sum(weights) + + boxes_pred, labels_pred, confs_pred = seq2box( + seq_pred, max_level, max_conf, num_cls) + boxes_gt, labels_gt, confs_gt = seq2box( + seq_gt, max_level, max_conf, num_cls) + + # Compute IoU matrix: Predictions X GT + iou = iou_fn_batched(boxes_pred, boxes_gt) + + # IoU thr + iou = jnp.where(iou > thr, iou, 0.0) + + # EOS mask + confs_mask = (confs_pred[:, None] > 0) * (confs_gt[None, :] > 0) + iou = confs_mask * iou + + # Label mask + label_mask = labels_pred[:, None] == labels_gt[None, :] + iou = label_mask * iou + + # Each prediction is matched to a single box + single_match_mask = jax.nn.one_hot(jnp.argmax(iou, axis=1), iou.shape[1]) + iou = iou * single_match_mask + + # Pred. boxes indicators + correct = jnp.any(iou > 0.0, axis=1).astype("int32") + 1 + correct = jnp.where(confs_pred > 0, correct, 0) + + # For each GT box find best match + matches_idx = jnp.argmax(iou, axis=0) + matches_iou = jnp.take_along_axis(iou, matches_idx[None], axis=0)[0] + matches_idx = jnp.where(matches_iou > 0.0, matches_idx, -1) + + match_reward = jnp.sum((matches_idx >= 0) * weights[labels_gt][None, :]) + + # Compute duplicate penalty (aka NMS). + matches_mask = jax.nn.one_hot(matches_idx, iou.shape[0], axis=0) + nms_penalty = jnp.sum( + (iou > 0.0) * (1 - matches_mask) * weights[labels_pred][:, None]) + + match_iou = jnp.sum(iou, axis=1) + + return { + "reward": (match_reward - nms_w * nms_penalty), + "num_matches": jnp.sum(matches_idx >= 0), + "nms_penalty": nms_penalty, + "correct": correct, + "match_iou": match_iou, + } + + +def reward_fn(seqs_pred, seqs_gt, config): + """Total reward.""" + result = {} + thrs = config.reward_thr + correct_thr = config.correct_thr + r_keys = ["reward", "num_matches", "nms_penalty"] + for thr in thrs: + fn = functools.partial( + _reward_fn_thr, + thr=thr, + nms_w=config.nms_w, + max_level=config.max_level, + max_conf=config.max_conf, + num_cls=config.num_cls, + cls_smooth=config.cls_smooth, + ) + rewards = jax.vmap(jax.vmap(fn, in_axes=(0, None)))(seqs_pred, seqs_gt) + + result = {**result, **{f"{k}-{thr:0.1f}": rewards[k] + for k in r_keys}} + if thr == correct_thr: + correct = rewards["correct"] + match_iou = rewards["match_iou"] + + result = { + **result, + **{k: jnp.mean( + jnp.array([result[f"{k}-{thr:0.1f}"] for thr in thrs]), axis=0) + for k in r_keys} + } + + return result["reward"], { + "result": result, + "correct": correct, + "match_iou": match_iou, + } diff --git a/big_vision/configs/proj/scaling_laws/train_vit_g.py b/big_vision/configs/proj/scaling_laws/train_vit_g.py new file mode 100644 index 0000000000000000000000000000000000000000..137e51cb629613f473ed7d34584f0117ab308465 --- /dev/null +++ b/big_vision/configs/proj/scaling_laws/train_vit_g.py @@ -0,0 +1,87 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-train ViT-g (1B params) on JFT-3B as in https://arxiv.org/abs/2106.04560 + +To train ViT-G (2B params), simply update the following single line: + `config.model.variant = 'G/14'` + +The code is released for reference purposes. +One can test the code using public ImageNet-1k or ImageNet-21k dataset. + +big_vision.train \ + --config big_vision/configs/proj/scaling_laws/train_vit_g.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` + +""" +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + + +def get_config(): + """Rocket config.""" + config = mlc.ConfigDict() + + config.dataset = 'jft_3b' + config.val_split = 'val' + config.train_split = 'train' + config.num_classes = 29_593 + config.init_head_bias = -10.0 + + # Fits 32 images per TPUv3 core with ViT-g/14. + config.batch_size = 4096*4 + + pp_common = '|value_range(-1, 1)' + pp_common += f'|onehot({config.num_classes})' + pp_common += '|keep("image", "labels")' + config.pp_train = 'inception_crop(224)|flip_lr' + pp_common + config.pp_eval = 'resize_small(256)|central_crop(224)' + pp_common + config.shuffle_buffer_size = 250_000 # Per host, so small-ish is ok. + + config.log_training_steps = 50 + config.log_eval_steps = 1000 + # NOTE: eval is very fast O(seconds) so it's fine to run it often. + + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 10_000 + + config.prefetch_to_device = 1 + config.trial = 0 + + # Model section + config.model_name = 'vit' + config.model = mlc.ConfigDict() + config.model.variant = 'g/14' + config.model.pool_type = 'map' + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.grad_clip_norm = 1.0 + config.lr = 8e-4 + config.wd = 0.03 * 8e-4 + config.wd_mults = [ + ('.*head/kernel', 100.0), + ('.*/kernel', 1.0), + ] + config.schedule = dict( + decay_type='rsqrt', timescale=10_000, warmup_steps=10_000, + cooldown_steps=50_000) + config.total_steps = 1_000_000 + + # Few-shot eval section + config.evals = {} + config.evals.fewshot = dict(log_steps=10_000, **get_fewshot_lsr()) + + return config diff --git a/big_vision/configs/proj/uvim/README.md b/big_vision/configs/proj/uvim/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8a6bfa8313f3608f2e8c725908248783e6bb1140 --- /dev/null +++ b/big_vision/configs/proj/uvim/README.md @@ -0,0 +1,84 @@ +# UViM: A Unified Modeling Approach for Vision with Learned Guiding Codes + +*by Alexander Kolesnikov, André Susano Pinto, Lucas Beyer, Xiaohua Zhai, Jeremiah Harmsen, Neil Houlsby* + +We provide pretrained UViM models from the [original paper](https://arxiv.org/abs/2205.10337), +as well as the instructions on how to reproduce core paper experiments. + +## Pretrained models + +The table below contains UViM models (stage I and II) trained for three +different tasks: panoptic segmentation, colorization and depth prediction. + +| task | model | dataset | accuracy | download link | +| --------------------- | ------------------- | ------------------------------------------------------------------------ | ------------ | ----------------------------------------------------------------------------------------- | +| Panoptic segmentation | UViM Stage I model | [COCO(2017)](https://cocodataset.org/#home) | 75.8 PQ | [link](https://storage.googleapis.com/big_vision/uvim/panoptic_stageI_params.npz) | +| Panoptic segmentation | UViM Stage II model | [COCO(2017)](https://cocodataset.org/#home) | 43.1 PQ | [link](https://storage.googleapis.com/big_vision/uvim/panoptic_stageII_params.npz) | +| Colorization | UViM Stage I model | [ILSVRC-2012](https://www.image-net.org/) | 15.59 FID | [link](https://storage.googleapis.com/big_vision/uvim/color_stageI_params.npz) | +| Colorization | UViM Stage II model | [ILSVRC-2012](https://www.image-net.org/) | 16.99 FID | [link](https://storage.googleapis.com/big_vision/uvim/color_stageII_params.npz) | +| Depth | UViM Stage I model | [NYU Depth V2](https://cs.nyu.edu/~silberman/datasets/nyu_depth_v2.html) | 0.155 RMSE | [link](https://storage.googleapis.com/big_vision/uvim/depth_stageI_params.npz) | +| Depth | UViM Stage II model | [NYU Depth V2](https://cs.nyu.edu/~silberman/datasets/nyu_depth_v2.html) | 0.463 RMSE | [link](https://storage.googleapis.com/big_vision/uvim/depth_stageII_params.npz) | + +All of this models can be interactively explored in our [colabs](configs/proj/uvim). + +## Running on a single-host TPU machine + +Below we provide instructions on how to run UViM training (stage I and +stage II) using a single TPU host with 8 TPU accelerators. These instructions +can be easily adapted to a GPU host and multi-host TPU setup, see the main +`big_vision` [README file](README.md). + +We assume that the user has already created and `ssh`-ed to the TPU host +machine. The next step is to clone `big_vision` repository: +`git clone https://github.com/google-research/big_vision.git`. + +The next steps are to create a python virtual environment and install python +dependencies: +``` +virtualenv bv +source bv/bin/activate +cd big_vision/ +pip3 install --upgrade pip +pip3 install -r big_vision/requirements.txt +pip install "jax[tpu]>=0.2.16" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html +``` + +After this invoke the helper tool to download and prepare data: +`python3 -m big_vision.tools.download_tfds_datasets coco/2017_panoptic nyu_depth_v2`. +For preparing the ImageNet dataset consult the main codebase README. + +> :warning: TPU machines have 100 GB of the disk space. It may not be enough to +> store all training data (though only panoptic or only depth data may fit). +> Consider preparing the data on a seperate machine and then copying it to +> to TPU machine's extra persistent disk or to a Google Cloud Bucket. See +> instructions for [creating an extra persistent disk](https://cloud.google.com/tpu/docs/users-guide-tpu-vm). +> Remember to set the correct data home directory, e.g.`export DISK=/mnt/disk/persist; export TFDS_DATA_DIR=$DISK/tensorflow_datasets`. + +Our panoptic evaluator uses raw variant of the COCO data, so we move it into a +separate folder. Note, `tfds` has already pre-downloaded the panoptic data, +except for one small json file that we fetch manually: +``` +mkdir $DISK/coco_data +cd $DISK/coco_data +mv $TFDS_DATA_DIR/downloads/extracted/ZIP.image.cocod.org_annot_panop_annot_train.zip/annotations/* . +wget https://raw.githubusercontent.com/cocodataset/panopticapi/master/panoptic_coco_categories.json +export COCO_DATA_DIR=$DISK/coco_data +``` + +For FID evaluator, which is used for the colorization model, set the path to the +directory with image id files, e.g. +`export FID_DATA_DIR=/big_vision/evaluators/proj/uvim/coltran_fid_data`. + +As an example, stage I panoptic training can be invoked as (note the `:singlehost` config parameter which will use lightweight configuration suitable for a single host): +``` +python3 -m big_vision.trainers.proj.uvim.vqvae --config big_vision/configs/proj/uvim/vqvae_coco_panoptic.py:singlehost --workdir workdirs/`date '+%m-%d_%H%M'` +``` +or stage II training +``` +python3 -m big_vision.trainers.proj.uvim.train --config big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py:singlehost --workdir workdirs/`date '+%m-%d_%H%M'` +``` + +## Acknowledgments +The sampling code in `models/proj/uvim/decode.py` module is based on contributions +from Anselm Levskaya, Ilya Tolstikhin and Maxim Neumann. + diff --git a/big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py b/big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..aa68eb7994115d06cf7b148fba62b89f20e6860a --- /dev/null +++ b/big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py @@ -0,0 +1,164 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a UViM stage II model for the panoptic task. + +This config is expected to reproduce the paper's result and achieve +approximately 43.7 PQ points on the COCO holdout data. + +We also provide a low-resource variant of this config, which can be enabled +by adding `:singlehost` postfix to the config name. This one is expected to +achieve 39.4 PQ points on the COCO holdout data. +""" + +import big_vision.configs.common as bvcc +from ml_collections import ConfigDict + +VTT_MODELS = { + 'base': dict(num_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768), + 'large': dict(num_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024), +} + +VQVAE_MODELS = { + 'base': dict(enc_depth=6, dec_depth=12, num_heads=12, mlp_dim=3072, width=768), +} + +RES = 512 +PATCH_SIZE = 16 +LABEL_RES = 512 +LABEL_PATCH_SIZE = 16 + + +def get_config(arg=''): + """Config for training.""" + arg = bvcc.parse_arg(arg, runlocal=False, singlehost=False) + config = ConfigDict() + + config.input = {} + config.input.pp = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({LABEL_RES}, inkey="image", outkey="image_ctx")|' + f'resize({RES})|resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1, 1, key="image_ctx")|' + f'value_range(-1, 1)|make_canonical|keep("image","image_ctx","labels")' + ) + pp_eval = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'resize({LABEL_RES}, inkey="image", outkey="image_ctx")|' + f'resize({RES})|resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1, 1, key="image_ctx")|' + f'value_range(-1, 1)|make_canonical|keep("image","image_ctx","labels")' + ) + pp_predict = ( + f'resize({LABEL_RES}, inkey="image", outkey="image_ctx")|resize({RES})|' + f'value_range(-1, 1, key="image_ctx")|value_range(-1, 1)|' + f'keep("image","image_ctx","image/id")' # image/id used for rng seeds. + ) + + config.input.data = dict(name='coco/2017_panoptic', split='train[4096:]') + config.input.batch_size = 512 + config.input.shuffle_buffer_size = 50_000 + + config.total_epochs = 200 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 5000 + config.prefetch_to_device = 2 + config.seed = 0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.lr = 0.001 + config.wd = 0.000001 + config.lr_mults = [ + ('pos_embedding_encoder.*', 0.1), + ('EmbedPatches.*', 0.1), + ('encoder.*', 0.1), + ('decoder.*', 1.0) + ] + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + + # Oracle section + config.oracle = ConfigDict() + config.oracle.task = 'proj.uvim.panoptic_task' + config.oracle.model_init = 'gs://big_vision/uvim/panoptic_stageI_params.npz' + config.oracle.model_name = 'proj.uvim.vit' + config.oracle.model = ConfigDict(VQVAE_MODELS['base']) + config.oracle.model.input_size = (LABEL_RES, LABEL_RES) + config.oracle.model.patch_size = (LABEL_PATCH_SIZE, LABEL_PATCH_SIZE) + config.oracle.model.code_len = 256 + config.oracle.model.dict_size = 4096 + config.oracle.model.codeword_dim = 768 + config.oracle.model.with_encoder_ctx = True + config.oracle.model.with_decoder_ctx = True + config.oracle.model.code_dropout = 'random' + config.oracle.model.bottleneck_resize = True + config.oracle.model.inputs = { + 'semantics': (133 + 1, LABEL_PATCH_SIZE**2), # +1 for void label + 'instances': (100, LABEL_PATCH_SIZE**2), # COCO: actually 98 train/78 validation. + } + config.oracle.model.outputs = config.oracle.model.inputs + + # Model section + config.model_name = 'proj.uvim.vtt' + # config.model_init = {'encoder': 'howto-i21k-B/8'} + config.model_init = {'encoder': 'howto-i21k-L/16'} + config.model = ConfigDict(VTT_MODELS['large']) + config.model.patches = ConfigDict({'size': (PATCH_SIZE, PATCH_SIZE)}) + config.model.vocab_size = config.oracle.model.get_ref('dict_size') + 1 + config.model.posemb_type = 'learn' + config.model.input_size = (RES, RES) + config.model.seq_len = config.oracle.model.get_ref('code_len') + + # Evaluation section + config.evals = {} + config.evals.val = ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = dict(name=config.input.data.name, split='train[:4096]') + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 1000 + + base = { + 'type': 'proj.uvim.coco_panoptic', + 'pp_fn': pp_predict, + 'log_steps': 10_000, + # Filters objects that occupy less than 0.03^2 fraction of all pixels. + # 'predict_kwargs': {'min_fraction': 0.03 ** 2}, + } + config.evals.coco_panoptic_train = dict(**base, split='train[4096:8192]') + config.evals.coco_panoptic_holdout = dict(**base, split='train[:4096]') + config.evals.coco_panoptic = dict(**base, split='validation') + + # config.evals.save_pred = dict(type='proj.uvim.save_predictions') + # config.evals.save_pred.pp = pp_eval.replace('decode|', '') + # config.evals.save_pred.log_steps = 100_000 + # config.evals.save_pred.dataset = config.dataset + # config.evals.save_pred.split = 'validation[:1024]' + # config.evals.save_pred.outfile = 'inference.npz' + + if arg.singlehost: + config.input.batch_size = 32 + config.num_epochs = 50 + elif arg.runlocal: + config.input.batch_size = 4 + config.input.shuffle_buffer_size = 10 + config.evals.val.data.split = 'train[:16]' + return config \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/train_imagenet2012_colorization_pretrained.py b/big_vision/configs/proj/uvim/train_imagenet2012_colorization_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..0245db559e2454584be3dc617401be4cf327df78 --- /dev/null +++ b/big_vision/configs/proj/uvim/train_imagenet2012_colorization_pretrained.py @@ -0,0 +1,161 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a UViM stage II model for the colorization task. +""" + +import big_vision.configs.common as bvcc +from ml_collections import ConfigDict + +VTT_MODELS = { + 'base': dict(num_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768), + 'large': dict(num_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024), +} + +VQVAE_MODELS = { + 'base': dict(enc_depth=6, dec_depth=12, num_heads=12, mlp_dim=3072, width=768), +} + +RES = 512 +PATCH_SIZE = 16 +LABEL_RES = 512 +LABEL_PATCH_SIZE = 16 + + +def get_config(arg=''): + """Config for training.""" + arg = bvcc.parse_arg(arg, runlocal=False, singlehost=False) + config = ConfigDict() + + config.input = {} + config.input.pp = ( + f'decode_jpeg_and_inception_crop({RES})' + f'|flip_lr' + f'|copy(inkey="image", outkey="labels")' + f'|resize({LABEL_RES},inkey="labels",outkey="labels",method="nearest")' + f'|value_range(-1,1,key="labels")' + f'|rgb_to_grayscale_to_rgb(inkey="image",outkey="image")' + f'|value_range(-1,1,key="image")' + f'|copy(inkey="image", outkey="image_ctx")' + f'|resize({LABEL_RES},inkey="image_ctx",outkey="image_ctx")' + f'|keep("image","image_ctx","labels")') + pp_eval = ( + f'decode' + f'|resize({RES})' + f'|copy(inkey="image", outkey="labels")' + f'|resize({LABEL_RES},inkey="labels",outkey="labels",method="nearest")' + f'|value_range(-1,1,key="labels")' + f'|rgb_to_grayscale_to_rgb(inkey="image",outkey="image")' + f'|value_range(-1,1,key="image")' + f'|copy(inkey="image", outkey="image_ctx")' + f'|resize({LABEL_RES},inkey="image_ctx",outkey="image_ctx")' + f'|strong_hash(inkey="tfds_id", outkey="image/id")' + f'|keep("image","image_ctx","labels","image/id")') + + config.input.data = dict(name='imagenet2012', split='train[4096:]') + config.input.batch_size = 512 + config.input.shuffle_buffer_size = 50_000 + + config.total_epochs = 50 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 5000 + config.prefetch_to_device = 2 + config.seed = 0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.lr = 0.001 + config.wd = 0.000001 + config.lr_mults = [ + ('pos_embedding_encoder.*', 0.1), + ('EmbedPatches.*', 0.1), + ('encoder.*', 0.1), + ('decoder.*', 1.0) + ] + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + + # Oracle section + config.oracle = ConfigDict() + config.oracle.task = 'proj.uvim.colorization_task' + config.oracle.model_init = 'gs://big_vision/uvim/color_stageI_params.npz' + config.oracle.model_name = 'proj.uvim.vit' + config.oracle.model = ConfigDict(VQVAE_MODELS['base']) + config.oracle.model.input_size = (LABEL_RES, LABEL_RES) + config.oracle.model.patch_size = (LABEL_PATCH_SIZE, LABEL_PATCH_SIZE) + config.oracle.model.code_len = 256 + config.oracle.model.dict_size = 4096 + config.oracle.model.codeword_dim = 768 + config.oracle.model.with_encoder_ctx = True + config.oracle.model.with_decoder_ctx = True + config.oracle.model.code_dropout = 'random' + config.oracle.model.bottleneck_resize = True + config.oracle.model.inputs = { + 'color': (3, LABEL_PATCH_SIZE**2), + } + config.oracle.model.outputs = config.oracle.model.inputs + + # Model section + config.model_name = 'proj.uvim.vtt' + # config.model_init = {'encoder': 'howto-i21k-B/8'} + config.model_init = {'encoder': 'howto-i21k-L/16'} + config.model = ConfigDict(VTT_MODELS['large']) + config.model.patches = ConfigDict({'size': (PATCH_SIZE, PATCH_SIZE)}) + config.model.vocab_size = config.oracle.model.get_ref('dict_size') + 1 + config.model.posemb_type = 'learn' + config.model.input_size = (RES, RES) + config.model.seq_len = config.oracle.model.get_ref('code_len') + + # Evaluation section + config.evals = {} + config.evals.val = ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = dict(name=config.input.data.name, split='train[:4096]') + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 1000 + + base = { + 'type': 'proj.uvim.psnr', + 'pp_fn': pp_eval.replace('decode|', ''), + 'log_steps': 10_000, + } + config.evals.psnr_train = dict(**base, split='train[4096:8192]') + config.evals.psnr_holdout = dict(**base, split='train[:4096]') + config.evals.psnr_val = dict(**base, split='validation') + + config.evals.colorization_val_coltran_fid = { + 'type': 'proj.uvim.coltran_fid', + 'log_steps': 100_000, + } + + # config.evals.save_pred = dict(type='proj.uvim.save_predictions') + # config.evals.save_pred.pp_fn = pp_eval.replace('decode|', '') + # config.evals.save_pred.log_steps = 100_000 + # config.evals.save_pred.dataset = config.dataset + # config.evals.save_pred.split = 'validation[:1024]' + # config.evals.save_pred.outfile = 'inference.npz' + + if arg.singlehost: + config.input.batch_size = 32 + config.total_epochs = 20 + elif arg.runlocal: + config.input.batch_size = 8 + config.input.shuffle_buffer_size = 10 + config.evals.val.data.split = 'validation[:256]' + return config \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/train_nyu_depth_pretrained.py b/big_vision/configs/proj/uvim/train_nyu_depth_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..d2e05810708e03ad9e198025a8641eec53811830 --- /dev/null +++ b/big_vision/configs/proj/uvim/train_nyu_depth_pretrained.py @@ -0,0 +1,170 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a UViM stage II model for the depth task. +""" + +import big_vision.configs.common as bvcc +from ml_collections import ConfigDict + + +VTT_MODELS = { + 'base': dict(num_layers=12, num_heads=12, mlp_dim=3072, emb_dim=768), + 'large': dict(num_layers=24, num_heads=16, mlp_dim=4096, emb_dim=1024), +} + +VQVAE_MODELS = { + 'base': dict(enc_depth=6, dec_depth=12, num_heads=12, mlp_dim=3072, width=768), +} + + +RES = 512 +PATCH_SIZE = 16 +LABEL_RES = 512 +LABEL_PATCH_SIZE = 16 +QUANTIZATION_BINS = 256 +# Same as values used in eval, see evaluators/nyu_depth.py. +MIN_DEPTH = 1e-3 +MAX_DEPTH = 10 + + +def get_config(arg='split=final'): + """Config for training.""" + arg = bvcc.parse_arg(arg, split='final', runlocal=False, singlehost=False) + config = ConfigDict() + + config.input = {} + config.input.pp = ( + f'decode|nyu_depth|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({RES})|' + f'resize({LABEL_RES},inkey="image",outkey="image_ctx")|' + f'resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1,1)|' + f'value_range(-1,1,inkey="image_ctx",outkey="image_ctx")|' + f'keep("image","image_ctx","labels")' + ) + pp_eval = ( + f'decode|nyu_depth|' + f'nyu_eval_crop|' + f'resize({RES})|' + f'resize({LABEL_RES},inkey="image",outkey="image_ctx")|' + f'resize({LABEL_RES},key="labels",method="nearest")|' + f'value_range(-1,1)|' + f'value_range(-1,1,inkey="image_ctx",outkey="image_ctx")|' + f'keep("image","image_ctx","labels")' + ) + pp_predict = ( + f'nyu_depth|' + f'nyu_eval_crop|copy("labels","ground_truth")|' + f'resize({RES})|' + f'resize({LABEL_RES},inkey="image",outkey="image_ctx")|' + f'value_range(-1,1)|' + f'value_range(-1,1,inkey="image_ctx",outkey="image_ctx")|' + f'keep("image","image_ctx","ground_truth")' + ) + + config.input.data = dict(name='nyu_depth_v2', split='train') + config.input.batch_size = 512 + config.input.shuffle_buffer_size = 50_000 + + config.total_epochs = 50 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 5000 + config.prefetch_to_device = 2 + config.seed = 0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + config.optax.clipping_threshold = None + + config.lr = 0.001 + config.wd = 0.000001 + config.lr_mults = ( + ('pos_embedding_encoder.*', 0.1), + ('EmbedPatches.*', 0.1), + ('encoder.*', 0.1), + ('decoder.*', 1.0) + ) + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + + # Oracle section + config.oracle = ConfigDict() + config.oracle.min_depth = MIN_DEPTH + config.oracle.max_depth = MAX_DEPTH + config.oracle.task = 'proj.uvim.depth_task' + config.oracle.model_init = 'gs://big_vision/uvim/depth_stageI_params.npz' + config.oracle.model_name = 'proj.uvim.vit' + config.oracle.model = ConfigDict(VQVAE_MODELS['base']) + config.oracle.model.input_size = (LABEL_RES, LABEL_RES) + config.oracle.model.patch_size = (LABEL_PATCH_SIZE, LABEL_PATCH_SIZE) + config.oracle.model.code_len = 256 + config.oracle.model.dict_size = 4096 + config.oracle.model.codeword_dim = 768 + config.oracle.model.with_encoder_ctx = True + config.oracle.model.with_decoder_ctx = True + config.oracle.model.code_dropout = 'random' + config.oracle.model.bottleneck_resize = True + config.oracle.model.inputs = { + 'depth': (QUANTIZATION_BINS, LABEL_PATCH_SIZE**2,), + } + config.oracle.model.outputs = config.oracle.model.inputs + + # Model section + config.model_name = 'proj.uvim.vtt' + # config.model_init = {'encoder': 'howto-i21k-B/8''} # B/8 I21K + config.model_init = {'encoder': 'howto-i21k-L/16'} # L/16 I21K + config.model = ConfigDict(VTT_MODELS['large']) + config.model.patches = ConfigDict({'size': (PATCH_SIZE, PATCH_SIZE)}) + config.model.vocab_size = config.oracle.model.dict_size + 1 + config.model.posemb_type = 'learn' + config.model.input_size = (RES, RES) + config.model.seq_len = config.oracle.model.get_ref('code_len') + config.model.zero_decoder_seq = False + + # Evaluation section + config.evals = {} + config.evals.val = ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'validation' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 1000 + + base = { + 'type': 'proj.uvim.nyu_depth', + 'dataset': config.input.data.name, + 'pp_fn': pp_predict, + 'log_steps': 2000, + 'min_depth': MIN_DEPTH, + 'max_depth': MAX_DEPTH, + } + config.evals.nyu_depth_val = dict(**base, split='validation') + + if arg.singlehost: + config.input.batch_size = 32 + config.total_epochs = 20 + elif arg.runlocal: + config.oracle.model_init = '/tmp/checkpoint.npz' + config.model_init = {'encoder': '/tmp/enc_checkpoint.npz'} + config.evals = {} + config.input.batch_size = 1 + config.input.shuffle_buffer_size = 10 + return config \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/uvim_color_task.ipynb b/big_vision/configs/proj/uvim/uvim_color_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a1ded392586375100e807f0bcdfd7b07be88bd5e --- /dev/null +++ b/big_vision/configs/proj/uvim/uvim_color_task.ipynb @@ -0,0 +1,167 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "UViM color task", + "provenance": [], + "collapsed_sections": [], + "private_outputs": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "code", + "source": [ + "# Fetch big_vision repository and move it into the current workdir (import path).\n", + "!git clone --depth=1 https://github.com/google-research/big_vision big_vision_repo\n", + "!cp -R big_vision_repo/big_vision big_vision\n", + "!pip install -qr big_vision/requirements.txt" + ], + "metadata": { + "id": "sKZK6_QpVI_O" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "\n", + "from big_vision.models.proj.uvim import vtt # stage-II model\n", + "from big_vision.models.proj.uvim import vit # stage-I model\n", + "\n", + "from big_vision.models.proj.uvim import decode\n", + "from big_vision.trainers.proj.uvim import colorization_task as task\n", + "from big_vision.configs.proj.uvim import train_imagenet2012_colorization_pretrained as config_module\n", + "\n", + "import big_vision.pp.ops_image\n", + "import big_vision.pp.ops_general\n", + "import big_vision.pp.proj.uvim.pp_ops\n", + "from big_vision.pp import builder as pp_builder\n", + "\n", + "config = config_module.get_config()\n", + "res = 512\n", + "seq_len = config.model.seq_len\n", + "\n", + "lm_model = vtt.Model(**config.model)\n", + "oracle_model = vit.Model(**config.oracle.model)\n", + "\n", + "preprocess_fn = pp_builder.get_preprocess_fn(\n", + " 'decode|resize(512)|'\n", + " 'rgb_to_grayscale_to_rgb|value_range(-1,1)|'\n", + " 'copy(inkey=\"image\",outkey=\"image_ctx\")')\n", + "\n", + "@jax.jit\n", + "def predict_code(params, x, rng, temperature):\n", + " prompts = jnp.zeros((x[\"image\"].shape[0], seq_len), dtype=jnp.int32)\n", + " seqs, _, _ = decode.temperature_sampling(\n", + " params=params, model=lm_model, seed=rng,\n", + " inputs=x[\"image\"],\n", + " prompts=prompts,\n", + " temperature=temperature,\n", + " num_samples=1, eos_token=-1, prefill=False)\n", + " seqs = jnp.squeeze(seqs, axis=1) # drop num_samples axis \n", + " return seqs - 1\n", + " \n", + "@jax.jit\n", + "def labels2code(params, x, ctx):\n", + " y, aux = oracle_model.apply(params, x, ctx=ctx, train=False, method=oracle_model.encode)\n", + " return aux[\"code\"]\n", + "\n", + "@jax.jit\n", + "def code2labels(params, code, ctx):\n", + " logits, aux = oracle_model.apply(params, code, ctx=ctx, train=False, discrete_input=True, method=oracle_model.decode)\n", + " return task.predict_outputs(logits, config.oracle)" + ], + "metadata": { + "id": "QzThueWDzc7I" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Load checkpoints\n", + "!gsutil cp -n gs://big_vision/uvim/color_stageI_params.npz gs://big_vision/uvim/color_stageII_params.npz .\n", + "\n", + "oracle_params, oracle_state = vit.load(None, \"color_stageI_params.npz\")\n", + "oracle_params = jax.device_put({\"params\": oracle_params, \"state\": oracle_state})\n", + "\n", + "lm_params = vtt.load(None, \"color_stageII_params.npz\")\n", + "lm_params = jax.device_put({\"params\": lm_params})" + ], + "metadata": { + "id": "AEjRgshLa6Fp" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Prepare set of images from coco/val2017:\n", + "# - https://cocodataset.org/\n", + "import os\n", + "import tensorflow as tf\n", + "\n", + "if not os.path.exists(\"val2017/\"):\n", + " !wget --no-clobber http://images.cocodataset.org/zips/val2017.zip\n", + " !unzip -uq val2017.zip\n", + "\n", + "dataset = tf.data.Dataset.list_files(\"val2017/*.jpg\", shuffle=True)\n", + "dataset = dataset.map(lambda filename: {\"image\": tf.io.read_file(filename)})\n", + "dataset = dataset.map(preprocess_fn)" + ], + "metadata": { + "id": "BKifDDRnH_Ll" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Run the model in a few examples:\n", + "from matplotlib import pyplot as plt\n", + "\n", + "num_examples = 4\n", + "data = dataset.batch(1).take(num_examples).as_numpy_iterator()\n", + "key = jax.random.PRNGKey(0)\n", + "temperature = jnp.array(1.0)\n", + "\n", + "def render_example(image, prediction):\n", + " f, ax = plt.subplots(1, 2, figsize=(10, 10))\n", + " ax[0].imshow(image*0.5 + 0.5)\n", + " ax[0].axis(\"off\")\n", + " ax[1].imshow(prediction*0.5 + 0.5)\n", + " ax[1].axis(\"off\")\n", + "\n", + "for idx, batch in enumerate(data):\n", + " subkey = jax.random.fold_in(key, idx)\n", + " code = predict_code(lm_params, batch, key, temperature)\n", + " aux_inputs = task.input_pp(batch, config.oracle)\n", + " prediction = code2labels(oracle_params, code, aux_inputs[\"ctx\"])\n", + " render_example(batch[\"image\"][0], prediction[\"color\"][0])" + ], + "metadata": { + "id": "TuevCy33nuv3" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/uvim_depth_task.ipynb b/big_vision/configs/proj/uvim/uvim_depth_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a4bfb3f0381238b4a0c3048006ff9fbc0ec848c7 --- /dev/null +++ b/big_vision/configs/proj/uvim/uvim_depth_task.ipynb @@ -0,0 +1,181 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "UViM depth task", + "provenance": [], + "collapsed_sections": [], + "private_outputs": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "code", + "source": [ + "# Fetch big_vision repository and move it into the current workdir (import path).\n", + "!git clone --depth=1 https://github.com/google-research/big_vision big_vision_repo\n", + "!cp -R big_vision_repo/big_vision big_vision\n", + "!pip install -qr big_vision/requirements.txt" + ], + "metadata": { + "id": "sKZK6_QpVI_O" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "\n", + "from big_vision.models.proj.uvim import vtt # stage-II model\n", + "from big_vision.models.proj.uvim import vit # stage-I model\n", + "\n", + "from big_vision.models.proj.uvim import decode\n", + "from big_vision.trainers.proj.uvim import depth_task as task\n", + "from big_vision.configs.proj.uvim import train_nyu_depth_pretrained as config_module\n", + "\n", + "import big_vision.pp.ops_image\n", + "import big_vision.pp.ops_general\n", + "import big_vision.pp.proj.uvim.pp_ops\n", + "from big_vision.pp import builder as pp_builder\n", + "\n", + "config = config_module.get_config()\n", + "res = 512\n", + "seq_len = config.model.seq_len\n", + "\n", + "lm_model = vtt.Model(**config.model)\n", + "oracle_model = vit.Model(**config.oracle.model)\n", + "\n", + "preprocess_fn = pp_builder.get_preprocess_fn(\n", + " 'resize(512)|value_range(-1,1)|'\n", + " 'copy(inkey=\"image\",outkey=\"image_ctx\")')\n", + "\n", + "@jax.jit\n", + "def predict_code(params, x, rng, temperature):\n", + " prompts = jnp.zeros((x[\"image\"].shape[0], seq_len), dtype=jnp.int32)\n", + " seqs, _, _ = decode.temperature_sampling(\n", + " params=params, model=lm_model, seed=rng,\n", + " inputs=x[\"image\"],\n", + " prompts=prompts,\n", + " temperature=temperature,\n", + " num_samples=1, eos_token=-1, prefill=False)\n", + " seqs = jnp.squeeze(seqs, axis=1) # drop num_samples axis \n", + " return seqs - 1\n", + " \n", + "@jax.jit\n", + "def labels2code(params, x, ctx):\n", + " y, aux = oracle_model.apply(params, x, ctx=ctx, train=False, method=oracle_model.encode)\n", + " return aux[\"code\"]\n", + "\n", + "@jax.jit\n", + "def code2labels(params, code, ctx):\n", + " logits, aux = oracle_model.apply(params, code, ctx=ctx, train=False, discrete_input=True, method=oracle_model.decode)\n", + " return task.predict_outputs(logits, config.oracle)" + ], + "metadata": { + "id": "QzThueWDzc7I" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Load checkpoints\n", + "!gsutil cp -n gs://big_vision/uvim/depth_stageI_params.npz gs://big_vision/uvim/depth_stageII_params.npz .\n", + "\n", + "oracle_params, oracle_state = vit.load(None, \"depth_stageI_params.npz\")\n", + "oracle_params = jax.device_put({\"params\": oracle_params, \"state\": oracle_state})\n", + "\n", + "lm_params = vtt.load(None, \"depth_stageII_params.npz\")\n", + "lm_params = jax.device_put({\"params\": lm_params})" + ], + "metadata": { + "id": "AEjRgshLa6Fp" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Prepare dataset of images from NYU Depth V2:\n", + "# - https://cs.nyu.edu/~silberman/datasets/nyu_depth_v2.html\n", + "import os\n", + "import h5py\n", + "import numpy as np\n", + "import tensorflow as tf\n", + "\n", + "if not os.path.exists(\"nyu_depth_v2_labeled.mat\"):\n", + " !wget --no-clobber http://horatio.cs.nyu.edu/mit/silberman/nyu_depth_v2/nyu_depth_v2_labeled.mat\n", + "\n", + "dataset_file = h5py.File(\"nyu_depth_v2_labeled.mat\", \"r\")\n", + "\n", + "def nyu_depth_examples():\n", + " for idx in range(dataset_file[\"images\"].shape[0]):\n", + " image = np.transpose(dataset_file[\"images\"][idx], (2, 1, 0))\n", + " yield {\"image\": image}\n", + "\n", + "dataset = tf.data.Dataset.from_generator(\n", + " nyu_depth_examples,\n", + " output_signature={\n", + " \"image\": tf.TensorSpec((480,640,3), tf.uint8),\n", + " }).map(preprocess_fn)" + ], + "metadata": { + "id": "BKifDDRnH_Ll" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Run the model in a few examples:\n", + "from matplotlib import pyplot as plt\n", + "from matplotlib import patches\n", + "\n", + "num_examples = 4\n", + "data = dataset.batch(1).take(num_examples).as_numpy_iterator()\n", + "key = jax.random.PRNGKey(0)\n", + "temperature = jnp.array(1e-7)\n", + "\n", + "def to_depth(x, nbins=256, mind=1e-3, maxd=10):\n", + " depth = x.astype(np.float32) + 0.5 # Undoes floor in expectation.\n", + " return depth/nbins * (maxd - mind) + mind\n", + "\n", + "def render_example(image, prediction, with_legend=True):\n", + " f, ax = plt.subplots(1, 2, figsize=(10, 10))\n", + " ax[0].imshow(image*0.5 + 0.5)\n", + " ax[0].axis(\"off\")\n", + " ax[1].imshow(to_depth(prediction))\n", + " ax[1].axis(\"off\")\n", + "\n", + "for idx, batch in enumerate(data):\n", + " subkey = jax.random.fold_in(key, idx)\n", + " code = predict_code(lm_params, batch, key, temperature)\n", + " aux_inputs = task.input_pp(batch, config.oracle)\n", + " prediction = code2labels(oracle_params, code, aux_inputs[\"ctx\"])\n", + " render_example(batch[\"image\"][0], prediction[\"depth\"][0])" + ], + "metadata": { + "id": "TuevCy33nuv3" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/uvim_panoptic_task.ipynb b/big_vision/configs/proj/uvim/uvim_panoptic_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c03324e5a6735f96c72f1eaecbba3cbf2b6dc843 --- /dev/null +++ b/big_vision/configs/proj/uvim/uvim_panoptic_task.ipynb @@ -0,0 +1,180 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "UViM panoptic task", + "provenance": [], + "collapsed_sections": [], + "private_outputs": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU", + "gpuClass": "standard" + }, + "cells": [ + { + "cell_type": "code", + "source": [ + "# Fetch big_vision repository and move it into the current workdir (import path).\n", + "!git clone --depth=1 https://github.com/google-research/big_vision big_vision_repo\n", + "!cp -R big_vision_repo/big_vision big_vision\n", + "!pip install -qr big_vision/requirements.txt" + ], + "metadata": { + "id": "sKZK6_QpVI_O" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import jax\n", + "import jax.numpy as jnp\n", + "import numpy as np\n", + "\n", + "from big_vision.models.proj.uvim import vtt # stage-II model\n", + "from big_vision.models.proj.uvim import vit # stage-I model\n", + "\n", + "from big_vision.models.proj.uvim import decode\n", + "from big_vision.trainers.proj.uvim import panoptic_task as task\n", + "from big_vision.configs.proj.uvim import train_coco_panoptic_pretrained as config_module\n", + "\n", + "import big_vision.pp.ops_image\n", + "import big_vision.pp.ops_general\n", + "import big_vision.pp.proj.uvim.pp_ops\n", + "from big_vision.pp import builder as pp_builder\n", + "\n", + "config = config_module.get_config()\n", + "res = 512\n", + "seq_len = config.model.seq_len\n", + "\n", + "lm_model = vtt.Model(**config.model)\n", + "oracle_model = vit.Model(**config.oracle.model)\n", + "\n", + "preprocess_fn = pp_builder.get_preprocess_fn(\n", + " 'decode|resize(512)|value_range(-1,1)|'\n", + " 'copy(inkey=\"image\",outkey=\"image_ctx\")')\n", + "\n", + "@jax.jit\n", + "def predict_code(params, x, rng, temperature):\n", + " prompts = jnp.zeros((x[\"image\"].shape[0], seq_len), dtype=jnp.int32)\n", + " seqs, _, _ = decode.temperature_sampling(\n", + " params=params, model=lm_model, seed=rng,\n", + " inputs=x[\"image\"],\n", + " prompts=prompts,\n", + " temperature=temperature,\n", + " num_samples=1, eos_token=-1, prefill=False)\n", + " seqs = jnp.squeeze(seqs, axis=1) # drop num_samples axis \n", + " return seqs - 1\n", + " \n", + "@jax.jit\n", + "def labels2code(params, x, ctx):\n", + " y, aux = oracle_model.apply(params, x, ctx=ctx, train=False, method=oracle_model.encode)\n", + " return aux[\"code\"]\n", + "\n", + "@jax.jit\n", + "def code2labels(params, code, ctx):\n", + " logits, aux = oracle_model.apply(params, code, ctx=ctx, train=False, discrete_input=True, method=oracle_model.decode)\n", + " return task.predict_outputs(logits, config.oracle)" + ], + "metadata": { + "id": "QzThueWDzc7I" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Load checkpoints\n", + "!gsutil cp -n gs://big_vision/uvim/panoptic_stageI_params.npz gs://big_vision/uvim/panoptic_stageII_params.npz .\n", + "\n", + "oracle_params, oracle_state = vit.load(None, \"panoptic_stageI_params.npz\")\n", + "oracle_params = jax.device_put({\"params\": oracle_params, \"state\": oracle_state})\n", + "\n", + "lm_params = vtt.load(None, \"panoptic_stageII_params.npz\")\n", + "lm_params = jax.device_put({\"params\": lm_params})" + ], + "metadata": { + "id": "AEjRgshLa6Fp" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Prepare set of images from coco/val2017:\n", + "# - https://cocodataset.org/\n", + "import os\n", + "import tensorflow as tf\n", + "\n", + "if not os.path.exists(\"val2017/\"):\n", + " !wget --no-clobber http://images.cocodataset.org/zips/val2017.zip\n", + " !unzip -uq val2017.zip\n", + " !wget -c https://raw.githubusercontent.com/cocodataset/panopticapi/master/panoptic_coco_categories.json\n", + "\n", + "dataset = tf.data.Dataset.list_files(\"val2017/*.jpg\", shuffle=True)\n", + "dataset = dataset.map(lambda filename: {\"image\": tf.io.read_file(filename)})\n", + "dataset = dataset.map(preprocess_fn)" + ], + "metadata": { + "id": "k2ArKPlFQVcz" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Run the model in a few examples:\n", + "from matplotlib import pyplot as plt\n", + "from matplotlib import patches\n", + "from big_vision.trainers.proj.uvim import coco_utils\n", + "\n", + "num_examples = 4\n", + "data = dataset.batch(1).take(num_examples).as_numpy_iterator()\n", + "key = jax.random.PRNGKey(0)\n", + "temperature = jnp.array(1e-7)\n", + "\n", + "def render_example(image, prediction, with_legend=True):\n", + " f, ax = plt.subplots(1, 2, figsize=(10, 10))\n", + " ax[0].imshow(image*0.5 + 0.5)\n", + " ax[0].axis(\"off\")\n", + "\n", + " rgb, info = coco_utils.rgb_panoptic_from_twochannels(prediction, boundaries=True)\n", + " ax[1].matshow(rgb)\n", + " ax[1].axis(\"off\")\n", + "\n", + " if with_legend:\n", + " handles = []\n", + " for instance in info.values():\n", + " handles.append(patches.Patch(\n", + " facecolor=np.array(instance[\"color\"])/255.0,\n", + " edgecolor='black', label=instance[\"name\"]))\n", + " ax[1].legend(handles=handles, loc=(1.04, 0.0));\n", + "\n", + "\n", + "for idx, batch in enumerate(data):\n", + " subkey = jax.random.fold_in(key, idx)\n", + " code = predict_code(lm_params, batch, key, temperature)\n", + " aux_inputs = task.input_pp(batch, config.oracle)\n", + " prediction = code2labels(oracle_params, code, aux_inputs[\"ctx\"])\n", + " render_example(batch[\"image\"][0], prediction[0])" + ], + "metadata": { + "id": "TuevCy33nuv3" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/vqvae_coco_panoptic.py b/big_vision/configs/proj/uvim/vqvae_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..5a620bae982e59187a02e54a0b735e346ad7d7ac --- /dev/null +++ b/big_vision/configs/proj/uvim/vqvae_coco_panoptic.py @@ -0,0 +1,143 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a UViM stage I model for the panoptic task. + +This config is expected to reproduce the paper's result and achieve +approximately 75.7 PQ points on the COCO holdout data. + +We also provide a low-resource variant of this config, which can be enabled +by adding `:singlehost` postfix to the config name. This one is expected to +achieve 67.8 PQ points on the COCO holdout data. +""" + +import itertools +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +def get_config(arg='res=512,patch_size=16'): + """Config for training label compression on COCO-panoptic.""" + arg = bvcc.parse_arg(arg, res=512, patch_size=16, + runlocal=False, singlehost=False) + config = mlc.ConfigDict() + + config.task = 'proj.uvim.panoptic_task' + + config.input = {} + config.input.data = dict(name='coco/2017_panoptic', split='train[4096:]') + + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 25_000 + + config.total_epochs = 1000 + + config.input.pp = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|keep("image","labels")' + ) + pp_eval = ( + f'decode|coco_panoptic|concat(["semantics","instances"], "labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|make_canonical|keep("image","labels")' + ) + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 20_000 + + # Model section + config.model_name = 'proj.uvim.vit' + config.model = mlc.ConfigDict() + config.model.input_size = (arg.res, arg.res) + config.model.patch_size = (arg.patch_size, arg.patch_size) + config.model.code_len = 256 + config.model.width = 768 + config.model.enc_depth = 6 + config.model.dec_depth = 12 + config.model.mlp_dim = 3072 + config.model.num_heads = 12 + config.model.dict_size = 4096 # Number of words in dict. + config.model.codeword_dim = 768 + config.model.dict_momentum = 0.995 # Momentum for dict. learning. + config.model.with_encoder_ctx = True + config.model.with_decoder_ctx = True + config.model.code_dropout = 'random' + config.model.bottleneck_resize = True + config.model.inputs = { + 'semantics': (133 + 1, arg.patch_size**2), # +1 for void label + 'instances': (100, arg.patch_size**2), # COCO: actually 98 train/78 validation. + } + config.model.outputs = config.model.inputs + + # VQVAE-specific params. + config.freeze_dict = False # Will freeze a dict. inside VQ-VAE model. + config.w_commitment = 0.0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.lr = 4e-4 + config.wd = 4e-5 + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + config.grad_clip_norm = 1.0 + + # Evaluation section + config.evals = {} + config.evals.val = mlc.ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'train[:4096]' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.uvim.coco_panoptic', + 'pp_fn': pp_eval.replace('decode|', ''), + 'log_steps': 10_000, + # Filters objects that occupy less than 0.03^2 fraction of all pixels. + # 'predict_kwargs': {'min_fraction': 0.03 ** 2}, + } + config.evals.coco_panoptic_train = dict(**base, split='train[4096:8192]') + config.evals.coco_panoptic_holdout = dict(**base, split='train[:4096]') + config.evals.coco_panoptic = dict(**base, split='validation') + + # config.evals.save_pred = dict(type='proj.uvim.save_predictions') + # config.evals.save_pred.pp = pp_eval.replace('decode|', '') + # config.evals.save_pred.log_steps = 100_000 + # config.evals.save_pred.dataset = config.dataset + # config.evals.save_pred.split = 'validation[:1024]' + # config.evals.save_pred.outfile = 'inference.npz' + + config.seed = 0 + + if arg.singlehost: + config.input.batch_size = 128 + config.num_epochs = 100 + elif arg.runlocal: + config.input.batch_size = 16 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.enc_depth = 1 + config.model.dec_depth = 1 + config.evals.val.data.split = 'validation[:16]' + config.evals.val.log_steps = 20 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/vqvae_imagenet2012_colorization.py b/big_vision/configs/proj/uvim/vqvae_imagenet2012_colorization.py new file mode 100644 index 0000000000000000000000000000000000000000..d4ecb7a57b6a47f573eca63f5b2652cdcc33dc47 --- /dev/null +++ b/big_vision/configs/proj/uvim/vqvae_imagenet2012_colorization.py @@ -0,0 +1,151 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a colorization VQ-VAE on imagenet2012. +""" + +import itertools +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +def get_config(arg='res=512,patch_size=16'): + """A config for training a UViM stage I model for the colorization task.""" + arg = bvcc.parse_arg(arg, res=512, patch_size=16, + runlocal=False, singlehost=False) + config = mlc.ConfigDict() + + config.task = 'proj.uvim.colorization_task' + + config.input = {} + config.input.data = dict(name='imagenet2012', split='train[4096:]') + + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 25_000 + + config.total_epochs = 100 + + config.input.pp = ( + f'decode_jpeg_and_inception_crop({arg.res})' + f'|flip_lr' + f'|copy(inkey="image", outkey="labels")' + f'|rgb_to_grayscale_to_rgb(inkey="image",outkey="image")' + f'|value_range(-1,1,key="image")' + f'|value_range(-1,1,key="labels")' + f'|keep("image","labels")') + + pp_eval = ( + f'decode' + f'|resize({arg.res})' + f'|copy(inkey="image", outkey="labels")' + f'|rgb_to_grayscale_to_rgb(inkey="image",outkey="image")' + f'|value_range(-1,1,key="image")' + f'|value_range(-1,1,key="labels")' + f'|keep("image","labels")') + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 20_000 + + # Model section + config.model_name = 'proj.uvim.vit' + config.model = mlc.ConfigDict() + config.model.input_size = (arg.res, arg.res) + config.model.patch_size = (arg.patch_size, arg.patch_size) + config.model.code_len = 256 + config.model.width = 768 + config.model.enc_depth = 6 + config.model.dec_depth = 12 + config.model.mlp_dim = 3072 + config.model.num_heads = 12 + config.model.dict_size = 4096 # Number of words in dict. + config.model.codeword_dim = 768 + config.model.dict_momentum = 0.995 # Momentum for dict. learning. + config.model.with_encoder_ctx = True + config.model.with_decoder_ctx = True + config.model.code_dropout = 'random' + config.model.bottleneck_resize = True + config.model.inputs = { + 'color': (3, arg.patch_size**2), + } + config.model.outputs = config.model.inputs + + # VQVAE-specific params. + config.freeze_dict = False # Will freeze a dict. inside VQ-VAE model. + config.w_commitment = 0.0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.lr = 4e-4 + config.wd = 4e-5 + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + config.grad_clip_norm = 1.0 + + # Evaluation section + config.evals = {} + config.evals.val = mlc.ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'train[:4096]' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.uvim.psnr', + 'pp_fn': pp_eval.replace('decode|', ''), + 'log_steps': 10_000, + } + config.evals.psnr_train = dict(**base, split='train[4096:8192]') + config.evals.psnr_holdout = dict(**base, split='train[:4096]') + config.evals.psnr_val = dict(**base, split='validation') + + config.evals.colorization_val_coltran_fid = { + 'type': 'proj.uvim.coltran_fid', + 'log_steps': 100_000, + } + + # config.evals.save_pred = dict(type='proj.uvim.save_predictions') + # config.evals.save_pred.pp = pp_eval.replace('decode|', '') + # config.evals.save_pred.log_steps = 100_000 + # config.evals.save_pred.dataset = config.dataset + # config.evals.save_pred.split = 'validation[:1024]' + # config.evals.save_pred.outfile = 'inference.npz' + + config.seed = 0 + + if arg.singlehost: + config.input.batch_size = 128 + config.total_epochs = 20 + elif arg.runlocal: + config.input.batch_size = 16 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.enc_depth = 1 + config.model.dec_depth = 1 + config.evals.val.data.split = 'validation[:16]' + config.evals.val.log_steps = 20 + config.evals.psnr_train.split = 'train[:256]' + config.evals.psnr_train.log_steps = 20 + config.evals.psnr_holdout.split = 'train[256:512]' + config.evals.psnr_holdout.log_steps = 20 + config.evals.psnr_val.split = 'train[:256]' + config.evals.psnr_val.log_steps = 20 + config.evals.colorization_val_coltran_fid.split = 'validation[:256]' + config.evals.colorization_val_coltran_fid.log_steps = 20 + + return config \ No newline at end of file diff --git a/big_vision/configs/proj/uvim/vqvae_nyu_depth.py b/big_vision/configs/proj/uvim/vqvae_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..d5ae29321bfa38bd2df097092126e0f3f15870f4 --- /dev/null +++ b/big_vision/configs/proj/uvim/vqvae_nyu_depth.py @@ -0,0 +1,144 @@ +# Copyright 2022 Big Vision 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. + +# pylint: disable=line-too-long +r"""A config for training a UViM stage I model for the depth task. +""" + +import itertools +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +QUANTIZATION_BINS = 256 +# Depths outside of this range will not be evaluated. +MIN_DEPTH = 1e-3 +MAX_DEPTH = 10 + + +def get_config(arg='res=512,patch_size=16'): + """Config for training label compression on NYU depth v2.""" + arg = bvcc.parse_arg(arg, res=512, patch_size=16, + runlocal=False, singlehost=False) + config = mlc.ConfigDict() + + config.task = 'proj.uvim.depth_task' + + config.input = {} + config.input.data = dict(name='nyu_depth_v2', split='train) + + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 25_000 + + config.total_epochs = 200 + + config.input.pp = ( + f'decode|nyu_depth|' + f'randu("fliplr")|det_fliplr(key="image")|det_fliplr(key="labels")|' + f'inception_box|crop_box(key="image")|crop_box(key="labels")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|keep("image","labels")' + ) + + pp_eval = ( + f'decode|nyu_depth|nyu_eval_crop|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|keep("image","labels")' + ) + + # There are no image IDs in TFDS, so hand through the ground truth for eval. + pp_pred = ( + f'nyu_depth|nyu_eval_crop|copy("labels","ground_truth")|' + f'resize({arg.res})|resize({arg.res},key="labels",method="nearest")|' + f'value_range(-1, 1)|' + f'keep("image","labels","ground_truth")' + ) + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = 20_000 + + # Model section + config.min_depth = MIN_DEPTH + config.max_depth = MAX_DEPTH + config.model_name = 'proj.uvim.vit' + config.model = mlc.ConfigDict() + config.model.input_size = (arg.res, arg.res) + config.model.patch_size = (arg.patch_size, arg.patch_size) + config.model.code_len = 256 + config.model.width = 768 + config.model.enc_depth = 6 + config.model.dec_depth = 12 + config.model.mlp_dim = 3072 + config.model.num_heads = 12 + config.model.dict_size = 4096 # Number of words in dict. + config.model.codeword_dim = 768 + config.model.dict_momentum = 0.995 # Momentum for dict. learning. + config.model.with_encoder_ctx = True + config.model.with_decoder_ctx = True + config.model.code_dropout = 'random' + config.model.bottleneck_resize = True + config.model.inputs = { + 'depth': (QUANTIZATION_BINS, arg.patch_size**2), + } + config.model.outputs = config.model.inputs + + # VQVAE-specific params. + config.freeze_dict = False # Will freeze a dict. inside VQ-VAE model. + config.w_commitment = 0.0 + + # Optimizer section + config.optax_name = 'big_vision.scale_by_adafactor' + config.optax = dict(beta2_cap=0.95) + + config.lr = 1e-3 + config.wd = 1e-5 + config.schedule = dict(decay_type='cosine', warmup_steps=4_000) + config.grad_clip_norm = 1.0 + + # Evaluation section + config.evals = {} + config.evals.val = mlc.ConfigDict() + config.evals.val.type = 'proj.uvim.compute_mean' + config.evals.val.pred = 'validation' + config.evals.val.data = {**config.input.data} + config.evals.val.data.split = 'validation' + config.evals.val.pp_fn = pp_eval + config.evals.val.log_steps = 250 + + base = { + 'type': 'proj.uvim.nyu_depth', + 'dataset': config.input.data.name, + 'pp_fn': pp_pred, + 'log_steps': 2000, + 'min_depth': MIN_DEPTH, + 'max_depth': MAX_DEPTH, + } + config.evals.nyu_depth_val = dict(**base, split='validation') + + config.seed = 0 + + if arg.singlehost: + config.input.batch_size = 128 + config.total_epochs = 50 + elif arg.runlocal: + config.input.batch_size = 16 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.enc_depth = 1 + config.model.dec_depth = 1 + config.evals.val.data.split = 'validation[:16]' + config.evals.val.log_steps = 20 + + return config \ No newline at end of file diff --git a/big_vision/configs/transfer.py b/big_vision/configs/transfer.py new file mode 100644 index 0000000000000000000000000000000000000000..1ee64e43b274bdf5d462c7e3f3d9b5fc085c1796 --- /dev/null +++ b/big_vision/configs/transfer.py @@ -0,0 +1,186 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long,missing-function-docstring +r"""A config for transferring vit-augreg. + +Best HP selected on (mini)val, expected test results (repeated 5 times): + +ViT-Augreg-B/32: + Dataset, crop, learning rate, mean (%), range (%) + - ImageNet, inception_crop, 0.03, 83.27, [83.22...83.33] + - Cifar10, resmall_crop, 0.003, 98.55, [98.46...98.6] + - Cifar100, resmall_crop, 0.01, 91.35, [91.09...91.62] + - Pets, inception_crop, 0.003, 93.78, [93.62...94.00] + - Flowers, inception_crop, 0.003, 99.43, [99.42...99.45] + + +Command to run: +big_vision.train \ + --config big_vision/configs/transfer.py:model=vit-i21k-augreg-b/32,dataset=cifar10,crop=resmall_crop \ + --workdir gs://$GS_BUCKET_NAME/big_vision/workdir/`date '+%m-%d_%H%M'` --config.lr=0.03 +""" + +import big_vision.configs.common as bvcc +import ml_collections as mlc + + +def _set_model(config, model): + """Load pre-trained models: vit or bit.""" + # Reset the head to init (of zeros) when transferring. + config.model_load = dict(dont_load=['head/kernel', 'head/bias']) + + if model == 'vit-i21k-augreg-b/32': + # Load "recommended" upstream B/32 from https://arxiv.org/abs/2106.10270 + config.model_name = 'vit' + config.model_init = 'howto-i21k-B/32' + config.model = dict(variant='B/32', pool_type='tok') + elif model == 'vit-i21k-augreg-l/16': + config.model_name = 'vit' + config.model_init = 'howto-i21k-L/16' + config.model = dict(variant='L/16', pool_type='tok') + elif model == 'vit-s16': + config.model_name = 'vit' + config.model_init = 'i1k-s16-300ep' + config.model = dict(variant='S/16', pool_type='gap', posemb='sincos2d', + rep_size=True) + elif model == 'bit-m-r50x1': + config.model_name = 'bit_paper' + config.model_init = 'M' + config.model = dict(depth=50, width=1) + else: + raise ValueError(f'Unknown model: {model}, please define customized model.') + + +def _set_dataset(config, dataset, crop='inception_crop', h_res=448, l_res=384): + if dataset == 'cifar10': + _set_task(config, 'cifar10', 'train[:98%]', 'train[98%:]', 'test', 10, steps=10_000, warmup=500, crop=crop, h_res=h_res, l_res=l_res) + elif dataset == 'cifar100': + _set_task(config, 'cifar100', 'train[:98%]', 'train[98%:]', 'test', 100, steps=10_000, warmup=500, crop=crop, h_res=h_res, l_res=l_res) + elif dataset == 'imagenet2012': + _set_task(config, 'imagenet2012', 'train[:99%]', 'train[99%:]', 'validation', 1000, steps=20_000, warmup=500, crop=crop, h_res=h_res, l_res=l_res) + _set_imagenet_variants(config) + elif dataset == 'oxford_iiit_pet': + _set_task(config, 'oxford_iiit_pet', 'train[:90%]', 'train[90%:]', 'test', 37, steps=500, warmup=100, crop=crop, h_res=h_res, l_res=l_res) + elif dataset == 'oxford_flowers102': + _set_task(config, 'oxford_flowers102', 'train[:90%]', 'train[90%:]', 'test', 102, steps=500, warmup=100, crop=crop, h_res=h_res, l_res=l_res) + else: + raise ValueError( + f'Unknown dataset: {dataset}, please define customized dataset.') + + +def _set_task(config, dataset, train, val, test, n_cls, + steps=20_000, warmup=500, lbl='label', crop='resmall_crop', + flip=True, h_res=448, l_res=384): + """Vision task with val and test splits.""" + config.total_steps = steps + config.schedule = dict( + warmup_steps=warmup, + decay_type='cosine', + ) + + config.input.data = dict(name=dataset, split=train) + pp_common = ( + '|value_range(-1, 1)|' + f'onehot({n_cls}, key="{lbl}", key_result="labels")|' + 'keep("image", "labels")' + ) + + if crop == 'inception_crop': + pp_train = f'decode|inception_crop({l_res})' + elif crop == 'resmall_crop': + pp_train = f'decode|resize_small({h_res})|random_crop({l_res})' + elif crop == 'resize_crop': + pp_train = f'decode|resize({h_res})|random_crop({l_res})' + else: + raise ValueError(f'Unknown crop: {crop}. Must be one of: ' + 'inception_crop, resmall_crop, resize_crop') + if flip: + pp_train += '|flip_lr' + config.input.pp = pp_train + pp_common + + pp = f'decode|resize_small({h_res})|central_crop({l_res})' + pp_common + config.num_classes = n_cls + + def get_eval(split): + return dict( + type='classification', + data=dict(name=dataset, split=split), + loss_name='softmax_xent', + log_steps=100, + pp_fn=pp, + ) + config.evals = dict(val=get_eval(val), test=get_eval(test)) + + +def _set_imagenet_variants(config, h_res=448, l_res=384): + """Evaluation tasks on ImageNet variants: v2 and real.""" + pp = (f'decode|resize_small({h_res})|central_crop({l_res})' + '|value_range(-1, 1)|onehot(1000, key="{lbl}", key_result="labels")|' + 'keep("image", "labels")' + ) + + # Special-case rename for i1k (val+test -> minival+val) + config.evals.minival = config.evals.val + config.evals.val = config.evals.test + # NOTE: keep test == val for convenience in subsequent analysis. + + config.evals.real = dict(type='classification') + config.evals.real.data = dict(name='imagenet2012_real', split='validation') + config.evals.real.pp_fn = pp.format(lbl='real_label') + config.evals.real.loss_name = config.loss + config.evals.real.log_steps = 100 + + config.evals.v2 = dict(type='classification') + config.evals.v2.data = dict(name='imagenet_v2', split='test') + config.evals.v2.pp_fn = pp.format(lbl='label') + config.evals.v2.loss_name = config.loss + config.evals.v2.log_steps = 100 + + +def get_config(arg=None): + """Config for adaptation.""" + arg = bvcc.parse_arg(arg, model='vit', dataset='cifar10', crop='resmall_crop', + h_res=448, l_res=384, batch_size=512, fsdp=False, + runlocal=False) + config = mlc.ConfigDict() + + config.input = {} + config.input.batch_size = arg.batch_size if not arg.runlocal else 8 + config.input.shuffle_buffer_size = 50_000 if not arg.runlocal else 100 + + config.log_training_steps = 10 + config.ckpt_steps = 1000 + config.ckpt_timeout = 600 + + # Optimizer section + config.optax_name = 'big_vision.momentum_hp' + config.grad_clip_norm = 1.0 + config.wd = None # That's our default, but just being explicit here! + config.loss = 'softmax_xent' + config.lr = 0.01 + config.mixup = dict(p=0.0) + + config.seed = 0 + + _set_dataset(config, arg.dataset, arg.crop, arg.h_res, arg.l_res) + + _set_model(config, arg.model) + if arg.fsdp: + config.mesh = [('data', -1)] + config.sharding_strategy = [('.*', 'fsdp(axis="data")')] + config.sharding_rules = [('act_batch', ('data',))] + config.model.scan = True + + return config \ No newline at end of file diff --git a/big_vision/configs/vit_i1k.py b/big_vision/configs/vit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..b17a43bacd272c4b1ae0e9ef614fdf6a3ed6d911 --- /dev/null +++ b/big_vision/configs/vit_i1k.py @@ -0,0 +1,177 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training ViT on ILSVRC-2012 as in https://arxiv.org/abs/2106.10270 + +This config does NOT include regularization (dropout, stochastic depth), which +was shown to help with B/32, B/16, L/16 models in the paper (Figure 4). + +This configuration makes use of the "arg" to get_config to select which model +to run, so a few examples are given below: + +Run training of a B/16 model: + +big_vision.train \ + --config big_vision/configs/vit_i1k.py:variant=B/16 \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` + +Run training of a B/32 model with custom aug-strenght and 300ep: + +big_vision.train \ + --config big_vision/configs/vit_i1k.py:variant=B/32,aug=light1 \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` \ + --config.total_epochs 300 +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + +MIXUP_DEF = { + 'none': dict(p=0.0, fold_in=None), + 'light1': dict(p=0.0, fold_in=None), + 'light2': dict(p=0.2, fold_in=None), + 'medium1': dict(p=0.2, fold_in=None), + 'medium2': dict(p=0.5, fold_in=None), + 'strong1': dict(p=0.5, fold_in=None), + 'strong2': dict(p=0.8, fold_in=None), +} + +RANDAUG_DEF = { + 'none': '', + 'light1': 'randaug(2,0)', # Actually not nothing! + 'light2': 'randaug(2,10)', + 'medium1': 'randaug(2,15)', + 'medium2': 'randaug(2,15)', + 'strong1': 'randaug(2,20)', + 'strong2': 'randaug(2,20)', +} + + +def get_config(arg=None): + """Config for training.""" + arg = bvcc.parse_arg(arg, variant='B/16', runlocal=False, aug='') + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 300 + config.num_classes = 1000 + config.loss = 'sigmoid_xent' + config.init_head_bias = -6.9 + + # If this gives a KeyError, lookup Fig4 of the paper and add an entry. + # Note, this here is a good average between 30ep and 300ep, sometimes you coud + # find a slightly better setting for either of them. + aug_setting = arg.aug or { + 'Ti/16': 'light1', + 'S/32': 'medium1', + 'S/16': 'medium2', + 'B/32': 'medium2', + 'B/16': 'medium2', + 'L/16': 'medium2', + }[arg.variant] + + config.input = dict() + config.input.data = dict( + name='imagenet2012', + split='train[:99%]', + ) + config.input.batch_size = 4096 + config.input.cache = 'raw_data' if arg.runlocal else 'none' # Needs up to 120GB of RAM! + config.input.shuffle_buffer_size = 250_000 + + pp_common = ( + '|value_range(-1, 1)' + '|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ) + config.input.pp = ( + 'decode_jpeg_and_inception_crop(224)|flip_lr|' + + RANDAUG_DEF[aug_setting] + + pp_common.format(lbl='label') + ) + pp_eval = 'decode|resize_small(256)|central_crop(224)' + pp_common + + # To continue using the near-defunct randaug op. + config.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'archive.randaug'] + + # Aggressive pre-fetching because our models here are small, so we not only + # can afford it, but we also need it for the smallest models to not be + # bottle-necked by the input pipeline. Play around with it for -L models tho. + config.input.prefetch = 8 + config.prefetch_to_device = 4 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'vit' + config.model = dict( + variant=arg.variant, + rep_size=True, + pool_type='tok', + ) + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + # The modified AdaFactor we introduced in https://arxiv.org/abs/2106.04560 + # almost always behaves exactly like adam, but at a fraction of the memory + # cost (specifically, adam_bf16 = +1.5M, adafactor = +0.5M), hence it is a + # good idea to try it when you are memory-bound! + # config.optax_name = 'big_vision.scale_by_adafactor' + # A good flag to play with when hitting instabilities, is the following: + # config.optax = dict(beta2_cap=0.95) + + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = dict(warmup_steps=10_000, decay_type='cosine') + + config.mixup = MIXUP_DEF[aug_setting] + + # Eval section + def get_eval(split, dataset='imagenet2012'): + return dict( + type='classification', + data=dict(name=dataset, split=split), + pp_fn=pp_eval.format(lbl='label'), + loss_name=config.loss, + log_steps=2500, # Very fast O(seconds) so it's fine to run it often. + cache='final' if arg.runlocal else 'none', + ) + config.evals = {} + config.evals.train = get_eval('train[:2%]') + config.evals.minival = get_eval('train[99%:]') + config.evals.val = get_eval('validation') + config.evals.v2 = get_eval('test', dataset='imagenet_v2') + config.evals.real = get_eval('validation', dataset='imagenet2012_real') + config.evals.real.pp_fn = pp_eval.format(lbl='real_label') + + config.fewshot = get_fewshot_lsr(runlocal=arg.runlocal) + config.fewshot.log_steps = 10_000 + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 8 + config.input.cache_raw = False + config.evals.train.data.split = 'train[:16]' + config.evals.minival.data.split = 'train[:16]' + config.evals.val.data.split = 'validation[:16]' + config.evals.v2.data.split = 'test[:16]' + config.evals.real.data.split = 'validation[:16]' + + return config \ No newline at end of file diff --git a/big_vision/configs/vit_i21k.py b/big_vision/configs/vit_i21k.py new file mode 100644 index 0000000000000000000000000000000000000000..adae41838736be4f4a9737e614152dc5c7fd329b --- /dev/null +++ b/big_vision/configs/vit_i21k.py @@ -0,0 +1,145 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training ViT on ImageNet-21k as in https://arxiv.org/abs/2106.10270 + +This config relies on the Imagenet-21k tfds dataset, which is not yet +available publicly in TFDS. We intend to add the dataset to public TFDS soon, +and this config will then be runnable. + +Note that regularization (dropout, stochastic depth) is not currently +implemented. This was not beneficial for ImageNet-21k pre-trainning. +""" + +import big_vision.configs.common as bvcc +from big_vision.configs.common_fewshot import get_fewshot_lsr +import ml_collections as mlc + +MIXUP_DEF = { + 'none': dict(p=0.0, fold_in=None), + 'light1': dict(p=0.0, fold_in=None), + 'light2': dict(p=0.2, fold_in=None), + 'medium1': dict(p=0.2, fold_in=None), + 'medium2': dict(p=0.5, fold_in=None), + 'strong1': dict(p=0.5, fold_in=None), + 'strong2': dict(p=0.8, fold_in=None), +} + +RANDAUG_DEF = { + 'none': '', + 'light1': 'randaug(2,0)', # Actually not nothing! + 'light2': 'randaug(2,10)', + 'medium1': 'randaug(2,15)', + 'medium2': 'randaug(2,15)', + 'strong1': 'randaug(2,20)', + 'strong2': 'randaug(2,20)', +} + + +def get_config(arg=None): + """Config for training.""" + arg = bvcc.parse_arg(arg, variant='B/16', runlocal=False, aug=None) + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 300 + config.num_classes = 21843 + config.init_head_bias = -10.0 + config.loss = 'sigmoid_xent' + + # If this gives a KeyError, lookup Fig4 of the paper and add an entry. + # Note, this here is a good average between 30ep and 300ep, sometimes you coud + # find a slightly better setting for either of them. + aug_setting = { + 'Ti/16': 'none', + 'S/32': 'none', + 'S/16': 'light1', + 'B/32': 'light2', + 'B/16': 'light2', + 'L/16': 'medium2', + }[arg.variant] + + config.input = dict() + config.input.data = dict( + name='imagenet21k', + split='full[51200:]', + ) + config.input.batch_size = 4096 + config.input.shuffle_buffer_size = 250_000 # Per host, so small-ish is ok. + + pp_common = '|value_range(-1, 1)|onehot({onehot_args})|keep("image", "labels")' + pp_common_i21k = pp_common.format(onehot_args=f'{config.num_classes}') + pp_common_i1k = pp_common.format(onehot_args='1000, key="label", key_result="labels"') + config.input.pp = f'decode_jpeg_and_inception_crop(224)|flip_lr|{RANDAUG_DEF[aug_setting]}' + pp_common_i21k + pp_eval = 'decode|resize_small(256)|central_crop(224)' + + # To continue using the near-defunct randaug op. + config.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'archive.randaug'] + + # Aggressive pre-fetching because our models here are small, so we not only + # can afford it, but we also need it for the smallest models to not be + # bottle-necked by the input pipeline. Play around with it for -L models tho. + config.input.prefetch = 8 + config.prefetch_to_device = 4 + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'vit' + config.model = dict(variant=arg.variant, pool_type='gap', posemb='learn') + + # Optimizer section + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + config.grad_clip_norm = 1.0 + + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = dict(warmup_steps=10_000, decay_type='cosine') + + config.mixup = MIXUP_DEF[aug_setting] + + # Evaluations on i21k itself. + def eval_i21k(split): + return dict( + type='classification', + data={**config.input.data, 'split': split}, + pp_fn=pp_eval + pp_common_i21k, + loss_name=config.loss, + log_steps=1000, # Very fast O(seconds) so it's fine to run it often. + ) + config.evals = {} + config.evals.test = eval_i21k('full[:25_600]') + config.evals.val = eval_i21k('full[25_600:51_200]') + config.evals.train = eval_i21k('full[51_200:76_800]') + + # Few-shot evaluators + config.evals.fewshot = get_fewshot_lsr(runlocal=arg.runlocal) + config.evals.fewshot.log_steps = 25_000 + + # Make a few things much smaller for quick local debugging testruns. + if arg.runlocal: + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 8 + config.evals.test.data.split = 'full[:16]' + config.evals.train.data.split = 'full[:16]' + config.evals.val.data.split = 'full[:16]' + config.evals.i1k_val.data.split = 'validation[:16]' + config.evals.i1k_v2.data.split = 'test[:16]' + config.evals.i1k_a.data.split = 'test[:16]' + config.evals.i1k_r.data.split = 'test[:16]' + + return config \ No newline at end of file diff --git a/big_vision/configs/vit_s16_i1k.py b/big_vision/configs/vit_s16_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..d50dd26508713b67c434f0e677e58fbef7d8af13 --- /dev/null +++ b/big_vision/configs/vit_s16_i1k.py @@ -0,0 +1,105 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Pre-training ViT-S/16 on ILSVRC-2012 following https://arxiv.org/abs/2205.01580. + +This should take 6-7h to finish 90ep on a TPU-v3-8 and reach 76.5%, +see the tech report for more details. + +Command to run: + +big_vision.train \ + --config big_vision/configs/vit_s16_i1k.py \ + --workdir gs://[your_bucket]/big_vision/`date '+%m-%d_%H%M'` + +To run for 300ep, add `--config.total_epochs 300` to the command. +""" + +import ml_collections as mlc + + +def get_config(): + """Config for training.""" + config = mlc.ConfigDict() + + config.seed = 0 + config.total_epochs = 90 + config.num_classes = 1000 + config.loss = 'softmax_xent' + + config.input = {} + config.input.data = dict( + name='imagenet2012', + split='train[:99%]', + ) + config.input.batch_size = 1024 + config.input.cache_raw = True # Needs up to 120GB of RAM! + config.input.shuffle_buffer_size = 250_000 + + pp_common = ( + '|value_range(-1, 1)' + '|onehot(1000, key="{lbl}", key_result="labels")' + '|keep("image", "labels")' + ) + config.input.pp = ( + 'decode_jpeg_and_inception_crop(224)|flip_lr|randaug(2,10)' + + pp_common.format(lbl='label') + ) + pp_eval = 'decode|resize_small(256)|central_crop(224)' + pp_common + + # To continue using the near-defunct randaug op. + config.pp_modules = ['ops_general', 'ops_image', 'ops_text', 'archive.randaug'] + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + + # Model section + config.model_name = 'vit' + config.model = dict( + variant='S/16', + rep_size=True, + pool_type='gap', + posemb='sincos2d', + ) + + # Optimizer section + config.grad_clip_norm = 1.0 + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16') + + config.lr = 0.001 + config.wd = 0.0001 + config.schedule = dict(warmup_steps=10_000, decay_type='cosine') + + config.mixup = dict(p=0.2, fold_in=None) + + # Eval section + def get_eval(split, dataset='imagenet2012'): + return dict( + type='classification', + data=dict(name=dataset, split=split), + pp_fn=pp_eval.format(lbl='label'), + loss_name=config.loss, + log_steps=2500, # Very fast O(seconds) so it's fine to run it often. + ) + config.evals = {} + config.evals.train = get_eval('train[:2%]') + config.evals.minival = get_eval('train[99%:]') + config.evals.val = get_eval('validation') + config.evals.v2 = get_eval('test', dataset='imagenet_v2') + config.evals.real = get_eval('validation', dataset='imagenet2012_real') + config.evals.real.pp_fn = pp_eval.format(lbl='real_label') + + return config diff --git a/big_vision/datasets/__pycache__/core.cpython-310.pyc b/big_vision/datasets/__pycache__/core.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de37c8c6980e8881b338a82afaef513fe91e028c Binary files /dev/null and b/big_vision/datasets/__pycache__/core.cpython-310.pyc differ diff --git a/big_vision/datasets/__pycache__/jsonl.cpython-310.pyc b/big_vision/datasets/__pycache__/jsonl.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fc2c7114e6856f626a3df8d70df902c7a9d33f07 Binary files /dev/null and b/big_vision/datasets/__pycache__/jsonl.cpython-310.pyc differ diff --git a/big_vision/datasets/ai2d/ai2d.py b/big_vision/datasets/ai2d/ai2d.py new file mode 100644 index 0000000000000000000000000000000000000000..710be04604a1d837de766f2b3ff436e56c585957 --- /dev/null +++ b/big_vision/datasets/ai2d/ai2d.py @@ -0,0 +1,209 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""AI2D TFDS converter. + + +It's a small dataset, so can be built locally. Copy the data to local disk: + + mkdir -p /tmp/data/ai2d && cd /tmp/data/ai2d + wget https://ai2-public-datasets.s3.amazonaws.com/diagrams/ai2d-all.zip + wget https://s3-us-east-2.amazonaws.com/prior-datasets/ai2d_test_ids.csv + wget https://github.com/googlefonts/dm-fonts/raw/main/Sans/fonts/ttf/DMSans-Regular.ttf + unzip ai2d-all.zip + +Also download a font for rendering, set the location in the flag font_path. + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd third_party/py/big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=ai2d + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load(ai2d', split='train', data_dir='/tmp/tfds') +""" + +import functools +import glob +import io +import json +import os +from typing import Any, Dict + +from absl import flags +import numpy as np +from PIL import Image +from PIL import ImageDraw +from PIL import ImageFont +import tensorflow_datasets as tfds + + +_DESCRIPTION = """AI2D dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{kembhavi2016eccv, + author = {Aniruddha Kembhavi, Mike Salvato, Eric Kolve, Minjoon Seo, Hannaneh Hajishirzi, Ali Farhadi}, + title = {A Diagram Is Worth A Dozen Images}, + booktitle = {European Conference on Computer Vision (ECCV)}, + year = {2016} + url={https://api.semanticscholar.org/CorpusID:2682274} +} +""" +# pylint: enable=line-too-long + + +_INPUT_PATH = flags.DEFINE_string( + 'input_path', '/tmp/data/ai2d/', 'Downloaded AI2D data.' +) +_FONT_PATH = flags.DEFINE_string( + 'font_path', '/tmp/data/ai2d/DMSans-Regular.ttf', + 'Font for rendering annotations.' +) + + +class Ai2d(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for AI2D dataset.""" + + VERSION = tfds.core.Version('1.1.0') + RELEASE_NOTES = {'1.1.0': 'Re-create from scratch + more fields.'} + + def _info(self): + """Returns the metadata.""" + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'id': tfds.features.Text(), + 'question': tfds.features.Text(), + 'label': tfds.features.Scalar(np.int32), + 'answer': tfds.features.Text(), + 'possible_answers': tfds.features.Sequence(tfds.features.Text()), + 'abc_label': tfds.features.Scalar(np.bool_), + 'image_name': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='png'), + }), + homepage='https://allenai.org/data/diagrams', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in ('test', 'train')} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples.""" + with open( + os.path.join(_INPUT_PATH.value, 'ai2d_test_ids.csv'), 'r' + ) as f: + all_test_ids = f.readlines() + all_test_ids = [line.strip() for line in all_test_ids] + + all_annotation_paths = glob.glob( + os.path.join(_INPUT_PATH.value, 'ai2d/questions', '*.json')) + for annotation_path in all_annotation_paths: + basename = os.path.basename(annotation_path) + image_id = basename.split('.')[0] + if image_id in all_test_ids and split == 'train': + continue + elif image_id not in all_test_ids and split == 'test': + continue + + text_annotation_path = os.path.join( + _INPUT_PATH.value, 'ai2d/annotations', basename + ) + with open(annotation_path, 'r') as f: + with open(text_annotation_path, 'r') as g: + question_json = json.load(f) + text_annotation_json = json.load(g) + for question in question_json['questions']: + label_id = int( + question_json['questions'][question]['correctAnswer'] + ) + choices = question_json['questions'][question]['answerTexts'] + abc_label = question_json['questions'][question]['abcLabel'] + annotation = { + 'id': question_json['questions'][question]['questionId'], + 'question': question, + 'label': label_id, + 'answer': choices[label_id], + 'possible_answers': tuple(choices), + 'abc_label': abc_label, + 'image_name': question_json['imageName'], + } + annotation['image'] = _create_image( + annotation, text_annotation_json['text'] + ) + yield annotation['id'], annotation + + +@functools.cache +def Font( # pylint: disable=invalid-name + size: int, +) -> ImageFont.FreeTypeFont: + """Loads the font from in the specified style. + + Args: + size: The size of the returned font. + + Returns: + The loaded font. + """ + return ImageFont.truetype(_FONT_PATH.value, size=size) + + +def _create_image( + annotation: Dict[str, Any], text_annotation: Dict[str, Any] +) -> bytes: + """Adds image to one annotation.""" + img_path = os.path.join(_INPUT_PATH.value, 'ai2d/images', + annotation['image_name']) + with open(img_path, 'rb') as f: + if annotation['abc_label']: + raw_image = _draw_text(f, text_annotation) + else: + raw_image = f.read() + return raw_image + + +def _draw_text(image, text_annotations) -> bytes: + """Replaces text in image by the correct replacement letter from AI2D.""" + image = Image.open(image) + draw = ImageDraw.Draw(image) + for annotation in text_annotations: + current_annotation = text_annotations[annotation] + rectangle = current_annotation['rectangle'] + box = [tuple(rectangle[0]), tuple(rectangle[1]),] + text = current_annotation['replacementText'] + position = box[0] + draw.rectangle(box, fill='white') + font_size = 100 + x_diff = box[1][0] - box[0][0] + y_diff = box[1][1] - box[0][1] + font = Font(font_size) + size = font.getbbox(text) + while (size[2] > x_diff or size[3] > y_diff) and font_size > 0: + font = Font(font_size) + size = font.getbbox(text) + font_size -= 1 + delta = (x_diff - size[2]) // 2 + position = (position[0] + delta, position[1]) + draw.text(position, text, fill='black', font=font) + new_image_bytes = io.BytesIO() + image.save(new_image_bytes, format='PNG') + return new_image_bytes.getvalue() diff --git a/big_vision/datasets/aokvqa/aokvqa.py b/big_vision/datasets/aokvqa/aokvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..497028110225a62cdab1b51e489a77f3d3719516 --- /dev/null +++ b/big_vision/datasets/aokvqa/aokvqa.py @@ -0,0 +1,182 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements the OKVQA dataset for TFDS. + +Download the required files from https://aokvqa.allenai.org/download.html: + +mkdir -p /tmp/tfds +cd /tmp/tfds/ +wget http://images.cocodataset.org/zips/train2017.zip +wget http://images.cocodataset.org/zips/val2017.zip +wget http://images.cocodataset.org/zips/test2017.zip +wget https://prior-datasets.s3.us-east-2.amazonaws.com/aokvqa/aokvqa_v1p0.tar.gz +unzip val2017.zip +unzip train2017.zip +unzip test2017.zip +tar xzf aokvqa_v1p0.tar.gz + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=aokvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('aokvqa', split='val', data_dir='/tmp/tfds') +""" + +import json +import os +from typing import Any +import numpy as np +import tensorflow_datasets as tfds + +_DESCRIPTION = """ +A-OKVQA addresses the task of VQA with outside knowledge. +It is a follow-up dataset of OKVQA. + +This version of the dataset contains: +- Questions + Answers + Multiple Choice Answers + Rationales from A-OKVQA. +- Images from COCO. +""" + +_CITATION = """ +@article{AOKVQA, + title={A-OKVQA: A Benchmark for Visual Question Answering using World Knowledge}, + author={Dustin Schwenk and Apoorv Khandelwal and Christopher Clark and Kenneth Marino and Roozbeh Mottaghi}, + journal={arXiv}, + year={2022}, +} +""" + +ANNOTATION_FILES = { + 'train': 'aokvqa_v1p0_train.json', + 'val': 'aokvqa_v1p0_val.json', + 'test': 'aokvqa_v1p0_test.json', +} + + +# When running locally (recommended), copy files as above an use these: +_AOKVQA_PATH = '/tmp/tfds' + + +class AOkVqa(tfds.core.GeneratorBasedBuilder): + """AOKVQA dataset for TFDS.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'ArrayRecord version.'} + MANUAL_DOWNLOAD_INSTRUCTIONS = """ + In manual_dir/ you should have a directory a_ok_vqa which contains the + following files and directories: + From the A-OKVQA dataset: + - aokvqa_v1p0_train.json + - aokvqa_v1p0_val.json + - aokvqa_v1p0_test.json + It also requires the COCO data files. + """ + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata.""" + features = tfds.features.FeaturesDict({ + 'image': tfds.features.Image(shape=(None, None, 3)), + 'image_id': tfds.features.Scalar(dtype=np.int64), + 'direct_answers': tfds.features.Sequence(tfds.features.Text()), + 'direct_answer_is_difficult': tfds.features.Scalar(dtype=np.bool_), + 'multiple_choice_possible_answers': # List of 4 possible answers. + tfds.features.Sequence(tfds.features.Text()), + 'multiple_choice_correct_idx': # Integer from 0-3. + tfds.features.Scalar(dtype=np.int32), + 'answer_rationales': tfds.features.Sequence(tfds.features.Text()), + 'question': tfds.features.Text(), + 'question_id': tfds.features.Text(), + }) + + return tfds.core.DatasetInfo( + builder=self, + features=features, + description=_DESCRIPTION, + supervised_keys=None, + homepage='https://okvqa.allenai.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager) -> ...: + """Call the function which defines the splits.""" + # data_dir = dl_manager.manual_dir + data_dir = _AOKVQA_PATH + return { + 'train': self._generate_examples(data_dir, 'train'), + 'val': self._generate_examples(data_dir, 'val'), + 'test': self._generate_examples(data_dir, 'test'), + } + + def _generate_examples(self, data_dir: str, split: str) -> ...: + annotations = get_annotations(data_dir, split) + + for question_id, feature_dict in annotations.items(): + image_id = feature_dict['image_id'] + + # Add image and GT segmentatio labels from total_transfer. + feature_dict['image'] = self.get_image_path(data_dir, split, image_id) + + # Add dummy features for several features in the test set. + if split not in ['train', 'val']: + assert split == 'test', f'Unknown split: {split}' + feature_dict['multiple_choice_correct_idx'] = -1 + feature_dict['direct_answers'] = [] + feature_dict['answer_rationales'] = [] + yield f'{question_id}', feature_dict + + def get_image_path(self, data_dir: str, split: str, image_id: int) -> str: + return f'{data_dir}/{split}2017/{image_id:012d}.jpg' + + +def get_annotations( + data_dir: str, split: str) -> dict[int, dict[str, Any]]: + """Return okvqa annotations (quesions and answers) as dictionary.""" + path = os.path.join(data_dir, ANNOTATION_FILES[split]) + with open(path) as f: + annotations = json.load(f) + + aokvqa_annotations = {} + for annotation in annotations: + # Sanity checks + assert len(annotation['choices']) == 4 + + question_id = annotation['question_id'] + + aokvqa_annotations[question_id] = { + 'image_id': annotation['image_id'], + 'direct_answer_is_difficult': annotation['difficult_direct_answer'], + 'multiple_choice_possible_answers': annotation['choices'], + 'question': annotation['question'], + 'question_id': annotation['question_id'], + } + + # Get answers and rationales for train and val only, not for test. + if split in ['train', 'val']: + assert len(annotation['direct_answers']) == 10 + assert len(annotation['rationales']) == 3 + + aokvqa_annotations[question_id]['direct_answers'] = annotation[ + 'direct_answers'] + aokvqa_annotations[question_id]['answer_rationales'] = annotation[ + 'rationales'] + aokvqa_annotations[question_id]['multiple_choice_correct_idx'] = ( + annotation['correct_choice_idx']) + + return aokvqa_annotations diff --git a/big_vision/datasets/chartqa/chartqa.py b/big_vision/datasets/chartqa/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..90748b691cd47b16b81ba8fdb733d26694963745 --- /dev/null +++ b/big_vision/datasets/chartqa/chartqa.py @@ -0,0 +1,122 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements CharQA in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: + + mkdir -p /tmp/data + wget -O /tmp/data/chartqa.zip https://huggingface.co/datasets/ahmed-masry/ChartQA/resolve/main/ChartQA%20Dataset.zip?download=true + unzip /tmp/data/chartqa.zip + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=chartqa + +Example to load: + + import tensorflow_datasets as tfds + dataset_augmented = tfds.load('chartqa/augmented', split='train', data_dir='/tmp/tfds') +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """ChartQA dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{masry-etal-2022-chartqa, + title = "{C}hart{QA}: A Benchmark for Question Answering about Charts with Visual and Logical Reasoning", + author = "Masry, Ahmed and + Do, Xuan Long and + Tan, Jia Qing and + Joty, Shafiq and + Hoque, Enamul", + editor = "Muresan, Smaranda and + Nakov, Preslav and + Villavicencio, Aline", + booktitle = "Findings of the Association for Computational Linguistics: ACL 2022", + month = may, + year = "2022", + address = "Dublin, Ireland", + publisher = "Association for Computational Linguistics", + url = "https://aclanthology.org/2022.findings-acl.177", + doi = "10.18653/v1/2022.findings-acl.177", + pages = "2263--2279", + abstract = "Charts are very popular for analyzing data. When exploring charts, people often ask a variety of complex reasoning questions that involve several logical and arithmetic operations. They also commonly refer to visual features of a chart in their questions. However, most existing datasets do not focus on such complex reasoning questions as their questions are template-based and answers come from a fixed-vocabulary. In this work, we present a large-scale benchmark covering 9.6K human-written questions as well as 23.1K questions generated from human-written chart summaries. To address the unique challenges in our benchmark involving visual and logical reasoning over charts, we present two transformer-based models that combine visual features and the data table of the chart in a unified way to answer questions. While our models achieve the state-of-the-art results on the previous datasets as well as on our benchmark, the evaluation also reveals several challenges in answering complex reasoning questions.", +} +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_CHARTQA_PATH = '/tmp/data/ChartQA Dataset/' + + +class ChartQAConfig(tfds.core.BuilderConfig): + """Configuration to build the dataset.""" + pass + + +class ChartQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for ChartQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + BUILDER_CONFIGS = [ + ChartQAConfig(name='human', description='Human set'), + ChartQAConfig(name='augmented', description='Augmented set'), + ] + + def _info(self): + """Returns the metadata.""" + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='png'), + 'question': tfds.features.Text(), + 'answer': tfds.features.Text(), + }), + homepage='https://github.com/vis-nlp/ChartQA', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split, self.builder_config.name) + for split in ('val', 'train', 'test')} + + def _generate_examples(self, split: str, source: str): + """Yields (key, example) tuples from test set.""" + annot_fname = os.path.join(_CHARTQA_PATH, split, f'{split}_{source}.json') + + with open(annot_fname, 'r') as f: + data = json.loads(f.read()) + + for idx, v in enumerate(data): + yield idx, { + 'question_id': idx, + 'image/filename': v['imgname'], + 'image': os.path.join(_CHARTQA_PATH, split, 'png', v['imgname']), + 'question': v['query'], + 'answer': v['label'], + } diff --git a/big_vision/datasets/coco35l/coco35l.py b/big_vision/datasets/coco35l/coco35l.py new file mode 100644 index 0000000000000000000000000000000000000000..0f390222506c8a16a0bcbe5eab495a2e7efa30d0 --- /dev/null +++ b/big_vision/datasets/coco35l/coco35l.py @@ -0,0 +1,154 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Generates COCO-35L in a TFDS-ready structure. + +First, download the captions from https://google.github.io/crossmodal-3600/ and the images from https://cocodataset.org/#download. +The coco Karpathy split is available at http://cs.stanford.edu/people/karpathy/deepimagesent/caption_datasets.zip: + mkdir -p /tmp/data/coco35l/images + wget https://storage.googleapis.com/crossmodal-3600/coco_mt_train.jsonl.bz2 -P /tmp/data/coco35l + wget https://storage.googleapis.com/crossmodal-3600/coco_mt_dev.jsonl.bz2 -P /tmp/data/coco35l + bzip2 -dk /tmp/data/coco35l/coco_mt_train.jsonl.bz2 /tmp/data/coco35l/coco_mt_dev.jsonl.bz2 + wget http://cs.stanford.edu/people/karpathy/deepimagesent/caption_datasets.zip -P /tmp/data/coco35l + unzip /tmp/data/coco35l/caption_datasets.zip -d /tmp/data/coco35l/ + wget http://images.cocodataset.org/zips/train2014.zip -P /tmp/data/coco35l/images + wget http://images.cocodataset.org/zips/val2014.zip -P /tmp/data/coco35l/images + unzip /tmp/data/coco35l/images/train2014.zip -d /tmp/data/coco35l/images/ + unzip /tmp/data/coco35l/images/val2014.zip -d /tmp/data/coco35l/images/ + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=coco35l + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load( + 'coco35l', split='dev_en', + data_dir='/tmp/tfds') +""" + +import json +import os.path + +import tensorflow_datasets as tfds + +_DESCRIPTION = """ +COCO image + captions, translated from English to 35 languages (English incl.). +""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{thapliyal-etal-2022-crossmodal, + title = "Crossmodal-3600: A Massively Multilingual Multimodal Evaluation Dataset", + author = "Thapliyal, Ashish V. and + Pont Tuset, Jordi and + Chen, Xi and + Soricut, Radu", + editor = "Goldberg, Yoav and + Kozareva, Zornitsa and + Zhang, Yue", + booktitle = "Proceedings of the 2022 Conference on Empirical Methods in Natural Language Processing", + month = dec, + year = "2022", + address = "Abu Dhabi, United Arab Emirates", + publisher = "Association for Computational Linguistics", + url = "https://aclanthology.org/2022.emnlp-main.45", + doi = "10.18653/v1/2022.emnlp-main.45", + pages = "715--729", +} +""" +# pylint: enable=line-too-long + + +_CAPTIONS_PATH = '/tmp/data/coco35l' +_IMAGES_PATH = '/tmp/data/mscoco/images' +_COCOCAPS_PATH = '/tmp/data/mscoco/dataset_coco.json' + +LANGUAGES = [ + 'ar', 'bn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', + 'he', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'mi', 'nl', 'no', 'pl', + 'pt', 'ro', 'ru', 'sv', 'sw', 'te', 'th', 'tr', 'uk', 'vi', 'zh', +] + + +class Coco35l(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for COCO-35L dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'image/id': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'captions': tfds.features.Sequence(tfds.features.Text()), + 'language': tfds.features.Text(), + }), + supervised_keys=None, + homepage='https://google.github.io/crossmodal-3600/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + splits = [] + for lang in LANGUAGES: + splits.extend([f'train_{lang}', f'dev_{lang}']) + return {split: self._generate_examples(split) for split in splits} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from dataset.""" + split, language = split.split('_') + + id_to_path = dict() + with open(_COCOCAPS_PATH, 'r') as f: + data = json.load(f)['images'] + for d in data: + id_to_path[d['cocoid']] = os.path.join( + _IMAGES_PATH, d['filepath'], d['filename'] + ) + + annot_fname = os.path.join(_CAPTIONS_PATH, f'coco_mt_{split}.jsonl') + data = {} + with open(annot_fname, 'r') as f: + for line in f: + j = json.loads(line) + image_id = f'{j["image_id"].split("_")[0]}_{language}' + if image_id not in data: + data[image_id] = [] + if language == 'en': + # COCO-35L was constructed from English into 35 other languages. + # To add English in our TFDS, we just select a language (eg. "de") to + # have each unique example, and add the corresponding source caption. + if j['trg_lang'] == 'de': + data[image_id].append(j['caption_tokenized']) + else: + if j['trg_lang'] == language: + data[image_id].append(j['translation_tokenized']) + + for image_id, captions in data.items(): + yield image_id, { + 'image/id': image_id, + 'image': id_to_path[int(image_id.split('_')[0])], + 'captions': captions, + 'language': language, + } diff --git a/big_vision/datasets/core.py b/big_vision/datasets/core.py new file mode 100644 index 0000000000000000000000000000000000000000..07d2a2c6814646908fc5133cb5a54aec6d3b57b3 --- /dev/null +++ b/big_vision/datasets/core.py @@ -0,0 +1,77 @@ +# Copyright 2024 Big Vision 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. + +"""Core data functions, dispatch calls to the requested dataset.""" +import importlib + + +# Note: intentionally not using ABC to avoid forcing implementation of every +# method, since one can imagine train-only datasets for example. +class DataSource: + """The API that any data source should implement.""" + + def get_tfdata(self, ordered, *, process_split=True, allow_cache=True): + """Creates this data object as a tf.data.Dataset. + + This will be called separately in each process, and it is up to the dataset + implementation to shard it accordingly if desired! + + Args: + ordered: if True, the dataset should use deterministic ordering, if False + it may have undefined ordering. Think of True == val, False == train. + process_split: if False then every process receives the entire dataset + (e.g. for evaluators running in a single process). + allow_cache: whether to allow caching the opened data or not. + + Returns: + A tf.data.Dataset object. + + Raises: + RuntimeError: if not implemented by the dataset, but called. + """ + raise RuntimeError("not implemented for {self.__class__.__name__}") + + @property + def total_examples(self): + """Returns number of examples in the dataset, regardless of sharding.""" + raise RuntimeError("not implemented for {self.__class__.__name__}") + + def num_examples_per_process(self): + """Returns a list of the numer of examples for each process. + + This is only needed for datasets that should go through make_for_inference. + + Returns: + Returns a list of the numer of examples for each process. + + Ideally, this would always be `[total() / nprocess] * nprocess`, but in + reality we can almost never perfectly shard a dataset across arbitrary + number of processes. + + One alternative option that can work in some cases is to not even shard + the dataset and thus return `[num_examples()] * nprocess. + + Raises: + RuntimeError: if not implemented by the dataset, but called. + """ + raise RuntimeError("not implemented for {self.__class__.__name__}") + + +def get(name, **kw): + if name.startswith("bv:"): + mod = importlib.import_module(f"big_vision.datasets.{name[3:]}") + return mod.DataSource(**kw) + else: + mod = importlib.import_module("big_vision.datasets.tfds") + return mod.DataSource(name, **kw) diff --git a/big_vision/datasets/countbenchqa/countbenchqa.py b/big_vision/datasets/countbenchqa/countbenchqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0994cae86b2ff4758dd4611c0ae4295c2d86b4e6 --- /dev/null +++ b/big_vision/datasets/countbenchqa/countbenchqa.py @@ -0,0 +1,164 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +"""Import CountBenchQA dataset (CountBench dataset with added QA annotations). + +It's small data, so simple to run locally. First, download all the data: + + mkdir /tmp/data/ ; cd /tmp/data + wget https://huggingface.co/datasets/nielsr/countbench/resolve/main/data/train-00000-of-00001-cf54c241ba947306.parquet + wget https://raw.githubusercontent.com/teaching-clip-to-count/teaching-clip-to-count.github.io/main/CountBench.json + +Then, update the PATHs below and run conversion locally like so: + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=countbenchqa + +The dataset contains 540 images so the dataset creation is very quick. + +There is a single split called huggingface to denote that the images come from +the hugginface parquet file. +""" + +import io +import json + +import numpy as np +import pandas as pd +import PIL +import tensorflow_datasets as tfds + + +# Huggingface dataset path; this is missing about 10% of the images. +_COUNTBENCH_PARQUET_PATH = '/tmp/data/train-00000-of-00001-cf54c241ba947306.parquet' +# Public path to the original CountBench JSON file. +_COUNTBENCH_JSON_PATH = '/tmp/data/CountBench.json' +# VQA annotations +_QA_JSON_PATH = 'countbenchqa/data/countbench_paired_questions.json' + +_DESCRIPTION = """ +CountBench: We introduce a new object counting benchmark called CountBench, + automatically curated (and manually verified) from the publicly available + LAION-400M image-text dataset. CountBench contains a total of 540 images + containing between two and ten instances of a particular object, where their + corresponding captions reflect this number. + +CountBenchQA: Each image is paired with a manually generated question about the + number of objects in the image to turn CountBench into a VQA task. +""" + +_CITATION = """ +@article{beyer2024paligemma, + title={{PaliGemma: A versatile 3B VLM for transfer}}, + author={Lucas Beyer and Andreas Steiner and André Susano Pinto and Alexander Kolesnikov and Xiao Wang and Daniel Salz and Maxim Neumann and Ibrahim Alabdulmohsin and Michael Tschannen and Emanuele Bugliarello and Thomas Unterthiner and Daniel Keysers and Skanda Koppula and Fangyu Liu and Adam Grycner and Alexey Gritsenko and Neil Houlsby and Manoj Kumar and Keran Rong and Julian Eisenschlos and Rishabh Kabra and Matthias Bauer and Matko Bošnjak and Xi Chen and Matthias Minderer and Paul Voigtlaender and Ioana Bica and Ivana Balazevic and Joan Puigcerver and Pinelopi Papalampidi and Olivier Henaff and Xi Xiong and Radu Soricut and Jeremiah Harmsen and Xiaohua Zhai}, + year={2024}, + journal={arXiv preprint arXiv:2407.07726} +} + +@article{paiss2023countclip, + title={{Teaching CLIP to Count to Ten}}, + author={Paiss, Roni and Ephrat, Ariel and Tov, Omer and Zada, Shiran and Mosseri, Inbar and Irani, Michal and Dekel, Tali}, + year={2023}, + journal={arXiv preprint arXiv:2302.12066} +} +""" + +_HOMEPAGE = 'https://teaching-clip-to-count.github.io/' + + +class CountbenchQA(tfds.core.GeneratorBasedBuilder): + """Create CountbenchQA dataset.""" + + VERSION = tfds.core.Version('1.2.0') + RELEASE_NOTES = {'1.1.0': 'Add `huggingface` split.', + '1.2.0': 'Fix image loading for `huggingface` split.'} + MANUAL_DOWNLOAD_INSTRUCTIONS = """ + There are two parts which should be downloaded: + * Countbench from Huggingface + * Questions found in `data/countbench_paired_questions.json` + """ + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata.""" + features = tfds.features.FeaturesDict({ + 'image': tfds.features.Image(shape=(None, None, 3)), + 'image_id': tfds.features.Scalar(dtype=np.int32), + 'question': tfds.features.Text(), + 'text': tfds.features.Text(), + 'image_url': tfds.features.Text(), + 'number': tfds.features.Scalar(dtype=np.int32), + }) + + return tfds.core.DatasetInfo( + builder=self, + features=features, + description=_DESCRIPTION, + supervised_keys=None, + homepage=_HOMEPAGE, + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Call the function which defines the splits.""" + del dl_manager + return { + 'huggingface': self._generate_examples(split='huggingface'), + } + + def _generate_examples_hf(self): + """Generate examples from Huggingface parquet file. + + Note that the parquet file provided on Huggingface is missing about 10% + of the images as can be verified by running + ``` + import pyarrow.parquet as pq + with open(_COUNTBENCH_PARQUET_PATH, 'rb') as f: + x = pq.read_table(f) + sum([x['image'][i].is_valid for i in range(len(x['image']))]) # result: 491 + ``` + + Yields: + An index and a dictionary with features. + """ + with open(_COUNTBENCH_PARQUET_PATH, 'rb') as f: + df = pd.read_parquet(f) + + with open(_QA_JSON_PATH, 'r') as fq: + df_question = pd.read_json(fq) + + df['question'] = df_question + + for idx, row in df.iterrows(): + # Some entries have no image. + if row['image'] is None: + continue + image = np.array(PIL.Image.open(io.BytesIO(row['image']['bytes']))) + if len(image.shape) != 3: + continue # Filter out one bad image. + countbenchqa_dict = { + 'image': image, + 'image_id': idx, + 'question': row['question'], + 'text': row['text'], + 'image_url': row['image_url'], + 'number': row['number'], + } + yield idx, countbenchqa_dict + + def _generate_examples(self, split: str): + if split == 'huggingface': + yield from self._generate_examples_hf() + else: + raise ValueError(f'Unknown split: {split}') diff --git a/big_vision/datasets/countbenchqa/data/countbench_paired_questions.json b/big_vision/datasets/countbenchqa/data/countbench_paired_questions.json new file mode 100644 index 0000000000000000000000000000000000000000..bb684fac07600d00e624d203878736e5e3e90e1d --- /dev/null +++ b/big_vision/datasets/countbenchqa/data/countbench_paired_questions.json @@ -0,0 +1 @@ +[{"question": "How many headsets are there in the image?"}, {"question": "How many light bulbs are there in the image?"}, {"question": "How many prints are there in the image?"}, {"question": "How many arrows are there in the image?"}, {"question": "How many spoons are there in the image?"}, {"question": "How many girls are there in the image?"}, {"question": "How many parrots are there in the image?"}, {"question": "How many coloring pages are there in the image?"}, {"question": "How many food containers are there in the image?"}, {"question": "How many birdhouse patterns are there in the image?"}, {"question": "How many sofas are there in the image?"}, {"question": "How many waterlilies are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many golfers are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many outfits are there in the image?"}, {"question": "How many pigs are there in the image?"}, {"question": "How many cars are there in the image?"}, {"question": "How many aum symbols are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many buttons are there in the image?"}, {"question": "How many rackets are there in the image?"}, {"question": "How many pots are there in the image?"}, {"question": "How many stars are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many kids are there in the image?"}, {"question": "How many moth silhouettes are there in the image?"}, {"question": "How many pumpkin candles are there in the image?"}, {"question": "How many essential oils are there in the image?"}, {"question": "How many stencils are there in the image?"}, {"question": "How many text boxes are there in the image?"}, {"question": "How many basketball players are there in the image?"}, {"question": "How many photos are there in the image?"}, {"question": "How many forks are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many bags are there in the image?"}, {"question": "How many couples are there in the image?"}, {"question": "How many weights are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many fish are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many clocks are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many chests are there in the image?"}, {"question": "How many stars are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many globe icons are there in the image?"}, {"question": "How many posters are there in the image?"}, {"question": "How many socks are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many snails are there in the image?"}, {"question": "How many crochet potholders are there in the image?"}, {"question": "How many christmas cards are there in the image?"}, {"question": "How many double beds are there in the image?"}, {"question": "How many baseball players are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many baseball players are there in the image?"}, {"question": "How many cars are there in the image?"}, {"question": "How many trees are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many tree trunk cuts are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many individual earrings are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many tomatoes are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many prints are there in the image?"}, {"question": "How many ice creams are there in the image?"}, {"question": "How many plates are there in the image?"}, {"question": "How many sumo wrestlers are there in the image?"}, {"question": "How many compositions are there in the image?"}, {"question": "How many PVC vinyls are there in the image?"}, {"question": "How many photos of fruit are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many mirrors are there in the image?"}, {"question": "How many groomsmen are there in the image?"}, {"question": "How many posters are there in the image?"}, {"question": "How many pumpkins are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many kittens are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many boots are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many moais are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many cats are there in the image?"}, {"question": "How many wallpaper variants are there in the image?"}, {"question": "How many nail polishes are there in the image?"}, {"question": "How many bumble bees are there in the image?"}, {"question": "How many tickets are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many wine bottles are there in the image?"}, {"question": "How many silhouettes of couples are there in the image?"}, {"question": "How many owls are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many flower pots are there in the image?"}, {"question": "How many coins are there in the image?"}, {"question": "How many placemats are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many cars are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many pairs of socks are there in the image?"}, {"question": "How many blinds are there in the image?"}, {"question": "How many floral patterns are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many bicycles are there in the image?"}, {"question": "How many dwarfs are there in the image?"}, {"question": "How many stickers are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many stamps are there in the image?"}, {"question": "How many pumpkins are there in the image?"}, {"question": "How many game cartridges are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many glasses are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many coins are there in the image?"}, {"question": "How many gears are there in the image?"}, {"question": "How many flowers are there in the image?"}, {"question": "How many crip packages are there in the image?"}, {"question": "How many bridesmaids are there in the image?"}, {"question": "How many apples are there in the image?"}, {"question": "How many bowls are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many photographs are there in the image?"}, {"question": "How many warning signs are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many cyclists are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many giraffes are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many male nurses are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many couples are there in the image?"}, {"question": "How many cars are there in the image?"}, {"question": "How many apples are there in the image?"}, {"question": "How many smartphones are there in the image?"}, {"question": "How many roses are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many greeting cards are there in the image?"}, {"question": "How many guitars are there in the image?"}, {"question": "How many ironman suits are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many CDs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many pot holders are there in the image?"}, {"question": "How many stamps are there in the image?"}, {"question": "How many bookmarks are there in the image?"}, {"question": "How many portraits are there in the image?"}, {"question": "How many girls are there in the image?"}, {"question": "How many labels are there in the image?"}, {"question": "How many mandalas are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many peacocks are there in the image?"}, {"question": "How many roses are there in the image?"}, {"question": "How many cars are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many canvases are there in the image?"}, {"question": "How many cards are there in the image?"}, {"question": "How many bell pepper halves are there in the image?"}, {"question": "How many pigs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many newspapers are there in the image?"}, {"question": "How many leggings are there in the image?"}, {"question": "How many medals are there in the image?"}, {"question": "How many patterns are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many doors are there in the image?"}, {"question": "How many pairs of socks are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many backgrounds are there in the image?"}, {"question": "How many images of dogs are there in the image?"}, {"question": "How many broadheads are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many petals does each flower have in this image?"}, {"question": "How many crayons are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many caps are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many frames are there in the image?"}, {"question": "How many animals are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many cats are there in the image?"}, {"question": "How many sconces are there in the image?"}, {"question": "How many spoons are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many planes are there in the image?"}, {"question": "How many cats are there in the image?"}, {"question": "How many sketches are there in the image?"}, {"question": "How many trees are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many napkins are there in the image?"}, {"question": "How many dogs are there in the image?"}, {"question": "How many kids are there in the image?"}, {"question": "How many hearts are there in the image?"}, {"question": "How many apples are there in the image?"}, {"question": "How many post-its are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many books are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many sofas are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many girls are there in the image?"}, {"question": "How many sketches are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many moons are there in the image?"}, {"question": "How many labels are there in the image?"}, {"question": "How many cylinders are there in the image?"}, {"question": "How many silhouettes of couples are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many dogs are there in the image?"}, {"question": "How many people on stage are there in the image?"}, {"question": "How many lambs are there in the image?"}, {"question": "How many violins are there in the image?"}, {"question": "How many armchairs are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many photos are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many moais are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many glasses are there in the image?"}, {"question": "How many sinks are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many buildings are there in the image?"}, {"question": "How many flyers are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many pizza slices are there in the image?"}, {"question": "How many stamps are there in the image?"}, {"question": "How many stickers are there in the image?"}, {"question": "How many gifts are there in the image?"}, {"question": "How many bowls are there in the image?"}, {"question": "How many onesies are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many posters are there in the image?"}, {"question": "How many cards are there in the image?"}, {"question": "How many portraits are there in the image?"}, {"question": "How many poinsettias are there in the image?"}, {"question": "How many chicken thighs are there in the image?"}, {"question": "How many glass windows are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many baskets are there in the image?"}, {"question": "How many tulips are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many stuffed animals are there in the image?"}, {"question": "How many keychains are there in the image?"}, {"question": "How many photos are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many adults are there in the image?"}, {"question": "How many tulips are there in the image?"}, {"question": "How many frames are there in the image?"}, {"question": "How many samosas are there in the image?"}, {"question": "How many strawberries are there in the image?"}, {"question": "How many cocktails are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many wineglasses are there in the image?"}, {"question": "How many goblets are there in the image?"}, {"question": "How many prints are there in the image?"}, {"question": "How many flowers are there in the image?"}, {"question": "How many zebras are there in the image?"}, {"question": "How many paint brushes are there in the image?"}, {"question": "How many prints are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many glasses are there in the image?"}, {"question": "How many sunglasses are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many bottle caps are there in the image?"}, {"question": "How many prints are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many books are there in the image?"}, {"question": "How many animals are there in the image?"}, {"question": "How many eyeshadows are there in the image?"}, {"question": "How many keychains are there in the image?"}, {"question": "How many pairs of earrings are there in the image?"}, {"question": "How many canisters are there in the image?"}, {"question": "How many bags are there in the image?"}, {"question": "How many baking trays are there in the image?"}, {"question": "How many diamonds are there in the image?"}, {"question": "How many portraits are there in the image?"}, {"question": "How many framed images are there in the image?"}, {"question": "How many flags are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many framed pictures are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many croissants are there in the image?"}, {"question": "How many Manikins are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many labels are there in the image?"}, {"question": "How many people in the foreground are there in the image?"}, {"question": "How many armchairs are there in the image?"}, {"question": "How many cups are there in the image?"}, {"question": "How many helicopters are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many buttons are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many Cookies are there in the image?"}, {"question": "How many sculptures are there in the image?"}, {"question": "How many school uniforms are there in the image?"}, {"question": "How many sculptures are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many packages are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many stories does this cottage have?"}, {"question": "How many gift cards are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many beers are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many planes are there in the image?"}, {"question": "How many cactus pots are there in the image?"}, {"question": "How many smartphones are there in the image?"}, {"question": "How many picture frames are there in the image?"}, {"question": "How many elephants are there in the image?"}, {"question": "How many guitars are there in the image?"}, {"question": "How many samurai are there in the image?"}, {"question": "How many ghosts are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many vases are there in the image?"}, {"question": "How many sets of headphones are there in the image?"}, {"question": "How many pandas are there in the image?"}, {"question": "How many books are there in the image?"}, {"question": "How many stickers are there in the image?"}, {"question": "How many rings are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many dragon balls are there in the image?"}, {"question": "How many tanks are there in the image?"}, {"question": "How many students are there in the image?"}, {"question": "How many cups are there in the image?"}, {"question": "How many cubs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many cushions are there in the image?"}, {"question": "How many shoes are there in the image?"}, {"question": "How many beers are there in the image?"}, {"question": "How many wine glasses are there in the image?"}, {"question": "How many cards are there in the image?"}, {"question": "How many boots are there in the image?"}, {"question": "How many stickers are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many butterflies are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many butterflies are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many hexagons are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many football players are there in the image?"}, {"question": "How many gifts are there in the image?"}, {"question": "How many light bulbs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many pots are there in the image?"}, {"question": "How many pumpkins are there in the image?"}, {"question": "How many owls are there in the image?"}, {"question": "How many doctors are there in the image?"}, {"question": "How many pigs are there in the image?"}, {"question": "How many pillars are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many pumpkins are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many roses are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many illustrations are there in the image?"}, {"question": "How many photos are there in the image?"}, {"question": "How many butterflies are there in the image?"}, {"question": "How many spoons are there in the image?"}, {"question": "How many potato spreads are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many wall arts are there in the image?"}, {"question": "How many covers are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many soldiers are there in the image?"}, {"question": "How many posters are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many patterns are there in the image?"}, {"question": "How many dogs are there in the image?"}, {"question": "How many pennies are there in the image?"}, {"question": "How many windows are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many geese are there in the image?"}, {"question": "How many tickets are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many cases are there in the image?"}, {"question": "How many people are in the foreground of this image?"}, {"question": "How many glasses are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many cups are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many kittens are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many scones are there in the image?"}, {"question": "How many schoolgirls are there in the image?"}, {"question": "How many padlocks are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many windows are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many dogs are there in the image?"}, {"question": "How many groomsmen are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many glasses are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many coins are there in the image?"}, {"question": "How many trees are there in the image?"}, {"question": "How many turtles are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many frames are there in the image?"}, {"question": "How many firescreens are there in the image?"}, {"question": "How many bowls are there in the image?"}, {"question": "How many stickers are there in the image?"}, {"question": "How many archaic mirrors are there in the image?"}, {"question": "How many dogs are there in the image?"}, {"question": "How many candles are there in the image?"}, {"question": "How many paint brushes are there in the image?"}, {"question": "How many forks are there in the image?"}, {"question": "How many figurines are there in the image?"}, {"question": "How many owls are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many fried eggs are there in the image?"}, {"question": "How many guitars are there in the image?"}, {"question": "How many signs are there in the image?"}, {"question": "How many watches are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many kittens are there in the image?"}, {"question": "How many kids are there in the image?"}, {"question": "How many kittens are there in the image?"}, {"question": "How many toys are there in the image?"}, {"question": "How many pairs of socks are there in the image?"}, {"question": "How many pairs of socks are there in the image?"}, {"question": "How many men are there in the image?"}, {"question": "How many stars are there in the image?"}, {"question": "How many quarters are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many diamonds are there in the image?"}, {"question": "How many moais are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many coins are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many banners are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many flags are there in the image?"}, {"question": "How many frames are there in the image?"}, {"question": "How many contestants are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many globes are there in the image?"}, {"question": "How many animals are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many buttons are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many piles of candy are there in the image?"}, {"question": "How many pictures are there in the image?"}, {"question": "How many plates are there in the image?"}, {"question": "How many calendars are there in the image?"}, {"question": "How many oranges are there in the image?"}, {"question": "How many puppies are there in the image?"}, {"question": "How many buffalos are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many christmas balls are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many armchairs are there in the image?"}, {"question": "How many frames are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many soldiers are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many cards are there in the image?"}, {"question": "How many colored tiles are there in the image?"}, {"question": "How many trapezoids are there in the image?"}, {"question": "How many pastries are there in the image?"}, {"question": "How many plants are there in the image?"}, {"question": "How many place mats are there in the image?"}, {"question": "How many symbols are there in the image?"}, {"question": "How many mazes are there in the image?"}, {"question": "How many tea bags are there in the image?"}, {"question": "How many photos are there in the image?"}, {"question": "How many children are there in the image?"}, {"question": "How many starfish are there in the image?"}, {"question": "How many mugs are there in the image?"}, {"question": "How many bottles are there in the image?"}, {"question": "How many eggs are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many balls are there in the image?"}, {"question": "How many paper bags are there in the image?"}, {"question": "How many garage doors are there in the image?"}, {"question": "How many silhouettes are there in the image?"}, {"question": "How many couples are there in the image?"}, {"question": "How many dice are there in the image?"}, {"question": "How many women are there in the image?"}, {"question": "How many icons are there in the image?"}, {"question": "How many wooden spoons are there in the image?"}, {"question": "How many people are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many chairs are there in the image?"}, {"question": "How many beds are there in the image?"}, {"question": "How many chairs are there in the image?"}] \ No newline at end of file diff --git a/big_vision/datasets/docvqa/docvqa.py b/big_vision/datasets/docvqa/docvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..f1a496ca24c4b2c5714c10d155585cb6194bd783 --- /dev/null +++ b/big_vision/datasets/docvqa/docvqa.py @@ -0,0 +1,110 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements DocVQA in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk. +An account will be needed in https://rrc.cvc.uab.es/?ch=17&com=downloads and +from there the task annotations and images can be fetched separatedly. + + mkdir -p /tmp/data/docvqa + + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=docvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('docvqa', split='val', data_dir='/tmp/tfds') +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """DocVQA dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@article{DBLP:journals/corr/abs-2007-00398, + author = {Minesh Mathew and + Dimosthenis Karatzas and + R. Manmatha and + C. V. Jawahar}, + title = {DocVQA: {A} Dataset for {VQA} on Document Images}, + journal = {CoRR}, + volume = {abs/2007.00398}, + year = {2020}, + url = {https://arxiv.org/abs/2007.00398}, + eprinttype = {arXiv}, + eprint = {2007.00398}, + timestamp = {Mon, 06 Jul 2020 15:26:01 +0200}, + biburl = {https://dblp.org/rec/journals/corr/abs-2007-00398.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_DOCVQA_PATH = '/tmp/data/docvqa/' + + +class DocVQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for DocVQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='png'), + 'question': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage='https://www.docvqa.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in ('val', 'train', 'test')} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from split.""" + suffix = '' if split == 'test' else '_withQT' + with open(os.path.join(_DOCVQA_PATH, f'{split}_v1.0{suffix}.json')) as f: + data = json.load(f) + for v in data['data']: + question_id = v['questionId'] + yield question_id, { + 'question_id': question_id, + 'image/filename': v['image'], + 'image': os.path.join(_DOCVQA_PATH, split, v['image']), + 'question': v['question'], + 'answers': v.get('answers', []), + } diff --git a/big_vision/datasets/gqa/gqa.py b/big_vision/datasets/gqa/gqa.py new file mode 100644 index 0000000000000000000000000000000000000000..ea137cbfa41923fca712ee40977f75b855a7966d --- /dev/null +++ b/big_vision/datasets/gqa/gqa.py @@ -0,0 +1,167 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Generates GQA in a TFDS-ready structure, using Beam. + +Instructions below are to generate the dataset with a *local* Beam pipeline. +It's advisable to run the Beam job on Google Cloud Dataflow, see + https://www.tensorflow.org/datasets/beam_datasets. +for more details, which would significantly speed up generation. This would +involve uploading the locally downloaded data to a GCS bucket, and then +adding in the Beam pipeline options and your GCP/GCS bucket details +to the `tfds build` command below (as detailed in the link). + +First, copy the data to local disk: + + mkdir -p /tmp/data/gqa + wget -O /tmp/data/gqa/question1.2.zip https://downloads.cs.stanford.edu/nlp/data/gqa/questions1.2.zip?download=true + unzip /tmp/data/gqa/question1.2.zip + mv /tmp/data/gqa/question1.2/* /tmp/data/gqa/ + wget -O /tmp/data/gqa/images.zip https://downloads.cs.stanford.edu/nlp/data/gqa/images.zip?download=true + unzip /tmp/data/gqa/images.zip + +Then, run conversion (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=gqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('gqa', split='testdev_balanced', data_dir='/tmp/tfds') + +Some statistics: + train_all: 14305356 examples + train_balanced: 943000 examples + val_all: 2011853 examples + val_balanced: 132062 examples + testdev_all: 172174 examples + testdev_balanced: 12578 examples +""" +import glob +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """GQA: Visual Reasoning in the Real World.""" + +# pylint: disable=line-too-long +_CITATION = """ +@article{DBLP:journals/corr/abs-2306-14610, + author = {Drew Hudson and + Christopher Manning}, + title = {GQA: A New Dataset for Real-World Visual Reasoning and Compositional Question Answering}, + journal = {CVPR}, + volume = {abs/1902.09506}, + year = {2019}, + url = {https://doi.org/10.48550/arXiv.1902.09506}, + doi = {10.48550/arXiv.1902.09506}, + eprinttype = {arXiv}, + eprint = {1902.09506}, + timestamp = {Tue, 25 Jun 2019 00:00:00 +0100}, + biburl = {https://dblp.org/rec/journals/corr/abs-1902-09506}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} +""" +# pylint: enable=line-too-long + + +_DATA_PATH = '/tmp/data/gqa/' + + +class GQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for GQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'example_id': tfds.features.Scalar(np.int64), + 'image/id': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question': tfds.features.Text(), + 'answer': tfds.features.Text(), + 'full_answer': tfds.features.Text(), + 'is_balanced': tfds.features.Scalar(np.bool_), + }), + homepage='https://cs.stanford.edu/people/dorarad/gqa/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + splits = [ + # 'debug', + 'train_all', + 'train_balanced', + 'testdev_all', + 'testdev_balanced', + 'val_all', + 'val_balanced', + 'challenge_all', + 'challenge_balanced', + ] + return {split: self._generate_examples(split) for split in splits} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from dataset.""" + if split == 'train_all': + train_json_dir = os.path.join(_DATA_PATH, 'train_all_questions', '*.json') + json_files = glob.glob(train_json_dir) + else: + json_files = [os.path.join(_DATA_PATH, f'{split}_questions.json')] + + def _prepare_data(json_path): + with open(os.path.join(json_path)) as f: + annotations = json.load(f) + return [(k, v) for k, v in annotations.items()] + + def _process_example(entry): + question_id, question_data = entry + image_id = question_data['imageId'] + image_path = os.path.join(_DATA_PATH, 'images', f'{image_id}.jpg') + answer = question_data['answer'] if 'answer' in question_data else '' + if 'fullAnswer' in question_data: + full_answer = question_data['fullAnswer'] + else: + full_answer = '' + + example = { + 'example_id': question_id, + 'image/id': image_id, + 'image': image_path, + 'question': question_data['question'], + 'answer': answer, + 'full_answer': full_answer, + 'is_balanced': question_data['isBalanced'], + } + return question_id, example + + beam = tfds.core.lazy_imports.apache_beam + return ( + beam.Create(json_files) + | beam.FlatMap(_prepare_data) + | beam.Reshuffle() + | beam.Map(_process_example) + ) diff --git a/big_vision/datasets/imagenet/class_names.py b/big_vision/datasets/imagenet/class_names.py new file mode 100644 index 0000000000000000000000000000000000000000..490594ebc2a23d508d21102cb7d0596c88396fb3 --- /dev/null +++ b/big_vision/datasets/imagenet/class_names.py @@ -0,0 +1,270 @@ +# Copyright 2024 Big Vision 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. + +"""Imagenet class names.""" + +# Copied from +# https://github.com/openai/CLIP/blob/main/notebooks/Prompt_Engineering_for_ImageNet.ipynb +CLIP_IMAGENET_CLASS_NAMES = [ + 'tench', 'goldfish', 'great white shark', 'tiger shark', 'hammerhead shark', + 'electric ray', 'stingray', 'rooster', 'hen', 'ostrich', 'brambling', + 'goldfinch', 'house finch', 'junco', 'indigo bunting', 'American robin', + 'bulbul', 'jay', 'magpie', 'chickadee', 'American dipper', + 'kite (bird of prey)', 'bald eagle', 'vulture', 'great grey owl', + 'fire salamander', 'smooth newt', 'newt', 'spotted salamander', 'axolotl', + 'American bullfrog', 'tree frog', 'tailed frog', 'loggerhead sea turtle', + 'leatherback sea turtle', 'mud turtle', 'terrapin', 'box turtle', + 'banded gecko', 'green iguana', 'Carolina anole', + 'desert grassland whiptail lizard', 'agama', 'frilled-necked lizard', + 'alligator lizard', 'Gila monster', 'European green lizard', 'chameleon', + 'Komodo dragon', 'Nile crocodile', 'American alligator', 'triceratops', + 'worm snake', 'ring-necked snake', 'eastern hog-nosed snake', + 'smooth green snake', 'kingsnake', 'garter snake', 'water snake', + 'vine snake', 'night snake', 'boa constrictor', 'African rock python', + 'Indian cobra', 'green mamba', 'sea snake', 'Saharan horned viper', + 'eastern diamondback rattlesnake', 'sidewinder rattlesnake', 'trilobite', + 'harvestman', 'scorpion', 'yellow garden spider', 'barn spider', + 'European garden spider', 'southern black widow', 'tarantula', + 'wolf spider', 'tick', 'centipede', 'black grouse', 'ptarmigan', + 'ruffed grouse', 'prairie grouse', 'peafowl', 'quail', 'partridge', + 'african grey parrot', 'macaw', 'sulphur-crested cockatoo', 'lorikeet', + 'coucal', 'bee eater', 'hornbill', 'hummingbird', 'jacamar', 'toucan', + 'duck', 'red-breasted merganser', 'goose', 'black swan', 'tusker', + 'echidna', 'platypus', 'wallaby', 'koala', 'wombat', 'jellyfish', + 'sea anemone', 'brain coral', 'flatworm', 'nematode', 'conch', 'snail', + 'slug', 'sea slug', 'chiton', 'chambered nautilus', 'Dungeness crab', + 'rock crab', 'fiddler crab', 'red king crab', 'American lobster', + 'spiny lobster', 'crayfish', 'hermit crab', 'isopod', 'white stork', + 'black stork', 'spoonbill', 'flamingo', 'little blue heron', 'great egret', + 'bittern bird', 'crane bird', 'limpkin', 'common gallinule', + 'American coot', 'bustard', 'ruddy turnstone', 'dunlin', 'common redshank', + 'dowitcher', 'oystercatcher', 'pelican', 'king penguin', 'albatross', + 'grey whale', 'killer whale', 'dugong', 'sea lion', 'Chihuahua', + 'Japanese Chin', 'Maltese', 'Pekingese', 'Shih Tzu', 'King Charles Spaniel', + 'Papillon', 'toy terrier', 'Rhodesian Ridgeback', 'Afghan Hound', + 'Basset Hound', 'Beagle', 'Bloodhound', 'Bluetick Coonhound', + 'Black and Tan Coonhound', 'Treeing Walker Coonhound', 'English foxhound', + 'Redbone Coonhound', 'borzoi', 'Irish Wolfhound', 'Italian Greyhound', + 'Whippet', 'Ibizan Hound', 'Norwegian Elkhound', 'Otterhound', 'Saluki', + 'Scottish Deerhound', 'Weimaraner', 'Staffordshire Bull Terrier', + 'American Staffordshire Terrier', 'Bedlington Terrier', 'Border Terrier', + 'Kerry Blue Terrier', 'Irish Terrier', 'Norfolk Terrier', 'Norwich Terrier', + 'Yorkshire Terrier', 'Wire Fox Terrier', 'Lakeland Terrier', + 'Sealyham Terrier', 'Airedale Terrier', 'Cairn Terrier', + 'Australian Terrier', 'Dandie Dinmont Terrier', 'Boston Terrier', + 'Miniature Schnauzer', 'Giant Schnauzer', 'Standard Schnauzer', + 'Scottish Terrier', 'Tibetan Terrier', 'Australian Silky Terrier', + 'Soft-coated Wheaten Terrier', 'West Highland White Terrier', 'Lhasa Apso', + 'Flat-Coated Retriever', 'Curly-coated Retriever', 'Golden Retriever', + 'Labrador Retriever', 'Chesapeake Bay Retriever', + 'German Shorthaired Pointer', 'Vizsla', 'English Setter', 'Irish Setter', + 'Gordon Setter', 'Brittany dog', 'Clumber Spaniel', + 'English Springer Spaniel', 'Welsh Springer Spaniel', 'Cocker Spaniel', + 'Sussex Spaniel', 'Irish Water Spaniel', 'Kuvasz', 'Schipperke', + 'Groenendael dog', 'Malinois', 'Briard', 'Australian Kelpie', 'Komondor', + 'Old English Sheepdog', 'Shetland Sheepdog', 'collie', 'Border Collie', + 'Bouvier des Flandres dog', 'Rottweiler', 'German Shepherd Dog', + 'Dobermann', 'Miniature Pinscher', 'Greater Swiss Mountain Dog', + 'Bernese Mountain Dog', 'Appenzeller Sennenhund', 'Entlebucher Sennenhund', + 'Boxer', 'Bullmastiff', 'Tibetan Mastiff', 'French Bulldog', 'Great Dane', + 'St. Bernard', 'husky', 'Alaskan Malamute', 'Siberian Husky', 'Dalmatian', + 'Affenpinscher', 'Basenji', 'pug', 'Leonberger', 'Newfoundland dog', + 'Great Pyrenees dog', 'Samoyed', 'Pomeranian', 'Chow Chow', 'Keeshond', + 'brussels griffon', 'Pembroke Welsh Corgi', 'Cardigan Welsh Corgi', + 'Toy Poodle', 'Miniature Poodle', 'Standard Poodle', + 'Mexican hairless dog (xoloitzcuintli)', 'grey wolf', 'Alaskan tundra wolf', + 'red wolf or maned wolf', 'coyote', 'dingo', 'dhole', 'African wild dog', + 'hyena', 'red fox', 'kit fox', 'Arctic fox', 'grey fox', 'tabby cat', + 'tiger cat', 'Persian cat', 'Siamese cat', 'Egyptian Mau', 'cougar', 'lynx', + 'leopard', 'snow leopard', 'jaguar', 'lion', 'tiger', 'cheetah', + 'brown bear', 'American black bear', 'polar bear', 'sloth bear', 'mongoose', + 'meerkat', 'tiger beetle', 'ladybug', 'ground beetle', 'longhorn beetle', + 'leaf beetle', 'dung beetle', 'rhinoceros beetle', 'weevil', 'fly', 'bee', + 'ant', 'grasshopper', 'cricket insect', 'stick insect', 'cockroach', + 'praying mantis', 'cicada', 'leafhopper', 'lacewing', 'dragonfly', + 'damselfly', 'red admiral butterfly', 'ringlet butterfly', + 'monarch butterfly', 'small white butterfly', 'sulphur butterfly', + 'gossamer-winged butterfly', 'starfish', 'sea urchin', 'sea cucumber', + 'cottontail rabbit', 'hare', 'Angora rabbit', 'hamster', 'porcupine', + 'fox squirrel', 'marmot', 'beaver', 'guinea pig', 'common sorrel horse', + 'zebra', 'pig', 'wild boar', 'warthog', 'hippopotamus', 'ox', + 'water buffalo', 'bison', 'ram (adult male sheep)', 'bighorn sheep', + 'Alpine ibex', 'hartebeest', 'impala (antelope)', 'gazelle', + 'arabian camel', 'llama', 'weasel', 'mink', 'European polecat', + 'black-footed ferret', 'otter', 'skunk', 'badger', 'armadillo', + 'three-toed sloth', 'orangutan', 'gorilla', 'chimpanzee', 'gibbon', + 'siamang', 'guenon', 'patas monkey', 'baboon', 'macaque', 'langur', + 'black-and-white colobus', 'proboscis monkey', 'marmoset', + 'white-headed capuchin', 'howler monkey', 'titi monkey', + 'Geoffroy\'s spider monkey', 'common squirrel monkey', 'ring-tailed lemur', + 'indri', 'Asian elephant', 'African bush elephant', 'red panda', + 'giant panda', 'snoek fish', 'eel', 'silver salmon', 'rock beauty fish', + 'clownfish', 'sturgeon', 'gar fish', 'lionfish', 'pufferfish', 'abacus', + 'abaya', 'academic gown', 'accordion', 'acoustic guitar', + 'aircraft carrier', 'airliner', 'airship', 'altar', 'ambulance', + 'amphibious vehicle', 'analog clock', 'apiary', 'apron', 'trash can', + 'assault rifle', 'backpack', 'bakery', 'balance beam', 'balloon', + 'ballpoint pen', 'Band-Aid', 'banjo', 'baluster / handrail', 'barbell', + 'barber chair', 'barbershop', 'barn', 'barometer', 'barrel', 'wheelbarrow', + 'baseball', 'basketball', 'bassinet', 'bassoon', 'swimming cap', + 'bath towel', 'bathtub', 'station wagon', 'lighthouse', 'beaker', + 'military hat (bearskin or shako)', 'beer bottle', 'beer glass', + 'bell tower', 'baby bib', 'tandem bicycle', 'bikini', 'ring binder', + 'binoculars', 'birdhouse', 'boathouse', 'bobsleigh', 'bolo tie', + 'poke bonnet', 'bookcase', 'bookstore', 'bottle cap', 'hunting bow', + 'bow tie', 'brass memorial plaque', 'bra', 'breakwater', 'breastplate', + 'broom', 'bucket', 'buckle', 'bulletproof vest', 'high-speed train', + 'butcher shop', 'taxicab', 'cauldron', 'candle', 'cannon', 'canoe', + 'can opener', 'cardigan', 'car mirror', 'carousel', 'tool kit', + 'cardboard box / carton', 'car wheel', 'automated teller machine', + 'cassette', 'cassette player', 'castle', 'catamaran', 'CD player', 'cello', + 'mobile phone', 'chain', 'chain-link fence', 'chain mail', 'chainsaw', + 'storage chest', 'chiffonier', 'bell or wind chime', 'china cabinet', + 'Christmas stocking', 'church', 'movie theater', 'cleaver', + 'cliff dwelling', 'cloak', 'clogs', 'cocktail shaker', 'coffee mug', + 'coffeemaker', 'spiral or coil', 'combination lock', 'computer keyboard', + 'candy store', 'container ship', 'convertible', 'corkscrew', 'cornet', + 'cowboy boot', 'cowboy hat', 'cradle', 'construction crane', 'crash helmet', + 'crate', 'infant bed', 'Crock Pot', 'croquet ball', 'crutch', 'cuirass', + 'dam', 'desk', 'desktop computer', 'rotary dial telephone', 'diaper', + 'digital clock', 'digital watch', 'dining table', 'dishcloth', 'dishwasher', + 'disc brake', 'dock', 'dog sled', 'dome', 'doormat', 'drilling rig', 'drum', + 'drumstick', 'dumbbell', 'Dutch oven', 'electric fan', 'electric guitar', + 'electric locomotive', 'entertainment center', 'envelope', + 'espresso machine', 'face powder', 'feather boa', 'filing cabinet', + 'fireboat', 'fire truck', 'fire screen', 'flagpole', 'flute', + 'folding chair', 'football helmet', 'forklift', 'fountain', 'fountain pen', + 'four-poster bed', 'freight car', 'French horn', 'frying pan', 'fur coat', + 'garbage truck', 'gas mask or respirator', 'gas pump', 'goblet', 'go-kart', + 'golf ball', 'golf cart', 'gondola', 'gong', 'gown', 'grand piano', + 'greenhouse', 'radiator grille', 'grocery store', 'guillotine', 'hair clip', + 'hair spray', 'half-track', 'hammer', 'hamper', 'hair dryer', + 'hand-held computer', 'handkerchief', 'hard disk drive', 'harmonica', + 'harp', 'combine harvester', 'hatchet', 'holster', 'home theater', + 'honeycomb', 'hook', 'hoop skirt', 'gymnastic horizontal bar', + 'horse-drawn vehicle', 'hourglass', 'iPod', 'clothes iron', + 'carved pumpkin', 'jeans', 'jeep', 'T-shirt', 'jigsaw puzzle', 'rickshaw', + 'joystick', 'kimono', 'knee pad', 'knot', 'lab coat', 'ladle', 'lampshade', + 'laptop computer', 'lawn mower', 'lens cap', 'letter opener', 'library', + 'lifeboat', 'lighter', 'limousine', 'ocean liner', 'lipstick', + 'slip-on shoe', 'lotion', 'music speaker', 'loupe magnifying glass', + 'sawmill', 'magnetic compass', 'messenger bag', 'mailbox', 'tights', + 'one-piece bathing suit', 'manhole cover', 'maraca', 'marimba', 'mask', + 'matchstick', 'maypole', 'maze', 'measuring cup', 'medicine cabinet', + 'megalith', 'microphone', 'microwave oven', 'military uniform', 'milk can', + 'minibus', 'miniskirt', 'minivan', 'missile', 'mitten', 'mixing bowl', + 'mobile home', 'ford model t', 'modem', 'monastery', 'monitor', 'moped', + 'mortar and pestle', 'graduation cap', 'mosque', 'mosquito net', 'vespa', + 'mountain bike', 'tent', 'computer mouse', 'mousetrap', 'moving van', + 'muzzle', 'metal nail', 'neck brace', 'necklace', 'baby pacifier', + 'notebook computer', 'obelisk', 'oboe', 'ocarina', 'odometer', 'oil filter', + 'pipe organ', 'oscilloscope', 'overskirt', 'bullock cart', 'oxygen mask', + 'product packet / packaging', 'paddle', 'paddle wheel', 'padlock', + 'paintbrush', 'pajamas', 'palace', 'pan flute', 'paper towel', 'parachute', + 'parallel bars', 'park bench', 'parking meter', 'railroad car', 'patio', + 'payphone', 'pedestal', 'pencil case', 'pencil sharpener', 'perfume', + 'Petri dish', 'photocopier', 'plectrum', 'Pickelhaube', 'picket fence', + 'pickup truck', 'pier', 'piggy bank', 'pill bottle', 'pillow', + 'ping-pong ball', 'pinwheel', 'pirate ship', 'drink pitcher', 'block plane', + 'planetarium', 'plastic bag', 'plate rack', 'farm plow', 'plunger', + 'Polaroid camera', 'pole', 'police van', 'poncho', 'pool table', + 'soda bottle', 'plant pot', 'potter\'s wheel', 'power drill', 'prayer rug', + 'printer', 'prison', 'missile', 'projector', 'hockey puck', 'punching bag', + 'purse', 'quill', 'quilt', 'race car', 'racket', 'radiator', 'radio', + 'radio telescope', 'rain barrel', 'recreational vehicle', + 'fishing casting reel', 'reflex camera', 'refrigerator', 'remote control', + 'restaurant', 'revolver', 'rifle', 'rocking chair', 'rotisserie', 'eraser', + 'rugby ball', 'ruler measuring stick', 'sneaker', 'safe', 'safety pin', + 'salt shaker', 'sandal', 'sarong', 'saxophone', 'scabbard', + 'weighing scale', 'school bus', 'schooner', 'scoreboard', 'CRT monitor', + 'screw', 'screwdriver', 'seat belt', 'sewing machine', 'shield', + 'shoe store', 'shoji screen / room divider', 'shopping basket', + 'shopping cart', 'shovel', 'shower cap', 'shower curtain', 'ski', + 'balaclava ski mask', 'sleeping bag', 'slide rule', 'sliding door', + 'slot machine', 'snorkel', 'snowmobile', 'snowplow', 'soap dispenser', + 'soccer ball', 'sock', 'solar thermal collector', 'sombrero', 'soup bowl', + 'keyboard space bar', 'space heater', 'space shuttle', 'spatula', + 'motorboat', 'spider web', 'spindle', 'sports car', 'spotlight', 'stage', + 'steam locomotive', 'through arch bridge', 'steel drum', 'stethoscope', + 'scarf', 'stone wall', 'stopwatch', 'stove', 'strainer', 'tram', + 'stretcher', 'couch', 'stupa', 'submarine', 'suit', 'sundial', 'sunglasses', + 'sunglasses', 'sunscreen', 'suspension bridge', 'mop', 'sweatshirt', + 'swim trunks / shorts', 'swing', 'electrical switch', 'syringe', + 'table lamp', 'tank', 'tape player', 'teapot', 'teddy bear', 'television', + 'tennis ball', 'thatched roof', 'front curtain', 'thimble', + 'threshing machine', 'throne', 'tile roof', 'toaster', 'tobacco shop', + 'toilet seat', 'torch', 'totem pole', 'tow truck', 'toy store', 'tractor', + 'semi-trailer truck', 'tray', 'trench coat', 'tricycle', 'trimaran', + 'tripod', 'triumphal arch', 'trolleybus', 'trombone', 'hot tub', + 'turnstile', 'typewriter keyboard', 'umbrella', 'unicycle', 'upright piano', + 'vacuum cleaner', 'vase', 'vaulted or arched ceiling', 'velvet fabric', + 'vending machine', 'vestment', 'viaduct', 'violin', 'volleyball', + 'waffle iron', 'wall clock', 'wallet', 'wardrobe', 'military aircraft', + 'sink', 'washing machine', 'water bottle', 'water jug', 'water tower', + 'whiskey jug', 'whistle', 'hair wig', 'window screen', 'window shade', + 'Windsor tie', 'wine bottle', 'airplane wing', 'wok', 'wooden spoon', + 'wool', 'split-rail fence', 'shipwreck', 'sailboat', 'yurt', 'website', + 'comic book', 'crossword', 'traffic or street sign', 'traffic light', + 'dust jacket', 'menu', 'plate', 'guacamole', 'consomme', 'hot pot', + 'trifle', 'ice cream', 'popsicle', 'baguette', 'bagel', 'pretzel', + 'cheeseburger', 'hot dog', 'mashed potatoes', 'cabbage', 'broccoli', + 'cauliflower', 'zucchini', 'spaghetti squash', 'acorn squash', + 'butternut squash', 'cucumber', 'artichoke', 'bell pepper', 'cardoon', + 'mushroom', 'Granny Smith apple', 'strawberry', 'orange', 'lemon', 'fig', + 'pineapple', 'banana', 'jackfruit', 'cherimoya (custard apple)', + 'pomegranate', 'hay', 'carbonara', 'chocolate syrup', 'dough', 'meatloaf', + 'pizza', 'pot pie', 'burrito', 'red wine', 'espresso', 'tea cup', 'eggnog', + 'mountain', 'bubble', 'cliff', 'coral reef', 'geyser', 'lakeshore', + 'promontory', 'sandbar', 'beach', 'valley', 'volcano', 'baseball player', + 'bridegroom', 'scuba diver', 'rapeseed', 'daisy', 'yellow lady\'s slipper', + 'corn', 'acorn', 'rose hip', 'horse chestnut seed', 'coral fungus', + 'agaric', 'gyromitra', 'stinkhorn mushroom', 'earth star fungus', + 'hen of the woods mushroom', 'bolete', 'corn cob', 'toilet paper' +] + +# ImageNet-A and ImageNet-R do not use the full label space of ImageNet. +# These were copied from third_party/py/robustness_metrics/datasets/tfds.py +# Kudos to mjlm@ who helped us notice this. +IMAGENET_A_LABELSET = [ + 6, 11, 13, 15, 17, 22, 23, 27, 30, 37, 39, 42, 47, 50, 57, 70, 71, 76, 79, + 89, 90, 94, 96, 97, 99, 105, 107, 108, 110, 113, 124, 125, 130, 132, 143, + 144, 150, 151, 207, 234, 235, 254, 277, 283, 287, 291, 295, 298, 301, 306, + 307, 308, 309, 310, 311, 313, 314, 315, 317, 319, 323, 324, 326, 327, 330, + 334, 335, 336, 347, 361, 363, 372, 378, 386, 397, 400, 401, 402, 404, 407, + 411, 416, 417, 420, 425, 428, 430, 437, 438, 445, 456, 457, 461, 462, 470, + 472, 483, 486, 488, 492, 496, 514, 516, 528, 530, 539, 542, 543, 549, 552, + 557, 561, 562, 569, 572, 573, 575, 579, 589, 606, 607, 609, 614, 626, 627, + 640, 641, 642, 643, 658, 668, 677, 682, 684, 687, 701, 704, 719, 736, 746, + 749, 752, 758, 763, 765, 768, 773, 774, 776, 779, 780, 786, 792, 797, 802, + 803, 804, 813, 815, 820, 823, 831, 833, 835, 839, 845, 847, 850, 859, 862, + 870, 879, 880, 888, 890, 897, 900, 907, 913, 924, 932, 933, 934, 937, 943, + 945, 947, 951, 954, 956, 957, 959, 971, 972, 980, 981, 984, 986, 987, 988, +] + +# Also check out https://github.com/hendrycks/imagenet-r/blob/master/eval.py +IMAGENET_R_LABELSET = [ + 1, 2, 4, 6, 8, 9, 11, 13, 22, 23, 26, 29, 31, 39, 47, 63, 71, 76, 79, 84, + 90, 94, 96, 97, 99, 100, 105, 107, 113, 122, 125, 130, 132, 144, 145, 147, + 148, 150, 151, 155, 160, 161, 162, 163, 171, 172, 178, 187, 195, 199, 203, + 207, 208, 219, 231, 232, 234, 235, 242, 245, 247, 250, 251, 254, 259, 260, + 263, 265, 267, 269, 276, 277, 281, 288, 289, 291, 292, 293, 296, 299, 301, + 308, 309, 310, 311, 314, 315, 319, 323, 327, 330, 334, 335, 337, 338, 340, + 341, 344, 347, 353, 355, 361, 362, 365, 366, 367, 368, 372, 388, 390, 393, + 397, 401, 407, 413, 414, 425, 428, 430, 435, 437, 441, 447, 448, 457, 462, + 463, 469, 470, 471, 472, 476, 483, 487, 515, 546, 555, 558, 570, 579, 583, + 587, 593, 594, 596, 609, 613, 617, 621, 629, 637, 657, 658, 701, 717, 724, + 763, 768, 774, 776, 779, 780, 787, 805, 812, 815, 820, 824, 833, 847, 852, + 866, 875, 883, 889, 895, 907, 928, 931, 932, 933, 934, 936, 937, 943, 945, + 947, 948, 949, 951, 953, 954, 957, 963, 965, 967, 980, 981, 983, 988, +] diff --git a/big_vision/datasets/infovqa/infovqa.py b/big_vision/datasets/infovqa/infovqa.py new file mode 100644 index 0000000000000000000000000000000000000000..4e6500548954b706ab0ebd203589871cca898b24 --- /dev/null +++ b/big_vision/datasets/infovqa/infovqa.py @@ -0,0 +1,141 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements InfoVqa in TFDS structure. + +First, download and unzip the dataset from https://rrc.cvc.uab.es/?ch=17 +and place it in /tmp/data/infovqa. + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd third_party/py/big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=infovqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('infovqa', split='train', data_dir='/tmp/tfds') + +Dataset splits: + train: 23946 examples/questions (4406 images) + val: 2801 examples/questions (500 images) + test: 3288 examples/questions (579 images) (no answers) + +Recommended training splits: + train: train[:95%] (22749 examples/questions) + minitrain: train[:5%] (1197 examples/questions) + minival: train[95%:] (1197 examples/questions) + eval: val (2801 examples/questions) + +Note that according to task description in +https://rrc.cvc.uab.es/?ch=17&com=tasks: + - Order of items in a multi span answer does not matter. Therefore, we include + all permutations of the answer in the val split. + - Answers are not case sensitive. We leave it to the user to lower case + answers if they want to. +""" +import itertools +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """InfographicVQA dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{Mathew_2022, + title={InfographicVQA}, + url={http://dx.doi.org/10.1109/WACV51458.2022.00264}, + DOI={10.1109/wacv51458.2022.00264}, + booktitle={2022 IEEE/CVF Winter Conference on Applications of Computer Vision (WACV)}, + publisher={IEEE}, + author={Mathew, Minesh and Bagal, Viraj and Tito, Ruben and Karatzas, Dimosthenis and Valveny, Ernest and Jawahar, C. V.}, + year={2022}, + month=jan } +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_INFOVQA_PATH = '/tmp/data/infovqa/' +_ANNOTATIONS = { + 'train': 'infographicsVQA_train_v1.0.json', + 'val': 'infographicsVQA_val_v1.0_withQT.json', + 'test': 'infographicsVQA_test_v1.0.json', + } + + +class Infovqa(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for infovqa dataset.""" + + VERSION = tfds.core.Version('1.1.0') + RELEASE_NOTES = { + '1.0.0': 'First release.', + '1.1.0': 'Add multi-span permutations to the val split answers.', + } + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage='https://www.docvqa.org/datasets/infographicvqa', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in ('train', 'val', 'test')} + + def _generate_examples(self, split): + """Yields (key, example) tuples from test set.""" + annot_fname = os.path.join(_INFOVQA_PATH, _ANNOTATIONS[split]) + with open(annot_fname, 'r') as f: + data = json.loads(f.read()) + + for x in data['data']: + yield x['questionId'], { + 'question_id': x['questionId'], + 'filename': x['image_local_name'], + 'image': os.path.join(_INFOVQA_PATH, 'images', x['image_local_name']), + 'question': x['question'], + 'answers': maybe_permute(x.get('answers', []), split), + } + + +def maybe_permute(answers, split): + if split != 'val': + return answers + new_answers = [] + for x in answers: + if ', ' in x: # Create all permutations. + # The first element remains the same. + new_answers.extend([', '.join(y) + for y in itertools.permutations(x.split(', '))]) + else: + new_answers.append(x) + return new_answers diff --git a/big_vision/datasets/jsonl.py b/big_vision/datasets/jsonl.py new file mode 100644 index 0000000000000000000000000000000000000000..719deba2b25987a4b9d58b56474e420cb5b1e706 --- /dev/null +++ b/big_vision/datasets/jsonl.py @@ -0,0 +1,177 @@ +# Copyright 2024 Big Vision 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. + +"""Simple data input from .jsonl files.""" + +import hashlib +import json +from multiprocessing.pool import ThreadPool +import os +import tempfile +import urllib.request + +from absl import logging +import big_vision.datasets.core as ds_core +import jax +import numpy as np +import overrides +import tensorflow as tf + + +def cached_download(url, dest=None, verbose=True): + """Download `url` to local file and return path to that, but with caching.""" + # NOTE: there is a small chance of saving corrupted data if the process is + # interrupted in the middle of writing the file. Then, reading in the input + # pipeline will fail, and the fix is to nuke the temp folder. + + # Compute a temp name based on the URL, so we can check if we already + # downloaded it before. + dest = dest or os.path.join(tempfile.gettempdir(), "bv") + os.makedirs(dest, exist_ok=True) + dest = os.path.join(dest, hashlib.md5(url.encode()).hexdigest()) + + # NOTE: we should use last-modified header to know whether to re-download. + if os.path.isfile(dest): + return dest + + if verbose: + print(f"\rRetrieving {url} into {dest}", end="", flush=True) + + with urllib.request.urlopen(url) as f: + data = f.read() + with open(dest, "wb+") as f: + f.write(data) + return dest + + +class DataSource(ds_core.DataSource): + """.jsonl DataSource.""" + + def __init__(self, fname, *, fopen_keys=(), download_keys=(), + start=0, stop=float("inf")): + """Create data-source that's jsonl + data files (eg images). + + This correctly supports multi-host in that each host only reads a subset of + the dataset automatically. However, currently, all hosts download all items + if `download_keys` is specified. TODO: b/lbeyer - This can be improved. + + Args: + fname: str, the path to the jsonl file that holds the dataset. + fopen_keys: collection of str or dict, the keys in the dataset whose + string value actually is a file-path that should be opened and read, + and its content is what goes into the batch (eg image filenames + commonly ["image"]). + If a dict, the values are folders prefixed to the filenames. + Supports gs:// for reading from buckets. + download_keys: collection of str, the keys in the dataset whose string + value actually is a URL from which the file should be downloaded first. + files are downloaded to a persistent tmp folder using the URL hash as + filename. If the file already exists, the download is skipped. + Must be a subset of `fopen_keys`. + start: int, index of the first row to use; use for slicing the data. + stop: int or inf, index of the row after the last one to use. + + Note: + This simple data input does not allow for nested/hierarchical values, + or in any way more complicated values like vectors. Use TFDS for that. + + The way start/stop arguments are used is as in list slicing[start:stop]. + """ + self.examples = [] + + with tf.io.gfile.GFile(fname) as f: + for i, line in enumerate(f): + if (start or 0) <= i < (stop or float("inf")): + try: + self.examples.append(json.loads(line)) + except json.decoder.JSONDecodeError as e: + raise ValueError(f"Invalid JSON in line {i}:\n{line}") from e + + if download_keys: + for k in download_keys: + assert k in fopen_keys, ( + f"{k} in download_keys but missing from fopen_keys {fopen_keys}") + + # TODO: b/lbeyer - use info from trainer instead, move that to utils. + logging.info( # pylint: disable=logging-fstring-interpolation + f"\u001b[33mNOTE\u001b[0m: Downloading {download_keys} " + f"for dataset {fname} ({len(self.examples)} examples) ...") + + def _dl_one(ex): + for k in download_keys: + ex[k] = cached_download(ex[k]) + + ThreadPool(100).map(_dl_one, self.examples) + print("Done") + logging.info("\u001b[33mNOTE\u001b[0m: Done downloading.") + + # Normalize. + if isinstance(fopen_keys, (list, tuple)): + self.fopen_keys = {k: "" for k in fopen_keys} + else: + self.fopen_keys = fopen_keys or {} + + # We need to apply fopen path prefix here already, because doing so while + # actually reading the files in TF, things are symbolic :( + for ex in self.examples: + for k, dirname in self.fopen_keys.items(): + ex[k] = os.path.join(dirname, ex[k]) + + def _indices(self, *, process_split=True, process_index=None): + indices = np.arange(len(self.examples)) + + if not process_split: + return list(indices) + + pid = jax.process_index() if process_index is None else process_index + return list(np.array_split(indices, jax.process_count())[pid]) + + @overrides.overrides + def get_tfdata(self, ordered=False, *, process_split=True, allow_cache=True): + del allow_cache # We don't cache anything anyways. + assert not process_split or len(self.examples) >= jax.process_count(), ( + "Process splitting the data with fewer examples than processes!?") + + my_idxs = self._indices(process_split=process_split) + if not ordered: + np.random.shuffle(my_idxs) + + dataset = tf.data.Dataset.from_generator( + generator=lambda: ({"id": str(i), **self.examples[i]} for i in my_idxs), + output_signature={ + "id": _guess_signature("0"), + **{k: _guess_signature(v) for k, v in self.examples[0].items()}, + }) + + def _read_files(example): + for k in self.fopen_keys: + example[k] = tf.io.read_file(example[k]) + return example + dataset = dataset.map(_read_files) + + return dataset + + @property + @overrides.overrides + def total_examples(self): + return len(self.examples) + + @overrides.overrides + def num_examples_per_process(self): + return [len(self._indices(process_index=pid)) + for pid in range(jax.process_count())] + + +def _guess_signature(value): + return tf.TensorSpec.from_tensor(tf.constant(value)) diff --git a/big_vision/datasets/nocaps/nocaps.py b/big_vision/datasets/nocaps/nocaps.py new file mode 100644 index 0000000000000000000000000000000000000000..fab90082bf3852100c59230ee23ddeb923144792 --- /dev/null +++ b/big_vision/datasets/nocaps/nocaps.py @@ -0,0 +1,160 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements nocaps val/test set in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: + + mkdir -p /tmp/data/nocaps_data + cd /tmp/data/nocaps_data + wget https://s3.amazonaws.com/open-images-dataset/tar/test.tar.gz + wget https://s3.amazonaws.com/open-images-dataset/tar/validation.tar.gz + curl -O https://nocaps.s3.amazonaws.com/nocaps_val_4500_captions.json + curl -O https://s3.amazonaws.com/nocaps/nocaps_test_image_info.json + + mkdir -p /tmp/data/nocaps_data/Images + tar -xf validation.tar.gz -C Images + rm validation.tar.gz + tar -xf test.tar.gz -C Images + rm test.tar.gz + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=nocaps + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('nocaps', split='val', data_dir='/tmp/tfds') +""" +import collections +import json +import os + +from absl import logging +import numpy as np +import tensorflow as tf +import tensorflow_datasets as tfds + + +_DESCRIPTION = """Nocaps dataset.""" + +_CITATION = ( + '@inproceedings{agrawal2019nocaps,' + 'title={nocaps: novel object captioning at scale},' + 'author={Agrawal, Harsh and Desai, Karan and Wang, Yufei and Chen, Xinlei' + 'and Jain, Rishabh and Johnson, Mark and Batra, Dhruv and Parikh, Devi' + 'and Lee, Stefan and Anderson, Peter},' + 'booktitle={ICCV},' + 'pages={8948--8957},' + 'year={2019}}') + +# When running locally (recommended), copy files as above an use these: +_FILEPATH = '/tmp/data/nocaps_data/Images/' +_VAL_FILES = '/tmp/data/nocaps_data/nocaps_val_4500_captions.json' +_TEST_FILES = '/tmp/data/nocaps_data/nocaps_test_image_info.json' + + +class NoCaps(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for nocaps dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = { + '1.0.0': 'Initial release.', + } + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata. + + (tfds.core.DatasetInfo object) + These are the features of your dataset like images, labels, etc. + """ + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'image/id': tf.int64, + 'image_filepath': tfds.features.Text(), + 'url': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'texts': tfds.features.Sequence(tfds.features.Text()), + }), + # If there's a common (input, target) tuple from the + # features, specify them here. They'll be used if + # `as_supervised=True` in `builder.as_dataset`. + supervised_keys=None, # Set to `None` to disable + homepage='https://nocaps.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + def group_by_id(data, image_dir): + id2caps = collections.defaultdict(list) + for ex in data.get('annotations', []): + id2caps[ex['image_id']].append(ex['caption']) + + id_to_example = {} + for ex in data['images']: + id_to_example[ex['id']] = { + 'image/id': ex['id'], + 'image_filepath': os.path.join( + _FILEPATH, image_dir, ex['file_name']), + 'url': ex['coco_url'], + 'image': os.path.join(_FILEPATH, image_dir, ex['file_name']), + 'texts': id2caps[ex['id']] if ex['id'] in id2caps else ['N/A'], + } + return id_to_example + + # Returns the Dict[split names, Iterator[Key, Example]] + with open(_VAL_FILES) as f: + val_data = group_by_id(json.load(f), 'validation') + with open(_TEST_FILES) as f: + test_data = group_by_id(json.load(f), 'test') + return { + 'val': self._generate_examples(val_data), + 'test': self._generate_examples(test_data), + } + + def _generate_examples(self, data): + """Generate a tf.Example object. + + This contains the image, objects, attributes, regions and relationships. + + Args: + data: a dictionary with the image/id. + + Yields: + (key, example) tuples from dataset. The example has format specified in + the above DatasetInfo. + """ + for k, v in data.items(): + try: + # Jpeg decode test to check early errors. The decoded images are not + # used, instead we rely on the default tfds.features.Image function. + unused_image = tf.io.read_file(v['image_filepath']) + unused_image = np.array(tf.image.decode_jpeg(unused_image)) + except tf.errors.InvalidArgumentError: + # Unable to read image, skip this image and output download link. + logging.error('Unable to decode: curl -O %s', v['url']) + continue + except tf.errors.NotFoundError: + # Unable to read image, skip this image and output download link. + logging.error('File not found: curl -O %s', v['url']) + continue + + yield k, v diff --git a/big_vision/datasets/okvqa/okvqa.py b/big_vision/datasets/okvqa/okvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9a5631c440bdca28627f656c87fdb4b8b3f7164e --- /dev/null +++ b/big_vision/datasets/okvqa/okvqa.py @@ -0,0 +1,213 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements the OKVQA dataset for TFDS. + +Download the required files from https://okvqa.allenai.org/download.html: + +mkdir -p /tmp/tfds +cd /tmp/tfds/ +wget http://images.cocodataset.org/zips/train2014.zip +wget http://images.cocodataset.org/zips/val2014.zip +wget https://okvqa.allenai.org/static/data/mscoco_train2014_annotations.json.zip +wget https://okvqa.allenai.org/static/data/mscoco_val2014_annotations.json.zip +wget https://okvqa.allenai.org/static/data/OpenEnded_mscoco_train2014_questions.json.zip +wget https://okvqa.allenai.org/static/data/OpenEnded_mscoco_val2014_questions.json.zip +unzip val2014.zip +unzip train2014.zip +unzip OpenEnded_mscoco_train2014_questions.json.zip +unzip OpenEnded_mscoco_val2014_questions.json.zip +unzip mscoco_train2014_annotations.json.zip +unzip mscoco_val2014_annotations.json.zip + +Then, run conversion locally (make sure to install tensorflow-datasets for the +`tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=okvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('okvqa', split='val', data_dir='/tmp/tfds') +""" + +import json +import os +from typing import Any +import numpy as np +import tensorflow_datasets as tfds + +_DESCRIPTION = """ +OKVQA addresses the task of VQA with outside knowledge. +This version of the dataset contains: +- Questions + Answers from OKVQA. +- Images from COCO. +""" + +_CITATION = """ +@InProceedings{okvqa, +author = {Kenneth Marino and Mohammad Rastegari and Ali Farhadi and Roozbeh Mottaghi}, +title = {OK-VQA: A Visual Question Answering Benchmark Requiring External Knowledge}, +booktitle = {Conference on Computer Vision and Pattern Recognition (CVPR)}, +year = {2019}, +} +""" + +ANNOTATION_FILE = { + 'train': 'mscoco_train2014_annotations.json', + 'val': 'mscoco_val2014_annotations.json', +} +QUESTIONS_FILE = { + 'train': 'OpenEnded_mscoco_train2014_questions.json', + 'val': 'OpenEnded_mscoco_val2014_questions.json', +} +QUESTION_TYPES = { + 'one': 'Vehicles and Transportation', + 'two': 'Brands, Companies and Products', + 'three': 'Objects, Material and Clothing', + 'four': 'Sports and Recreation', + 'five': 'Cooking and Food', + 'six': 'Geography, History, Language and Culture', + 'seven': 'People and Everyday life', + 'eight': 'Plants and Animals', + 'nine': 'Science and Technology', + 'ten': 'Weather and Climate', + 'other': 'Other', +} + + +# When running locally (recommended), copy files as above an use these: +_OKVQA_PATH = '/media/scratch/okvqa' + + +class OkVqa(tfds.core.GeneratorBasedBuilder): + """Import COCO dataset for OKVQA with KAT features.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'Changed to array record format.'} + MANUAL_DOWNLOAD_INSTRUCTIONS = """ + In manual_dir/ you should have a directory okvqa which contains the + following files and directories: + From the OKVQA dataset: + - mscoco_train2014_annotations.json + - mscoco_val2014_annotations.json + - OpenEnded_mscoco_train2014_questions.json + - OpenEnded_mscoco_val2014_questions.json + - train2014.zip + - val2014.zip + """ + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata.""" + features = tfds.features.FeaturesDict({ + 'image': tfds.features.Image(shape=(None, None, 3)), + 'image_id': tfds.features.Scalar(dtype=np.int64), + 'answer_type': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + 'answers_confidence': tfds.features.Tensor(shape=[10], dtype=np.bool_), + 'answers_raw': tfds.features.Sequence(tfds.features.Text()), + 'question_id': tfds.features.Scalar(dtype=np.int64), + 'question_type': tfds.features.Text(), + 'question_type_readable': tfds.features.Text(), + 'question': tfds.features.Text(), + }) + + return tfds.core.DatasetInfo( + builder=self, + features=features, + description=_DESCRIPTION, + supervised_keys=None, + homepage='https://okvqa.allenai.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager) -> ...: + """Call the function which defines the splits.""" + # data_dir = dl_manager.manual_dir + data_dir = _OKVQA_PATH + return { + 'train': self._generate_examples(data_dir, 'train'), + 'val': self._generate_examples(data_dir, 'val'), + } + + def _generate_examples(self, data_dir: str, split: str) -> ...: + annotations = get_okvqa_annotations(data_dir, split) + + for question_id, annotation in annotations.items(): + image_id = annotation['image_id'] + + # Sanity check. + if len(annotation['answers']) != 10: + num_answers = len(annotation['answers']) + raise ValueError( + f'The number of answers for {image_id} is not 10 but {num_answers}') + + feature_dict = { + 'image': self.get_image_path(data_dir, split, image_id), + 'image_id': image_id, + 'answer_type': annotation['answer_type'], + 'answers': [a['answer'] for a in annotation['answers']], + 'answers_confidence': _get_answer_confidence(annotation['answers']), + 'answers_raw': [a['raw_answer'] for a in annotation['answers']], + 'question_id': annotation['question_id'], + 'question_type': annotation['question_type'], + 'question_type_readable': QUESTION_TYPES[annotation['question_type']], + 'question': annotation['question'], + } + yield f'{question_id}', feature_dict + + def get_image_path(self, data_dir: str, split: str, image_id: int) -> str: + subdir = {'train': 'train2014', 'val': 'val2014'}[split] + return f'{data_dir}/{subdir}/COCO_{subdir}_{image_id:012d}.jpg' + + +def _get_answer_confidence(answers: list[dict[str, str]]) -> np.ndarray: + """Get OKVQA answer confidences as bool.""" + confidences = [] + for a in answers: + confidence = a['answer_confidence'] + if confidence == 'yes': + confidences.append(True) + elif confidence == 'no': + confidences.append(False) + else: + raise ValueError(f'Unknown confidence: {confidence}') + return np.array(confidences, dtype=bool) + + +def _read_json( + data_dir: str, file: str, key: str +) -> dict[int, dict[str, Any]]: + with open(os.path.join(data_dir, file)) as f: + data = json.load(f) + questions = {d['question_id']: d for d in data[key]} + return questions + + +def get_okvqa_annotations( + data_dir: str, split: str +) -> dict[int, dict[str, Any]]: + """Return okvqa annotations (quesions and answers) as dictionary.""" + questions = _read_json(data_dir, QUESTIONS_FILE[split], 'questions') + annotations = _read_json(data_dir, ANNOTATION_FILE[split], 'annotations') + + assert len(annotations) == len(questions) + for question_id, question in questions.items(): + assert question['image_id'] == annotations[question_id]['image_id'] + assert question['question_id'] == annotations[question_id]['question_id'] + annotations[question_id]['question'] = question['question'] + + return annotations diff --git a/big_vision/datasets/pope/pope.py b/big_vision/datasets/pope/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..3f266d6ce21d218186736e434db328744a377389 --- /dev/null +++ b/big_vision/datasets/pope/pope.py @@ -0,0 +1,145 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements POPE test-set in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: +First download json files from https://github.com/AoiDragon/POPE; then download +MSCOCO (val 2014) images from https://cocodataset.org/#download + + mkdir -p /tmp/data/pope/ + mkdir -p /tmp/data/pope/pope/ + mkdir -p /tmp/data/pope/images/ + git clone https://github.com/AoiDragon/POPE.git + cp POPE/output/coco/* /tmp/data/pope/pope/ + wget http://images.cocodataset.org/zips/val2014.zip + unzip val2014.zip + cp -r val2014/ /tmp/data/pope/images/ + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=pope + +Example to load: + + import tensorflow_datasets as tfds + dataset_random = tfds.load('pope/pope_random', split='test', data_dir='/tmp/tfds') + dataset_popular = tfds.load('pope/pope_popular', split='test', data_dir='/tmp/tfds') + dataset_adversarial = tfds.load('pope/pope_adversarial', split='test', data_dir='/tmp/tfds') + +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """POPE dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{li-etal-2023-evaluating, + title = "Evaluating Object Hallucination in Large Vision-Language Models", + author = "Li, Yifan and + Du, Yifan and + Zhou, Kun and + Wang, Jinpeng and + Zhao, Xin and + Wen, Ji-Rong", + editor = "Bouamor, Houda and + Pino, Juan and + Bali, Kalika", + booktitle = "Proceedings of the 2023 Conference on Empirical Methods in Natural Language Processing", + month = dec, + year = "2023", + address = "Singapore", + publisher = "Association for Computational Linguistics", + url = "https://aclanthology.org/2023.emnlp-main.20", + doi = "10.18653/v1/2023.emnlp-main.20", + pages = "292--305", + abstract = "Inspired by the superior language abilities of large language models (LLM), large vision-language models (LVLM) have been recently proposed by integrating powerful LLMs for improving the performance on complex multimodal tasks. Despite the promising progress on LVLMs, we find that they suffer from object hallucinations, i.e., they tend to generate objects inconsistent with the target images in the descriptions. To investigate it, this work presents the first systematic study on object hallucination of LVLMs. We conduct the evaluation experiments on several representative LVLMs, and show that they mostly suffer from severe object hallucination issues. We further discuss that the visual instructions may influence the hallucination, and find that: objects that frequently appear in the visual instructions or co-occur with the image objects are obviously prone to be hallucinated by LVLMs. Besides, we further design a polling-based query method called POPE for better evaluation of object hallucination. Experiment results show that our POPE can evaluate object hallucination in a more stable and flexible way.", +} +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above and use these: +_POPE_PATH = '/tmp/data/pope/' + + +class POPEConfig(tfds.core.BuilderConfig): + """Configuration to build the dataset.""" + + pass + + +class POPE(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for POPE dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + BUILDER_CONFIGS = [ + POPEConfig(name='pope_random', description='Random set'), + POPEConfig(name='pope_popular', description='Popular set'), + POPEConfig(name='pope_adversarial', description='Adversarial set'), + ] + + def _info(self): + """Returns the metadata.""" + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='png'), + 'question': tfds.features.Text(), + 'answer': tfds.features.Text(), + 'thing': tfds.features.Text(), + }), + supervised_keys=None, + homepage='https://github.com/AoiDragon/POPE', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {'test': self._generate_examples('test', self.builder_config.name)} + + def _generate_examples(self, split: str, source: str): + """Yields (key, example) tuples from test set.""" + annot_fname = os.path.join( + _POPE_PATH, f'pope/coco_{source}.json' + ) + + with open(annot_fname, 'r') as f: + data = [json.loads(line) for line in f] + + for idx, v in enumerate(data): + question = v['text'] + thing = ( + question.replace('Is there an ', '') + .replace('Is there a ', '') + .replace(' in the image?', '') + ) + yield idx, { + 'question_id': idx, + 'image/filename': v['image'], + 'image': os.path.join(_POPE_PATH, 'images/val2014/', v['image']), + 'question': question, + 'answer': v['label'], + 'thing': thing, + } diff --git a/big_vision/datasets/refcoco/refcoco.py b/big_vision/datasets/refcoco/refcoco.py new file mode 100644 index 0000000000000000000000000000000000000000..27d391bfdbc4ffcdb0467940ad5597119089a1d5 --- /dev/null +++ b/big_vision/datasets/refcoco/refcoco.py @@ -0,0 +1,448 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Unbatch RefCOCO, RefCOCO+, RefCOCOg datasets in TFDS structure.""" + +# Based on tensorflow_datasets/datasets/ref_coco + +import io +import os +import pickle + +import numpy as np +import PIL.Image +import pycocotools.coco +import tensorflow_datasets as tfds + +_ROOT_PATH = '/tmp/data/' + + +class RefCocoConfig(tfds.core.BuilderConfig): + """Config to specify each RefCoco variant.""" + + def __init__(self, dataset, dataset_partition, **kwargs): + name = f'{dataset}_{dataset_partition}' + super(RefCocoConfig, self).__init__(name=name, **kwargs) + self.dataset = dataset + self.dataset_partition = dataset_partition + + +_DESCRIPTION = """RefCOCO, RefCOCO+, RefCOCOg datasets. + +Images, boxes and segmentations are from the original COCO dataset +(Lin et al, ECCV 2014). The referential segmentations are from two different +sources: + +1) RefCOCOg (Mao et al, CVPR 2016): + - https://github.com/mjhucla/Google_Refexp_toolbox + - This is the split used in the "refcocog_google" dataset. Note that this + split has overlapping images in train/validation. The same split is also + provided in 2). + +2) Source of RefCOCO and RefCOCO+ (Yu et al, ECCV 2016): + - https://github.com/lichengunc/refer + - Apache License 2.0 + - Provides all the splits used for generation of these datasets, including the + "refcocog_google" split that is identical with the split from 1). + +For convenience, we provide an additional dataset "refcocox_combined" that +combines the datasets "refcoco_unc", "refcocoplus_unc", and "refcocog_umd", +unifying "testA" and "testB" into a single "test" split, and removing any images +from "train" that appear either in "validation" or "test". + +Also for convenience, every split is unrolled twice (at the "objects" level and +at the "object/refs" level) and saved as "{split}_flat". +""" + +# pylint: disable=line-too-long +_CITATION = r""" +@inproceedings{DBLP:conf/cvpr/MaoHTCY016, + author = {Junhua Mao and + Jonathan Huang and + Alexander Toshev and + Oana Camburu and + Alan L. Yuille and + Kevin Murphy}, + title = {Generation and Comprehension of Unambiguous Object Descriptions}, + booktitle = {2016 {IEEE} Conference on Computer Vision and Pattern Recognition, + {CVPR} 2016, Las Vegas, NV, USA, June 27-30, 2016}, + pages = {11--20}, + publisher = {{IEEE} Computer Society}, + year = {2016}, + url = {https://doi.org/10.1109/CVPR.2016.9}, + doi = {10.1109/CVPR.2016.9}, + timestamp = {Fri, 24 Mar 2023 00:02:52 +0100}, + biburl = {https://dblp.org/rec/conf/cvpr/MaoHTCY016.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + +@inproceedings{DBLP:conf/eccv/YuPYBB16, + author = {Licheng Yu and + Patrick Poirson and + Shan Yang and + Alexander C. Berg and + Tamara L. Berg}, + editor = {Bastian Leibe and + Jiri Matas and + Nicu Sebe and + Max Welling}, + title = {Modeling Context in Referring Expressions}, + booktitle = {Computer Vision - {ECCV} 2016 - 14th European Conference, Amsterdam, + The Netherlands, October 11-14, 2016, Proceedings, Part {II}}, + series = {Lecture Notes in Computer Science}, + volume = {9906}, + pages = {69--85}, + publisher = {Springer}, + year = {2016}, + url = {https://doi.org/10.1007/978-3-319-46475-6\_5}, + doi = {10.1007/978-3-319-46475-6\_5}, + timestamp = {Wed, 07 Dec 2022 23:10:23 +0100}, + biburl = {https://dblp.org/rec/conf/eccv/YuPYBB16.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + +@article{DBLP:journals/corr/LinMBHPRDZ14, + author = {Tsung{-}Yi Lin and + Michael Maire and + Serge J. Belongie and + Lubomir D. Bourdev and + Ross B. Girshick and + James Hays and + Pietro Perona and + Deva Ramanan and + Piotr Doll{\'{a}}r and + C. Lawrence Zitnick}, + title = {Microsoft {COCO:} Common Objects in Context}, + journal = {CoRR}, + volume = {abs/1405.0312}, + year = {2014}, + url = {http://arxiv.org/abs/1405.0312}, + archivePrefix = {arXiv}, + eprint = {1405.0312}, + timestamp = {Mon, 13 Aug 2018 16:48:13 +0200}, + biburl = {https://dblp.org/rec/bib/journals/corr/LinMBHPRDZ14}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} +""" + +# coco_data = json.load(open('annotations/instances_train2017.json')) +# [l['name'] for l in coco_data['licenses']] +LICENSES = [ + 'Attribution-NonCommercial-ShareAlike License', + 'Attribution-NonCommercial License', + 'Attribution-NonCommercial-NoDerivs License', + 'Attribution License', + 'Attribution-ShareAlike License', + 'Attribution-NoDerivs License', + 'No known copyright restrictions', + 'United States Government Work', +] +# _licenses_map = {l['id']: i for i, l in enumerate(coco_data['licenses'])} +_licenses_map = {1: 0, 2: 1, 3: 2, 4: 3, 5: 4, 6: 5, 7: 6, 8: 7} + +# pyformat: disable +# [c['name'] for c in coco_data['categories']] +CATEGORIES = [ + 'person', 'bicycle', 'car', 'motorcycle', 'airplane', 'bus', 'train', + 'truck', 'boat', 'traffic light', 'fire hydrant', 'stop sign', + 'parking meter', 'bench', 'bird', 'cat', 'dog', 'horse', 'sheep', 'cow', + 'elephant', 'bear', 'zebra', 'giraffe', 'backpack', 'umbrella', 'handbag', + 'tie', 'suitcase', 'frisbee', 'skis', 'snowboard', 'sports ball', 'kite', + 'baseball bat', 'baseball glove', 'skateboard', 'surfboard', + 'tennis racket', 'bottle', 'wine glass', 'cup', 'fork', 'knife', 'spoon', + 'bowl', 'banana', 'apple', 'sandwich', 'orange', 'broccoli', 'carrot', + 'hot dog', 'pizza', 'donut', 'cake', 'chair', 'couch', 'potted plant', + 'bed', 'dining table', 'toilet', 'tv', 'laptop', 'mouse', 'remote', + 'keyboard', 'cell phone', 'microwave', 'oven', 'toaster', 'sink', + 'refrigerator', 'book', 'clock', 'vase', 'scissors', 'teddy bear', + 'hair drier', 'toothbrush', +] +# sorted(set(c['supercategory'] for c in coco_data['categories'])) +SUPERCATEGORIES = [ + 'accessory', 'animal', 'appliance', 'electronic', 'food', 'furniture', + 'indoor', 'kitchen', 'outdoor', 'person', 'sports', 'vehicle', +] +# pyformat: enable + + +# Will be exported into directory `$TFDS_DATA_DIR/ref_coco_bv` +# If the class name was `RefCOCO` then it would be exported into +# `$TFDS_DATA_DIR/ref_coco`, which would collide with the default TFDS dataset +# also named `ref_coco` (which has precedence over `data_dir` builder arg). +class RefCocoBv(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for RefCoco datasets.""" + + VERSION = tfds.core.Version('1.4.0') + RELEASE_NOTES = { + '1.4.0': 'Added flat versions of all dataset splits.', + '1.3.0': 'Added "refcocox_combined" dataset.', + '1.2.0': 'Added "train_flat" splits.', + '1.1.0': 'Added more features (mask etc), nested "refs" in "objects".', + '1.0.0': 'Initial release.', + } + + MANUAL_DOWNLOAD_INSTRUCTIONS = """ + 1. Install https://pypi.org/project/pycocotools/. + + 2. Download data (requires ~20G for COCO images): + + (mkdir -p /tmp/tfds/downloads/manual && + cd /tmp/tfds/downloads/manual && + wget http://images.cocodataset.org/zips/train2017.zip && + wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip && + wget https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco.zip && + wget https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco+.zip && + wget https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcocog.zip && + for zip in *.zip; do unzip $zip; done + ) + + 3. Run the generation script with `TFDS_DATA_DIR=/tmp/tfds` + """ + + BUILDER_CONFIGS = [ + RefCocoConfig(dataset='refcoco', dataset_partition='unc'), + RefCocoConfig(dataset='refcoco', dataset_partition='google'), + RefCocoConfig(dataset='refcocoplus', dataset_partition='unc'), + RefCocoConfig(dataset='refcocog', dataset_partition='google'), + RefCocoConfig(dataset='refcocog', dataset_partition='umd'), + RefCocoConfig(dataset='refcocox', dataset_partition='combined'), + ] + + def _info(self) -> tfds.core.DatasetInfo: + return tfds.core.DatasetInfo( + builder=self, + features=tfds.features.FeaturesDict({ + 'id': tfds.features.Scalar(np.int32), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'height': tfds.features.Scalar(np.int32), + 'width': tfds.features.Scalar(np.int32), + 'license': tfds.features.ClassLabel(names=LICENSES), + 'file_name': tfds.features.Text(), + 'flickr_url': tfds.features.Text(), + 'coco_url': tfds.features.Text(), + 'objects': tfds.features.Sequence({ + 'id': tfds.features.Scalar(np.int64), + 'area': tfds.features.Scalar(np.float32), + 'bbox': tfds.features.BBoxFeature(), + 'mask': tfds.features.Image(encoding_format='png'), + 'category': tfds.features.ClassLabel(names=CATEGORIES), + 'supercategory': tfds.features.ClassLabel( + names=SUPERCATEGORIES + ), + 'iscrowd': tfds.features.Scalar(np.bool_), + # refcoco, refcoco+, refcocog features: + 'refs': tfds.features.Sequence({ + 'id': tfds.features.Scalar(np.int32), + 'sentence': tfds.features.Text(), + }), + }), + }), + supervised_keys=None, # Set to `None` to disable + citation=_CITATION, + description=_DESCRIPTION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + allowed_splits = { + ('refcoco', 'google'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + tfds.Split.TEST, + ], + ('refcoco', 'unc'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + 'testA', + 'testB', + ], + ('refcocoplus', 'unc'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + 'testA', + 'testB', + ], + # Verified manually that image and annotation IDs match the ones in + # https://storage.googleapis.com/refexp/google_refexp_dataset_release.zip + ('refcocog', 'google'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + ], + ('refcocog', 'umd'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + tfds.Split.TEST, + ], + ('refcocox', 'combined'): [ + tfds.Split.TRAIN, + tfds.Split.VALIDATION, + tfds.Split.TEST, + ], + } + bc = self.builder_config + splits = allowed_splits[(bc.dataset, bc.dataset_partition)] + + data_dir = dl_manager.manual_dir + for url, components in ( + # pylint: disable=line-too-long + # pyformat: disable + ('http://images.cocodataset.org/zips/train2017.zip', ('train2017', '000000147328.jpg')), + ('http://images.cocodataset.org/annotations/annotations_trainval2017.zip', ('annotations', 'instances_train2017.json')), + ('https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco.zip', ('refcoco', 'refs(unc).p')), + ('https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcoco+.zip', ('refcoco+', 'refs(unc).p')), + ('https://bvisionweb1.cs.unc.edu/licheng/referit/data/refcocog.zip', ('refcocog', 'refs(umd).p')), + # pyformat: enable + # pylint: enable=line-too-long + ): + path = os.path.exists(os.path.join(data_dir, *components)) + if not path: + raise FileNotFoundError( + f'Could not find {path}: please download {url} and unzip into' + f' {data_dir}' + ) + + coco = pycocotools.coco.COCO( + os.path.join(data_dir, 'annotations', 'instances_train2017.json') + ) + + return { + split + suffix: self._generate_examples( + coco, data_dir, bc.dataset, bc.dataset_partition, split + suffix, + ) + for split in splits + for suffix in ('', '_flat') + } + + # Builder must overwrite all abstract methods. + def _generate_examples( + self, coco, data_dir, dataset, dataset_partition, split): + return _generate_examples(coco, data_dir, dataset, dataset_partition, split) + + +def _get_ids(data_dir, dataset, dataset_partition, split): + """Returns `img_ids, ann_to_refs` for specified dataset/partition/split.""" + + def load(dataset, dataset_partition): + fname = f'refs({dataset_partition}).p' + path = os.path.join(data_dir, dataset, fname) + refcoco = pickle.load(open(path, 'rb')) + return refcoco + + if split == tfds.Split.VALIDATION: + split = 'val' + + if (dataset, dataset_partition) == ('refcocox', 'combined'): + refcoco = ( + load('refcocog', 'umd') + + load('refcoco', 'unc') + + load('refcoco+', 'unc') + ) + if split == 'test': + splits = ('test', 'testA', 'testB') + else: + splits = (split,) + + exclude_img_ids = set() + if split == 'train': + # Exclude all images with val/test annotations from train set. + exclude_img_ids = { + r['image_id'] for r in refcoco if r['split'] != 'train' + } + refcoco = [ + r + for r in refcoco + if r['split'] in splits and r['image_id'] not in exclude_img_ids + ] + + else: + if dataset == 'refcocoplus': + dataset = 'refcoco+' + refcoco = load(dataset, dataset_partition) + refcoco = [r for r in refcoco if r['split'] == split] + + img_ids = {r['image_id'] for r in refcoco} + ann_to_refs = {} + for r in refcoco: + for sent in r['sentences']: + ann_to_refs.setdefault(r['ann_id'], []).append(dict( + id=sent['sent_id'], + sentence=sent['sent'] + )) + + return img_ids, ann_to_refs + + +def _generate_examples(coco, data_dir, dataset, dataset_partition, split): + """Generates examples for a given split.""" + + flat = '_flat' in split + split = split.replace('_flat', '') + img_ids, ann_to_refs = _get_ids(data_dir, dataset, dataset_partition, split) + + for img_id in coco.getImgIds(): + + if img_id not in img_ids: + continue + img, = coco.loadImgs([img_id]) + + example = { + 'id': img_id, + 'image': os.path.join(data_dir, 'train2017', img['file_name']), + 'height': img['height'], + 'width': img['width'], + 'license': LICENSES[_licenses_map[img['license']]], + 'file_name': img['file_name'], + 'flickr_url': img['flickr_url'], + 'coco_url': img['coco_url'], + 'objects': [], + } + for ann in coco.loadAnns(coco.getAnnIds(img_id)): + refs = ann_to_refs.get(ann['id']) + if not refs: + continue + cat, = coco.loadCats([ann['category_id']]) + mask = coco.annToMask(ann).astype(np.bool_) + mask_buf = io.BytesIO() + PIL.Image.fromarray(mask).save(mask_buf, 'png') + mask_buf.seek(0) + object_ = { + 'id': ann['id'], + 'mask': mask_buf, + 'category': cat['name'], + 'supercategory': cat['supercategory'], + 'iscrowd': ann['iscrowd'], + 'area': ann['area'], + 'bbox': _convert_bbox(img, *ann['bbox']), + 'refs': refs, + } + if flat: + example['objects'] = [object_] + for ref_i, ref in enumerate(refs): + object_['refs'] = [ref] + mask_buf.seek(0) + yield f'{img_id}_{ann["id"]}_{ref_i}', example + else: + example['objects'].append(object_) + + if not flat: + yield img_id, example + + +def _convert_bbox(img, x, y, w, h): + return tfds.features.BBox( + ymin=y / img['height'], + xmin=x / img['width'], + ymax=(y + h) / img['height'], + xmax=(x + w) / img['width'], + ) diff --git a/big_vision/datasets/rsvqa_hr/rsvqa_hr.py b/big_vision/datasets/rsvqa_hr/rsvqa_hr.py new file mode 100644 index 0000000000000000000000000000000000000000..9f41612edfa9606572c316a68c8908061933e6c7 --- /dev/null +++ b/big_vision/datasets/rsvqa_hr/rsvqa_hr.py @@ -0,0 +1,193 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements RSVQA-HR dataset in TFDS. + +Remote sensing visual question answering task, using high-resolution airborne +image data at 15cm resolution per pixel. + +It's small dataset at source (14G), so simple to run locally. +First, download and unzip the dataset from https://zenodo.org/records/6344367 +and place it in /tmp/data/rsvqa_hr. + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd third_party/py/big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=rsvqa_hr + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('rsvqa_hr', split='train', data_dir='/tmp/tfds') + +Dataset splits (all): + train: 625,340 examples/questions + val: 102,843 examples/questions + test: 222,684 examples/questions + test_2: 105,647 examples/questions (other area, unknown instrument) +Non-numeric data splits (nonum): + train: 371,834 examples/questions + val: 60,405 examples/questions + test: 131,468 examples/questions + test_2: 62,554 examples/questions + +Note: due to image duplication with each question, the dataset size is +significatnly increased by the number of questions per image. + +Recommended training splits: + train: train + minitrain: train[:5%] + eval: val + full_train: train+val + test: test + +Image sizes: 512x512 +Number of answers per question: 1 +Question types distribution in train split: + - Area (area): 14.6% (integers, binned into {0m2, 1-10m2, 11-100m2, 101-1000m2, >1000m2}) + - Comparison(comp): 33.5% + - Count (count): 26.0% (integers, not binned, maximum number of objects is 89) + - Presence (presence): 26.0% +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """RSVQA-HR dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@article{Lobry_2020, + title={RSVQA: Visual Question Answering for Remote Sensing Data}, + volume={58}, + ISSN={1558-0644}, + url={http://dx.doi.org/10.1109/TGRS.2020.2988782}, + DOI={10.1109/tgrs.2020.2988782}, + number={12}, + journal={IEEE Transactions on Geoscience and Remote Sensing}, + publisher={Institute of Electrical and Electronics Engineers (IEEE)}, + author={Lobry, Sylvain and Marcos, Diego and Murray, Jesse and Tuia, Devis}, + year={2020}, + month=dec, pages={8555-8566} } +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +PATH = '/tmp/data/rsvqa_hr/' + + +class RsvqaHrConfig(tfds.core.BuilderConfig): + """Config to specify each variant.""" + + def __init__(self, nonum, **kwargs): + name = 'nonum' if nonum else 'all' + super(RsvqaHrConfig, self).__init__(name=name, **kwargs) + self.nonum = nonum + + +class RsvqaHr(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for RSVQA-HR dataset.""" + + VERSION = tfds.core.Version('1.0.2') + RELEASE_NOTES = { + '1.0.0': 'First release.', + '1.0.1': 'Rename binned values.', + '1.0.2': 'Removed explicit png image encoding.', + } + + BUILDER_CONFIGS = [ + RsvqaHrConfig(nonum=False), + RsvqaHrConfig(nonum=True), + ] + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'filename': tfds.features.Text(), + 'image': tfds.features.Image(), + 'question': tfds.features.Text(), + 'question_type': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + 'raw_answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage='https://rsvqa.sylvainlobry.com/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return { + split: self._generate_examples(split) + for split in ('train', 'val', 'test', 'test_2') + } + + def _generate_examples(self, split): + """Yields (key, example) tuples.""" + if split == 'test_2': + split = 'test_phili' + questions_path = os.path.join(PATH + f'USGS_split_{split}_questions.json') + answers_path = os.path.join(PATH + f'USGS_split_{split}_answers.json') + images_path = os.path.join(PATH + 'Data') + + with open(questions_path, 'r') as f: + questions = json.loads(f.read())['questions'] + with open(answers_path, 'r') as f: + answers = json.loads(f.read())['answers'] + + for q, a in zip(questions, answers): + assert q['active'] == a['active'] + if not q['active']: + continue + if self.builder_config.nonum and q['type'] in ('area', 'count'): + continue + assert q['answers_ids'][0] == a['id'] + assert q['id'] == a['question_id'] + + filename = f'{q["img_id"]}.png' + yield q['id'], { + 'question_id': q['id'], + 'filename': filename, + 'image': os.path.join(images_path, filename), + 'question': q['question'], + 'question_type': q['type'], + 'answers': [bin_answer(a['answer'], q['type'])], + 'raw_answers': [a['answer']], + } + + +def bin_answer(answer, question_type): + """Bins answers into expected ranges.""" + if question_type == 'area': + area = int(answer[:-2]) + if area == 0: + return '0 m2' + elif area <= 10: + return 'between 1 m2 and 10 m2' + elif area <= 100: + return 'between 11 m2 and 100 m2' + elif area <= 1000: + return 'between 101 m2 and 1000 m2' + else: + return 'more than 1000 m2' + return answer diff --git a/big_vision/datasets/rsvqa_lr/rsvqa_lr.py b/big_vision/datasets/rsvqa_lr/rsvqa_lr.py new file mode 100644 index 0000000000000000000000000000000000000000..7b2a963446ab2524141e55f07af4b64cf483c54e --- /dev/null +++ b/big_vision/datasets/rsvqa_lr/rsvqa_lr.py @@ -0,0 +1,198 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements RSVQA-LR dataset in TFDS. + +Remote sensing visual question answering task, using low-resolution satellite +(Sentinel-2) RGB channels data at 10m resolution per pixel. + +It's small dataset at source (200M), so simple to run locally. +First, download and unzip the dataset from https://zenodo.org/records/6344334 +and place it in /tmp/data/rsvqa_lr. + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd third_party/py/big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=rsvqa_lr + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('rsvqa_lr', split='train', data_dir='/tmp/tfds') + +Dataset splits: + train: 57223 examples/questions + val: 10005 examples/questions + test: 10004 examples/questions +And the same splits are available excluding numeric questions: + train_nonum: 39441 examples/questions + val_nonum: 6782 examples/questions + test_nonum: 6782 examples/questions + +Note: due to image duplication with each question, the dataset size is +significatnly increased by the number of questions per image. + +Recommended training splits: + train: train + minitrain: train[:5%] + eval: val + full_train: train+val + test: test + +Image sizes: 256x256 +Number of answers per question: 1 +Question types distribution in train split: + - Comparison(comp): 39.4% + - Count (count): 29.9% (integers, binned at evaluation into + {0, 1-10, 11-100, 101-1000, >10000}) + - Presence (presence): 29.7% + - Rural/Urban (rural_urban): 1% +""" +import io +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """RSVQA-LR dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@article{Lobry_2020, + title={RSVQA: Visual Question Answering for Remote Sensing Data}, + volume={58}, + ISSN={1558-0644}, + url={http://dx.doi.org/10.1109/TGRS.2020.2988782}, + DOI={10.1109/tgrs.2020.2988782}, + number={12}, + journal={IEEE Transactions on Geoscience and Remote Sensing}, + publisher={Institute of Electrical and Electronics Engineers (IEEE)}, + author={Lobry, Sylvain and Marcos, Diego and Murray, Jesse and Tuia, Devis}, + year={2020}, + month=dec, pages={8555–8566} } +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +PATH = '/tmp/data/rsvqa_lr/' + + +class RsvqaLrConfig(tfds.core.BuilderConfig): + """Config to specify each variant.""" + + def __init__(self, nonum, **kwargs): + name = 'nonum' if nonum else 'all' + super(RsvqaLrConfig, self).__init__(name=name, **kwargs) + self.nonum = nonum + + +class RsvqaLr(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for RSVQA-LR dataset.""" + + VERSION = tfds.core.Version('1.0.2') + RELEASE_NOTES = { + '1.0.0': 'First release.', + '1.0.1': 'Rename binned values.', + '1.0.2': 'Removed explicit png image encoding.', + } + + BUILDER_CONFIGS = [ + RsvqaLrConfig(nonum=False), + RsvqaLrConfig(nonum=True), + ] + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'filename': tfds.features.Text(), + 'image': tfds.features.Image(), + 'question': tfds.features.Text(), + 'question_type': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + 'raw_answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage='https://rsvqa.sylvainlobry.com/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return { + split: self._generate_examples(split) + for split in ('train', 'val', 'test') + } + + def _generate_examples(self, split): + """Yields (key, example) tuples.""" + questions_path = os.path.join(PATH + f'LR_split_{split}_questions.json') + answers_path = os.path.join(PATH + f'LR_split_{split}_answers.json') + images_path = os.path.join(PATH + 'Images_LR') + + with open(questions_path, 'r') as f: + questions = json.loads(f.read())['questions'] + with open(answers_path, 'r') as f: + answers = json.loads(f.read())['answers'] + + for q, a in zip(questions, answers): + assert q['active'] == a['active'] + if not q['active']: + continue + if self.builder_config.nonum and q['type'] == 'count': + continue + assert q['answers_ids'] == [a['id']] + assert q['id'] == a['question_id'] + + filename = f'{q["img_id"]}.tif' + img = read_tif(os.path.join(images_path, filename)) + yield q['id'], { + 'question_id': q['id'], + 'filename': filename, + 'image': img, + 'question': q['question'], + 'question_type': q['type'], + 'answers': [bin_answer(a['answer'], q['type'])], + 'raw_answers': [a['answer']], + } + + +def bin_answer(answer, question_type): + """Bins answers into expected ranges.""" + if question_type == 'count': + count = int(answer) + if count == 0: + return '0' + elif count <= 10: + return 'between 1 and 10' + elif count <= 100: + return 'between 11 and 100' + elif count <= 1000: + return 'between 101 and 1000' + else: + return 'more than 1000' + return answer + + +def read_tif(path): + with open(path, 'rb') as f: + img = tfds.core.lazy_imports.tifffile.imread(io.BytesIO(f.read())) + return img.astype(np.uint8) diff --git a/big_vision/datasets/scicap/scicap.py b/big_vision/datasets/scicap/scicap.py new file mode 100644 index 0000000000000000000000000000000000000000..3aa714981785b6c5cda5a6e880160987e2a2e250 --- /dev/null +++ b/big_vision/datasets/scicap/scicap.py @@ -0,0 +1,205 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Creates TFDS dataset for SciCap. + +Preparing the data: + 1) mkdir /tmp/data/scicap && cd /tmp/data/scicap + 2) wget 'https://www.dropbox.com/s/t1sjqesl0pynaxo/scicap_data.zip?dl=0' + 3) unzip -UU 'scicap_data.zip?dl=0' && rm 'scicap_data.zip?dl=0' + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=scicap + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('scicap', split='train', data_dir='/tmp/tfds') +""" +# pylint: enable=line-too-long +import enum +import functools +import json +import os + +import tensorflow_datasets as tfds + + +_DESCRIPTION = """SciCap dataset.""" +_CITATION = """ +@article{hsu2021scicap, + title={SciCap: Generating captions for scientific figures}, + author={Hsu, Ting-Yao and Giles, C Lee and Huang, Ting-Hao'Kenneth'}, + journal={arXiv preprint arXiv:2110.11624}, + year={2021} +} +""" + +# When running locally (recommended), copy files as above an use these: +_SCICAP_DIR = "/tmp/data/scicap/scicap_data" + + +class ScicapSubset(enum.Enum): + """Versions of the SciCap dataset.""" + SINGLE_SENTENCE = "single_sentence" + FIRST_SENTENCE = "first_sentence" + LEQ_100_TOKENS = "leq_100_tokens" + +_SPLITS_TO_GENERATE = ["train", "test", "val"] +_CONFIG_TO_IDS_PATH = { + (ScicapSubset.SINGLE_SENTENCE, True): "Single-Sentence-Caption/Yes-Subfig", + (ScicapSubset.SINGLE_SENTENCE, False): "Single-Sentence-Caption/No-Subfig", + (ScicapSubset.FIRST_SENTENCE, True): "First-Sentence/Yes-Subfig", + (ScicapSubset.FIRST_SENTENCE, False): "First-Sentence/No-Subfig", + (ScicapSubset.LEQ_100_TOKENS, True): + "Caption-No-More-Than-100-Tokens/Yes-Subfig", + (ScicapSubset.LEQ_100_TOKENS, False): + "Caption-No-More-Than-100-Tokens/No-Subfig", +} +_SUBFIG_TO_PATH = { + True: "SciCap-Yes-Subfig-Img", False: "SciCap-No-Subfig-Img" +} + + +class ScicapConfig(tfds.core.BuilderConfig): + """"Configuration for SciCap caption length and subfigure inclusion.""" + + def __init__(self, *, subset: ScicapSubset, subfig: bool, **kwargs): + """Parameters specifying how the dataset will be processed. + + Args: + subset: Subset of the Scicap data (see enum above). + subfig: Whether or not figure with subfigures are included. + **kwargs: Passed on to the constructor of `BuilderConfig`. + """ + super(ScicapConfig, self).__init__(**kwargs) + self.subset = subset + self.subfig = subfig + + +@functools.cache +def _read_annotations(split: str, image_id: str): + """Reads annotations for a single file.""" + path = os.path.join(_SCICAP_DIR, "SciCap-Caption-All", split) + fname = os.path.join(path, image_id + ".json") + with open(fname, "r") as fin: + return json.load(fin) + + +class Scicap(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for the SciCap dataset.""" + + VERSION = tfds.core.Version("1.0.0") + RELEASE_NOTES = {"1.0.0": "First release."} + + BUILDER_CONFIGS = [ + ScicapConfig( + name="single_sentence_subfig_yes", + description="Single sentence caption with subfigures allowed.", + subset=ScicapSubset.SINGLE_SENTENCE, + subfig=True + ), + ScicapConfig( + name="single_sentence_subfig_no", + description="Single sentence caption with subfigures not allowed.", + subset=ScicapSubset.SINGLE_SENTENCE, + subfig=False + ), + ScicapConfig( + name="first_sentence_subfig_yes", + description="First sentence of captions with subfigures allowed.", + subset=ScicapSubset.FIRST_SENTENCE, + subfig=True + ), + ScicapConfig( + name="first_sentence_subfig_no", + description="First sentence of captions with subfigures not allowed.", + subset=ScicapSubset.FIRST_SENTENCE, + subfig=False + ), + ScicapConfig( + name="leq_100_tokens_subfig_yes", + description="Captions with <= 100 tokens with subfigures allowed.", + subset=ScicapSubset.LEQ_100_TOKENS, + subfig=True + ), + ScicapConfig( + name="leq_100_tokens_subfig_no", + description=("Captions with <= 100 tokens with subfigures" + " not allowed."), + subset=ScicapSubset.LEQ_100_TOKENS, + subfig=False + ), + ] + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + "image/id": tfds.features.Text(), + "image/filename": tfds.features.Text(), + "image": tfds.features.Image(encoding_format="png"), + "caption/originally_extracted": tfds.features.Text(), + "caption/lowercase_and_token_and_remove_figure_index": + tfds.features.Text(), + "caption/normalized/basic_num": tfds.features.Text(), + "caption/normalized/advanced_equation_bracket": + tfds.features.Text(), + }), + supervised_keys=None, + homepage="https://github.com/tingyaohsu/SciCap", + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in _SPLITS_TO_GENERATE} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from test set.""" + config_path = _CONFIG_TO_IDS_PATH[ + (self.builder_config.subset, self.builder_config.subfig)] + image_path = os.path.join( + _SCICAP_DIR, _SUBFIG_TO_PATH[self.builder_config.subfig], split) + id_list_fname = os.path.join( + _SCICAP_DIR, "List-of-Files-for-Each-Experiments", + config_path, split, "file_idx.json") + with open(id_list_fname, "r") as fin: + split_images = json.load(fin) + + for fname in split_images: + assert fname.endswith(".png") + image_id = fname[:-len(".png")] + annotations = _read_annotations(split, image_id) + yield fname, { + "image/id": image_id, + "image/filename": fname, + "image": os.path.join(image_path, fname), + "caption/originally_extracted": annotations["0-originally-extracted"], + "caption/lowercase_and_token_and_remove_figure_index": + annotations["1-lowercase-and-token-and-remove-figure-index"][ + "caption"], + "caption/normalized/basic_num": annotations["2-normalized"][ + "2-1-basic-num"]["caption"], + "caption/normalized/advanced_equation_bracket": + annotations["2-normalized"][ + "2-2-advanced-euqation-bracket"]["caption"] + } diff --git a/big_vision/datasets/science_qa/science_qa.py b/big_vision/datasets/science_qa/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..8eb8189e57fbe9352d4b82044e8d7e44576052f5 --- /dev/null +++ b/big_vision/datasets/science_qa/science_qa.py @@ -0,0 +1,156 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements ScienceQA train/val/test-set in TFDS structure. + +First, download the science QA dataset from their website https://scienceqa.github.io/#download + - mkdir -p /tmp/data/ScienceQA_DATA + - From Google Drive: https://drive.google.com/corp/drive/folders/1w8imCXWYn2LxajmGeGH_g5DaL2rabHev +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + - cd big_vision/datasets + - env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=science_qa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load( + 'science_qa', split='train', + data_dir='/tmp/tfds') + +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """Sci QA test-set.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{lu2022learn, + title={Learn to Explain: Multimodal Reasoning via Thought Chains for Science Question Answering}, + author={Lu, Pan and Mishra, Swaroop and Xia, Tony and Qiu, Liang and Chang, Kai-Wei and Zhu, Song-Chun and Tafjord, Oyvind and Clark, Peter and Ashwin Kalyan}, + booktitle={The 36th Conference on Neural Information Processing Systems (NeurIPS)}, + year={2022} +} +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_SCIQA_PATH = '/tmp/data/ScienceQA_DATA/' +# _IMAGE_COCO_PATH = '/tmp/data/val2014' + +_ALPHABETS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + +class ScienceQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for ScienceQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question': tfds.features.Text(), + 'choices': tfds.features.Sequence(tfds.features.Text()), + 'answer': tfds.features.Scalar(np.int32), + 'hint': tfds.features.Text(), + 'task': tfds.features.Text(), + 'grade': tfds.features.Text(), + 'subject': tfds.features.Text(), + 'topic': tfds.features.Text(), + 'category': tfds.features.Text(), + 'skill': tfds.features.Text(), + 'lecture': tfds.features.Text(), + 'solution': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='png'), + 'indexed_choices': tfds.features.Text(), + 'indexed_answer': tfds.features.Text(), + }), + supervised_keys=None, + homepage='https://github.com/lupantech/ScienceQA/tree/main', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return { + split: self._generate_examples(split) + for split in ('train', 'test', 'val') + } + + def _generate_examples(self, split): + """Yields (key, example) tuples from test set.""" + annot_fname = os.path.join(_SCIQA_PATH, 'problems.json') + + with open(annot_fname, 'r') as f: + data = json.loads(f.read()) + + for k, v in data.items(): + if v['split'] == split: # "split":"train" + image = v['image'] + # Science QA contains the example without image as well. As this + # conversion is for VQA tasks, we dropped the examples without Image. + # TODO: Include the examples without image, and udpate the + # downstream pipeline to skip the examples without image, instead of + # doing it at pre-processing. + if image: + image = os.path.join(f'{_SCIQA_PATH}/{split}/{k}/', f'{image}') + else: + # image = None + continue + question = v['question'] + choices = v['choices'] + answer = v['answer'] + hint = v['hint'] + if not hint: + hint = 'N/A' # align with orignal github implementation + task = v['task'] + grade = v['grade'] + subject = v['subject'] + topic = v['topic'] + category = v['category'] + skill = v['skill'] + lecture = v['lecture'] + solution = v['solution'] + split = v['split'] + indexed_choices = ', '.join( + f'({_ALPHABETS[i]}) {c}' for i, c in enumerate(choices) + ) + indexed_answer = _ALPHABETS[int(answer)] + yield int(k), { + 'question': question, + 'choices': choices, + 'answer': answer, + 'hint': hint, + 'task': task, + 'grade': grade, + 'subject': subject, + 'topic': topic, + 'category': category, + 'skill': skill, + 'lecture': lecture, + 'solution': solution, + 'image': image, + 'indexed_choices': indexed_choices, + 'indexed_answer': indexed_answer, + } diff --git a/big_vision/datasets/screen2words/screen2words.py b/big_vision/datasets/screen2words/screen2words.py new file mode 100644 index 0000000000000000000000000000000000000000..97d469b13734d1a566cbfa05e8e1de91c6e55e6b --- /dev/null +++ b/big_vision/datasets/screen2words/screen2words.py @@ -0,0 +1,120 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Creates TFDS dataset for Screen2words. + + +Preparing the data: + 1) mkdir /tmp/data/rico && cd /tmp/data/rico + 2) wget https://storage.googleapis.com/crowdstf-rico-uiuc-4540/rico_dataset_v0.1/unique_uis.tar.gz + 3) tar xvfz unique_uis.tar.gz && rm unique_uis.tar.gz + 4) git clone https://github.com/google-research-datasets/screen2words.git + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=screen2words + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('screen2_words', split='train', data_dir='/tmp/tfds') +""" +# pylint: enable=line-too-long +import collections +import csv +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """Screen2words dataset.""" +_CITATION = """ +@inproceedings{wang2021screen2words, + title={Screen2words: Automatic mobile UI summarization with multimodal + learning}, + author={Wang, Bryan and + Li, Gang and + Zhou, Xin and + Chen, Zhourong and + Grossman, Tovi and + Li, Yang}, + booktitle={The 34th Annual ACM Symposium on User Interface Software + and Technology}, + pages={498--510}, + year={2021} +} +""" + +# When running locally (recommended), copy files as above an use these: +_SCREEN2WORDS_DIR = "/tmp/data/rico/screen2words" +_RICO_DIR = "/tmp/data/rico/combined" + + +# (name, path) tuples for splits to be generated. +_SPLITS_TO_GENERATE = ["train", "dev", "test"] + + +class Screen2Words(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for the Screen2words dataset.""" + + VERSION = tfds.core.Version("1.0.0") + RELEASE_NOTES = {"1.0.0": "First release."} + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + "image/id": tfds.features.Scalar(np.int32), + "image/filename": tfds.features.Text(), + "image": tfds.features.Image(encoding_format="jpeg"), + "summary": tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage="https://github.com/google-research-datasets/screen2words", + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in _SPLITS_TO_GENERATE} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from test set.""" + id_list_fname = os.path.join( + _SCREEN2WORDS_DIR, "split", f"{split}_screens.txt") + with open(id_list_fname, "r") as fin: + split_ids = fin.readlines() + + summaries_fname = os.path.join(_SCREEN2WORDS_DIR, "screen_summaries.csv") + summaries = collections.defaultdict(list) + with open(summaries_fname, "r") as fin: + for entry in csv.DictReader(fin): + summaries[int(entry["screenId"])].append(entry["summary"]) + + for line in split_ids: + line = line.strip() + image_id = int(line) + yield image_id, { + "image/id": image_id, + "image/filename": f"{image_id}.jpg", + "image": os.path.join(_RICO_DIR, f"{image_id}.jpg"), + "summary": summaries[image_id], + } diff --git a/big_vision/datasets/sequence_packing.py b/big_vision/datasets/sequence_packing.py new file mode 100644 index 0000000000000000000000000000000000000000..91629eadf591d6f00e7411a210c07eb75b7c3f0b --- /dev/null +++ b/big_vision/datasets/sequence_packing.py @@ -0,0 +1,209 @@ +# Copyright 2024 Big Vision 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. + +"""Packed Sequence Op.""" + +# Forked from +# https://github.com/google/maxtext/blob/main/MaxText/sequence_packing.py. + + +from typing import Dict, Optional, List, Union + +import tensorflow as tf + +AUTOTUNE = tf.data.experimental.AUTOTUNE + + +def pack_dataset(dataset: tf.data.Dataset, + key2length: Union[int, Dict[str, int]], + keys: Optional[List[str]] = None) -> tf.data.Dataset: + """Creates a 'packed' version of a dataset on-the-fly. + + Adapted from the mesh-tf implementation. + This is meant to replace the irritation of having to create a separate + "packed" version of a dataset to train efficiently on TPU. + Each example in the output dataset represents several examples in the + input dataset. + For each key in the input dataset, two additional keys are created: + _seg: an int32 tensor identifying the parts + representing the original example. + _pos: an int32 tensor identifying the position within the original + example. + Example: + Two input examples get combined to form an output example. + The input examples are: + {"inputs": [8, 7, 1, 0], "targets":[4, 1, 0]} + {"inputs": [2, 3, 4, 1], "targets":[5, 6, 1]} + The output example is: + { + "inputs": [8, 7, 1, 2, 3, 4, 1, 0, 0, 0] + "inputs_seg": [1, 1, 1, 2, 2, 2, 2, 0, 0, 0] + "inputs_pos": [0, 1, 2, 0, 1, 2, 3, 0, 0, 0] + "targets": [4, 1, 5, 6, 1, 0, 0, 0, 0, 0] + "targets_seg": [1, 1, 2, 2, 2, 0, 0, 0, 0, 0] + "targets_pos": [0, 1, 0, 1, 2, 0, 0, 0, 0, 0] + } + 0 represents padding in both the inputs and the outputs. + Sequences in the incoming examples are truncated to length "length", and the + sequences in the output examples all have fixed (padded) length "length". + Args: + dataset: a tf.data.Dataset + key2length: an integer, or a dict from feature-key to integer + keys: a list of strings (e.g. ["inputs", "targets"]) + Returns: + a tf.data.Dataset + """ + shapes = tf.nest.map_structure(lambda spec: spec.shape, dataset.element_spec) + if keys is None: + keys = list(shapes.keys()) + for k in keys: + if k not in shapes: + raise ValueError(f"""Key {k} not found in dataset. Available keys are + {shapes.keys()}""") + if not shapes[k].is_compatible_with(tf.TensorShape([None])): + raise ValueError('Tensors to be packed must be one-dimensional.') + # make sure that the length dictionary contains all keys as well as the + # keys suffixed by "_seg" and "_pos" + if isinstance(key2length, int): + key2length = {k: key2length for k in keys} + else: + key2length = dict(key2length) # Make new dict, we'll edit in-place. + for k in keys: + for suffix in ['_seg', '_pos']: + key2length[k + suffix] = key2length[k] + + # trim to length + dataset = dataset.map( + lambda x: {k: x[k][:key2length[k]] for k in keys}, + num_parallel_calls=AUTOTUNE) + # Setting batch_size=length ensures that the concatenated sequences (if they + # have length >=1) are sufficient to fill at least one packed example. + batch_size = max(key2length.values()) + dataset = dataset.padded_batch( + batch_size, padded_shapes={k: [-1] for k in keys}) + dataset = _pack_with_tf_ops(dataset, keys, key2length) + + # Set the Tensor shapes correctly since they get lost in the process. + def my_fn(x): + return {k: tf.reshape(v, [key2length[k]]) for k, v in x.items()} + + return dataset.map(my_fn, num_parallel_calls=AUTOTUNE) + + +def _pack_with_tf_ops(dataset: tf.data.Dataset, keys: List[str], + key2length: Dict[str, int]) -> tf.data.Dataset: + """Helper-function for packing a dataset which has already been batched. + Helper for pack_dataset() Uses tf.while_loop. + Args: + dataset: a dataset containing padded batches of examples. + keys: a list of strings + key2length: an dict from feature-key to integer + Returns: + a dataset. + """ + empty_example = {} + for k in keys: + empty_example[k] = tf.zeros([0], dtype=tf.int32) + empty_example[k + '_pos'] = tf.zeros([0], dtype=tf.int32) + keys_etc = empty_example.keys() + + def write_packed_example(partial, outputs): + new_partial = empty_example.copy() + new_outputs = {} + for k in keys_etc: + new_outputs[k] = outputs[k].write( + outputs[k].size(), + tf.pad(partial[k], [[0, key2length[k] - tf.size(partial[k])]])) + return new_partial, new_outputs + + def map_fn(x): + """Internal function to flat_map over. + Consumes a batch of input examples and produces a variable number of output + examples. + Args: + x: a single example + Returns: + a tf.data.Dataset + """ + partial = empty_example.copy() + i = tf.zeros([], dtype=tf.int32) + dynamic_batch_size = tf.shape(x[keys[0]])[0] + outputs = {} + for k in keys: + outputs[k] = tf.TensorArray( + tf.int32, size=0, dynamic_size=True, element_shape=[key2length[k]]) + outputs[k + '_pos'] = tf.TensorArray( + tf.int32, size=0, dynamic_size=True, element_shape=[key2length[k]]) + + def body_fn(i, partial, outputs): + """Body function for while_loop. + Args: + i: integer scalar + partial: dictionary of Tensor (partially-constructed example) + outputs: dictionary of TensorArray + Returns: + A triple containing the new values of the inputs. + """ + can_append = True + one_example = {} + for k in keys: + val = tf.cast(x[k][i], tf.int32) + val = val[:tf.reduce_sum(tf.cast(tf.not_equal(val, 0), tf.int32))] + one_example[k] = val + for k in keys: + can_append = tf.logical_and( + can_append, + tf.less_equal( + tf.size(partial[k]) + tf.size(one_example[k]), key2length[k])) + + def false_fn(): + return write_packed_example(partial, outputs) + + def true_fn(): + return partial, outputs + + partial, outputs = tf.cond(can_append, true_fn, false_fn) + new_partial = {} + for k in keys: + new_seq = one_example[k][:key2length[k]] + new_seq_len = tf.size(new_seq) + new_partial[k] = tf.concat([partial[k], new_seq], 0) + new_partial[k + '_pos'] = tf.concat( + [partial[k + '_pos'], + tf.range(new_seq_len)], 0) + partial = new_partial + return i + 1, partial, outputs + + # For loop over all examples in the batch. + i, partial, outputs = tf.while_loop( + cond=lambda *_: True, + body=body_fn, + loop_vars=(i, partial, outputs), + shape_invariants=( + tf.TensorShape([]), + {k: tf.TensorShape([None]) for k in keys_etc}, + {k: tf.TensorShape(None) for k in keys_etc}, + ), + maximum_iterations=dynamic_batch_size) + _, outputs = write_packed_example(partial, outputs) + packed = {k: outputs[k].stack() for k in keys_etc} + for k in keys: + packed[k + '_seg'] = ( + tf.cumsum( + tf.cast(tf.equal(packed[k + '_pos'], 0), tf.int32), axis=1) * + tf.cast(tf.not_equal(packed[k], 0), tf.int32)) + return packed + + dataset = dataset.map(map_fn, num_parallel_calls=AUTOTUNE) + return dataset.unbatch() diff --git a/big_vision/datasets/stvqa/stvqa.py b/big_vision/datasets/stvqa/stvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..6d215554bd820411415a1a490c6337a43ad61258 --- /dev/null +++ b/big_vision/datasets/stvqa/stvqa.py @@ -0,0 +1,134 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements ST-VQA dataset in TFDS. + +It's small data, so simple to run locally. +First, download and unzip the dataset from https://rrc.cvc.uab.es/?ch=11 +and place it in /tmp/data/stvqa. + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd third_party/py/big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=stvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('stvqa', split='train', data_dir='/tmp/tfds') + +Dataset splits: + train: 23446 examples/questions (subset of original train) + val: 2628 examples/questions (subset of original train) + test: 4070 examples/questions (no answers) + +Note: original source data has no val/holdout split, and we therefore split the +original train split (26074 examples/questions) by ourselves into train & val +splits. + +Recommended training splits: + train: train + minitrain: train[:5%] + eval: val + fulltrain: train+val +""" +import json +import os + +from big_vision.datasets.stvqa import val_ids +import numpy as np +import tensorflow_datasets as tfds + +_VAL_IDS = val_ids.PSEUDO_VAL_IMAGE_PATHS + +_DESCRIPTION = """ST-VQA dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{Biten_2019, + title={Scene Text Visual Question Answering}, + url={http://dx.doi.org/10.1109/ICCV.2019.00439}, + DOI={10.1109/iccv.2019.00439}, + booktitle={2019 IEEE/CVF International Conference on Computer Vision (ICCV)}, + publisher={IEEE}, + author={Biten, Ali Furkan and Tito, Ruben and Mafla, Andres and Gomez, Lluis and Rusinol, Marcal and Jawahar, C.V. and Valveny, Ernest and Karatzas, Dimosthenis}, + year={2019}, + month=oct } +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_STVQA_PATH = '/tmp/data/stvqa/' + + +class Stvqa(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for ST-VQA dataset.""" + + VERSION = tfds.core.Version('1.2.0') + RELEASE_NOTES = { + '1.0.0': 'First release.', + '1.1.0': 'Switch to COCO high-res images and lower-case answers.', + '1.2.0': 'Rename pseudo splits and remove lower-case answers.', + } + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question_id': tfds.features.Scalar(np.int32), + 'filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, + homepage='https://rrc.cvc.uab.es/?ch=11', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in ('train', 'val', 'test')} + + def _generate_examples(self, split): + """Yields (key, example) tuples.""" + src_split = 'test' if split == 'test' else 'train' + annot_fname = os.path.join(_STVQA_PATH, f'{src_split}_task_3.json') + images_path = f'{src_split}{"_task3" if src_split == "test" else ""}_images' + + with open(annot_fname, 'r') as f: + data = json.loads(f.read()) + + for x in data['data']: + if split == 'val' and x['file_path'] not in _VAL_IDS: + continue + elif split == 'train' and x['file_path'] in _VAL_IDS: + continue + image_path = os.path.join(_STVQA_PATH, images_path, x['file_path']) + # Always use high-res COCO images from train2014 directory. + if x['file_path'].startswith('coco-text'): + image_path = image_path.replace(os.path.join(images_path, 'coco-text'), + 'train2014') + yield x['question_id'], { + 'question_id': x['question_id'], + 'filename': x['file_path'], + 'image': image_path, + 'question': x['question'], + 'answers': x.get('answers', []), + } diff --git a/big_vision/datasets/tallyqa/tallyqa.py b/big_vision/datasets/tallyqa/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0fcde490c89f652fc2c3a9397670c340016f1eb7 --- /dev/null +++ b/big_vision/datasets/tallyqa/tallyqa.py @@ -0,0 +1,146 @@ +# Copyright 2024 Big Vision 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. + +"""Import TallyQA into TFDS format. Uses Visual Genome and COCO images. + +It's small data, so simple to run locally. First, download all the data: + + mkdir /tmp/data/ ; cd /tmp/data + wget http://images.cocodataset.org/zips/{train2014,val2014}.zip + wget https://cs.stanford.edu/people/rak248/VG_100K_2/images.zip + wget https://cs.stanford.edu/people/rak248/VG_100K_2/images2.zip + wget https://github.com/manoja328/tallyqa/blob/master/tallyqa.zip?raw=true + unzip *.zip + +Then, update the PATHs below and run conversion locally like so (make sure to +install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=tallyqa + +Example to load: + import tensorflow_datasets as tfds + dataset = tfds.load('tallyqa', split='train', data_dir='/tmp/tfds') + +The test split distinguishes between simple and complex questions. The train +split does not contain this information. We therefore set issimple to `-1` in +the train split to indicate it is not known. +""" + +import json + +import numpy as np +import tensorflow_datasets as tfds + + +_TALLYQA_PATH = '/tmp/data/tallyQA/' +_VISUAL_GENOME_PATH = '/tmp/data/visual_genome/' + +_COCO_PATH = '/tmp/data/coco/' + + +_DESCRIPTION = """ +TallyQA: Answering Complex Counting Questions +Most counting questions in visual question answering (VQA) datasets are simple +and require no more than object detection. Here, we study algorithms for complex +counting questions that involve relationships between objects, attribute +identification, reasoning, and more. To do this, we created TallyQA, the world's +largest dataset for open-ended counting. +""" + +_CITATION = """ +@inproceedings{acharya2019tallyqa, + title={TallyQA: Answering Complex Counting Questions}, + author={Acharya, Manoj and Kafle, Kushal and Kanan, Christopher}, + booktitle={AAAI}, + year={2019} +} +""" + +_HOMEPAGE = 'https://github.com/manoja328/TallyQA_dataset' + + +class TallyQA(tfds.core.GeneratorBasedBuilder): + """Import TallyQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'Initial release.'} + MANUAL_DOWNLOAD_INSTRUCTIONS = """ + There are three parts which should be downloaded: + * TallyQA (train / test json files) + * Visual Genome images (needed for train and test split) + * COCO (2014) train / val images (only needed for train split) + """ + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata.""" + features = tfds.features.FeaturesDict({ + 'image': tfds.features.Image(shape=(None, None, 3)), + 'image_id': tfds.features.Scalar(dtype=np.int32), + 'image_source': tfds.features.Text(), + 'question': tfds.features.Text(), + 'question_id': tfds.features.Scalar(dtype=np.int32), + 'answer': tfds.features.Scalar(dtype=np.int32), + 'issimple': tfds.features.Scalar(dtype=np.int32), + }) + + return tfds.core.DatasetInfo( + builder=self, + features=features, + description=_DESCRIPTION, + supervised_keys=None, + homepage=_HOMEPAGE, + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager) -> ...: + """Call the function which defines the splits.""" + del dl_manager + return { + 'train': self._generate_examples(split='train'), + 'test': self._generate_examples(split='test'), + } + + def _generate_examples(self, split: str) -> ...: + tally_json_file = f'{_TALLYQA_PATH}/{split}.json' + with open(tally_json_file, 'r') as f: + tally_json = json.load(f) + + for tally_qa in tally_json: + # The TallyQA images come from two sources: Visual Genome and COCO. + # Determine the correct dataset by inspecting the prefix. + filepath = tally_qa['image'] + if filepath.startswith('VG_100K'): + filepath = _VISUAL_GENOME_PATH + filepath + elif filepath.startswith('train2014') or filepath.startswith('val2014'): + filepath = _COCO_PATH + filepath + else: + raise ValueError(f'Unknown image path: {filepath}') + + tally_qa_dict = { + 'image': filepath, + 'image_id': tally_qa['image_id'], + 'image_source': tally_qa['data_source'], + 'question': tally_qa['question'], + 'question_id': tally_qa['question_id'], + 'answer': int(tally_qa['answer']), + } + if split == 'test': + # Field only present in test split. + tally_qa_dict.update({'issimple': tally_qa['issimple']}) + else: + # In the train split, we set issimple to -1 to indicate it is not known. + tally_qa_dict.update({'issimple': -1}) + tally_qa_id = f'{tally_qa_dict["image_id"]} / {tally_qa_dict["question_id"]}' # pylint: disable=line-too-long + yield tally_qa_id, tally_qa_dict diff --git a/big_vision/datasets/textcaps/textcaps.py b/big_vision/datasets/textcaps/textcaps.py new file mode 100644 index 0000000000000000000000000000000000000000..4b004f03082d4e576c3a306dab3a94a3887059b3 --- /dev/null +++ b/big_vision/datasets/textcaps/textcaps.py @@ -0,0 +1,152 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements textcaps val-set in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: + + mkdir -p /tmp/data/textcaps + cd /tmp/data/textcaps + curl -O https://dl.fbaipublicfiles.com/textvqa/data/textcaps/TextCaps_0.1_train.json + curl -O https://dl.fbaipublicfiles.com/textvqa/data/textcaps/TextCaps_0.1_val.json + curl -O https://dl.fbaipublicfiles.com/textvqa/data/textcaps/TextCaps_0.1_test.json + curl -O https://dl.fbaipublicfiles.com/textvqa/images/train_val_images.zip + curl -O https://dl.fbaipublicfiles.com/textvqa/images/test_images.zip + unzip train_val_images.zip + rm train_val_images.zip + unzip test_images.zip + rm test_images.zip + +Then, run conversion locally (make sure to install tensorflow-datasets for the +`tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=textcaps + + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('text_caps', split='val', data_dir='/tmp/tfds') +""" +import collections +import json +import os + +from absl import logging +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """TextCaps dataset.""" + +# pylint: disable=line-too-long +_CITATION = ( + '@inproceedings{sidorov2019textcaps,' + 'title={TextCaps: a Dataset for Image Captioningwith Reading Comprehension},' + 'author={Sidorov, Oleksii and Hu, Ronghang and Rohrbach, Marcus and Singh, Amanpreet},' + 'journal={European Conference on Computer Vision},' + 'year={2020}}') +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_FILEPATH = '/tmp/data/textcaps/' +_TRAIN_FILES = '/tmp/data/textcaps/TextCaps_0.1_train.json' +_VAL_FILES = '/tmp/data/textcaps/TextCaps_0.1_val.json' +_TEST_FILES = '/tmp/data/textcaps/TextCaps_0.1_test.json' + + +class TextCaps(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for TextCaps dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = { + '1.0.0': 'Initial release.', + } + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata. + + (tfds.core.DatasetInfo object) + These are the features of your dataset like images, labels, etc. + """ + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'image/id': tfds.features.Text(), + 'image_filepath': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'texts': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, # Set to `None` to disable + homepage='https://textvqa.org/textcaps/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + def group_by_id(data, image_dir): + id_to_example = collections.defaultdict(list) + for ex in data: + id_to_example[ex['image_id']].append(ex) + + for k, exs in id_to_example.items(): + image_ids, image_names, texts = [], [], [] + for ex in exs: + image_ids.append(ex['image_id']) + image_names.append(ex['image_name']) + if ex.get('caption_str'): + texts.append(ex.get('caption_str')) + assert len(set(image_ids)) == 1 + assert len(set(image_names)) == 1 + image_filepath = os.path.join( + _FILEPATH, image_dir, str(image_names[0])+'.jpg') + id_to_example[k] = { + 'image/id': image_ids[0], + 'image_filepath': image_filepath, + 'image': image_filepath, + 'texts': texts, + } + return id_to_example + + # Returns the Dict[split names, Iterator[Key, Example]] + with open(_TRAIN_FILES) as f: + train_data = group_by_id(json.load(f)['data'], 'train_images') + with open(_VAL_FILES) as f: + val_data = group_by_id(json.load(f)['data'], 'train_images') + with open(_TEST_FILES) as f: + test_data = group_by_id(json.load(f)['data'], 'test_images') + return { + 'train': self._generate_examples(train_data), + 'val': self._generate_examples(val_data), + 'test': self._generate_examples(test_data), + } + + def _generate_examples(self, data): + """Generate a tf.Example object. + + This contains the image, objects, attributes, regions and relationships. + + Args: + data: a dictionary with the image/id. + + Yields: + (key, example) tuples from dataset. The example has format specified in + the above DatasetInfo. + """ + for k, v in data.items(): + yield k, v diff --git a/big_vision/datasets/textvqa/textvqa.py b/big_vision/datasets/textvqa/textvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..a9b996c682b9bf6fe156e1dcbc1b089ac40cf751 --- /dev/null +++ b/big_vision/datasets/textvqa/textvqa.py @@ -0,0 +1,186 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements textvqa in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: + + mkdir -p /tmp/data/textvqa + cd /tmp/data/textvqa + curl -O https://dl.fbaipublicfiles.com/textvqa/images/train_val_images.zip + curl -O https://dl.fbaipublicfiles.com/textvqa/images/test_images.zip + curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_0.5.1_train.json + curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_0.5.1_val.json + curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_0.5.1_test.json + # The Rosetta_OCR files are probably not needed. + # curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_Rosetta_OCR_v0.2_train.json + # curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_Rosetta_OCR_v0.2_val.json + # curl -O https://dl.fbaipublicfiles.com/textvqa/data/TextVQA_Rosetta_OCR_v0.2_test.json + unzip train_val_images.zip + rm train_val_images.zip + unzip test_images.zip + rm test_images.zip + # Background: at https://textvqa.org/dataset/ it says: + # "Note: Some of the images in OpenImages are rotated, + # please make sure to check the Rotation field in the Image IDs files + # for train and test." + curl -O https://storage.googleapis.com/openimages/2018_04/train/train-images-boxable-with-rotation.csv + curl -O https://storage.googleapis.com/openimages/2018_04/test/test-images-with-rotation.csv + mv train-images-boxable-with-rotation.csv train_images/rotation.csv + mv test-images-with-rotation.csv test_images/rotation.csv + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=textvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('textvqa', split='train', data_dir='/tmp/tfds') +""" +import json +import os + +from absl import logging +import numpy as np +import pandas as pd +import tensorflow as tf +import tensorflow_datasets as tfds + + +_DESCRIPTION = """TextVqa dataset.""" + +# pylint: disable=line-too-long +_CITATION = ( + '@inproceedings{singh2019towards,' + 'title={Towards VQA Models That Can Read},' + 'author={Singh, Amanpreet and Natarjan, Vivek and Shah, Meet and Jiang, Yu and Chen, Xinlei and Parikh, Devi and Rohrbach, Marcus},' + 'booktitle={Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition},' + 'pages={8317-8326},' + 'year={2019}}' + ) +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above and use these: +_FILEPATH = '/tmp/data/textvqa/' +_TRAIN_FILES = '/tmp/data/textvqa/TextVQA_0.5.1_train.json' +_VAL_FILES = '/tmp/data/textvqa/TextVQA_0.5.1_val.json' +_TEST_FILES = '/tmp/data/textvqa/TextVQA_0.5.1_test.json' +_ROTATION_CSV = 'rotation.csv' + + +class TextVqa(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for textvqa dataset.""" + + VERSION = tfds.core.Version('1.0.1') + RELEASE_NOTES = { + '1.0.0': 'Initial release.', + '1.0.1': 'Undo rotation for known rotated images.', + } + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the dataset metadata. + + (tfds.core.DatasetInfo object) + These are the features of your dataset like images, labels, etc. + """ + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'image/id': tfds.features.Scalar(np.int32), + 'image_filepath': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question_id': tfds.features.Scalar(np.int32), + 'question': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + }), + supervised_keys=None, # Set to `None` to disable + homepage='https://textvqa.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + def json_to_examples(data, image_dir): + # Load rotation csv. + logging.info('Processing %d items in %s', len(data), image_dir) + rot = pd.read_csv(os.path.join(_FILEPATH, image_dir, _ROTATION_CSV)) + rotation_by_id = {} + for row in rot.itertuples(): + rotation = int(row.Rotation) if not np.isnan(row.Rotation) else 0 + rotation_by_id[row.ImageID] = rotation + + examples = {} + for v in data: + image_id = str(v['image_id']) + image_filepath = os.path.join(_FILEPATH, image_dir, image_id + '.jpg') + question_id = v['question_id'] + examples[question_id] = { + 'image/id': question_id, + 'image_filepath': image_filepath, + 'image': image_filepath, + 'rotation': rotation_by_id[image_id], + 'question_id': question_id, + 'question': v['question'], + 'answers': v.get('answers', []), # No answers in test set. + } + return examples + + # Returns the Dict[split names, Iterator[Key, Example]] + with open(_TRAIN_FILES) as f: + train_data = json_to_examples(json.load(f)['data'], 'train_images') + with open(_VAL_FILES) as f: + # Validation images are stored in the train_images folder. + val_data = json_to_examples(json.load(f)['data'], 'train_images') + with open(_TEST_FILES) as f: + test_data = json_to_examples(json.load(f)['data'], 'test_images') + return { + 'train': self._generate_examples(train_data), + 'val': self._generate_examples(val_data), + 'test': self._generate_examples(test_data), + } + + def _generate_examples(self, data): + """Generate a tf.Example object. + + Args: + data: a dictionary with the image/id. + + Yields: + (key, example) tuples from dataset. The example has format specified in + the above DatasetInfo. + """ + for k, v in data.items(): + # If the image is rotated, we undo the rotation here and re-encode. + image_bytes = open(v['image_filepath'], 'rb').read() + if v['rotation'] != 0: + rotation = v['rotation'] + assert rotation % 90 == 0 + turns = int(rotation / 90) + image = tf.image.decode_jpeg(image_bytes) + image_bytes = tf.io.encode_jpeg( + tf.image.rot90(image, turns), quality=100 + ).numpy() + # If no rotation was needed, we just pass along the unchanged bytes. + v['image'] = image_bytes + + # Now all rotation should have been accounted for. And we don't want to + # pass on the (now obsolete) rotation info as features. + del v['rotation'] + + yield k, v diff --git a/big_vision/datasets/tfds.py b/big_vision/datasets/tfds.py new file mode 100644 index 0000000000000000000000000000000000000000..4561c0f4111c877d1d4693d091a2eb7e3778fe08 --- /dev/null +++ b/big_vision/datasets/tfds.py @@ -0,0 +1,95 @@ +# Copyright 2024 Big Vision 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. + +"""TensorFlow Datasets as data source for big_vision.""" +import functools + +import big_vision.datasets.core as ds_core +import jax +import numpy as np +import overrides +import tensorflow as tf +import tensorflow_datasets as tfds + + +class DataSource(ds_core.DataSource): + """Use TFDS as a data source.""" + + def __init__(self, name, split, data_dir=None, skip_decode=("image",)): + self.builder = _get_builder(name, data_dir) + self.split = split + # Each host is responsible for a fixed subset of data + process_splits = tfds.even_splits(split, jax.process_count()) + self.process_split = process_splits[jax.process_index()] + self.skip_decode = skip_decode + + @overrides.overrides + def get_tfdata( + self, ordered=False, *, process_split=True, allow_cache=True, **kw): + # The tf.data may use a lot of RAM, so we need to expose the option of not + # keeping this in memory when we use lots of input pipelines, such as when + # having many ephemeral evaluators. + return (_cached_get_dataset if allow_cache else _get_dataset)( + self.builder, self.skip_decode, + split=self.process_split if process_split else self.split, + shuffle_files=not ordered, + **kw) + + @property + @overrides.overrides + def total_examples(self): + return self.builder.info.splits[self.split].num_examples + + @overrides.overrides + def num_examples_per_process(self): + splits = tfds.even_splits(self.split, jax.process_count()) + return [self.builder.info.splits[s].num_examples for s in splits] + + +@functools.cache +def _get_builder(dataset, data_dir): + if dataset == "from_data_dir": + return tfds.builder_from_directory(data_dir) + else: + return tfds.builder(dataset, data_dir=data_dir, try_gcs=True) + + +# Cache as it may well take 1-2min on large datasets, and we may use the same +# multiple times (eg various evaluators). +def _get_dataset(builder, skip_decode, **kw): + """Returns a tf.data to be used.""" + rckw = {k: kw.pop(k) for k in ("shuffle_seed",) if k in kw} + ds = builder.as_dataset( + read_config=tfds.ReadConfig( + skip_prefetch=True, # We prefetch after pipeline. + try_autocache=False, # We control this, esp. for few-shot. + add_tfds_id=True, + **rckw, + ), + decoders={ + f: tfds.decode.SkipDecoding() + for f in skip_decode if f in builder.info.features + }, + **kw) + + def _hash_tfds_id(example): + id_ = tf.strings.to_hash_bucket_strong( + example["tfds_id"], + np.iinfo(np.uint32).max, # Max value + [3714561454027272724, 8800639020734831960]) # Magic. + example["_id"] = tf.bitcast(id_, tf.int32)[0] # good device dtype. + return example + + return ds.map(_hash_tfds_id) +_cached_get_dataset = functools.cache(_get_dataset) diff --git a/big_vision/datasets/vizwizvqa/vizwizvqa.py b/big_vision/datasets/vizwizvqa/vizwizvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9f30a124e916f0557e1d101b2952a22485ccc5bd --- /dev/null +++ b/big_vision/datasets/vizwizvqa/vizwizvqa.py @@ -0,0 +1,128 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Implements VizWizVQA dataset in TFDS structure. + +It's small data, so simple to run locally. First, copy the data to local disk: + + mkdir -p /tmp/data/vizwizvqa + + wget -O https://vizwiz.cs.colorado.edu/VizWiz_final/images/train.zip /tmp/data/vizwizvqa + wget -O https://vizwiz.cs.colorado.edu/VizWiz_final/images/val.zip /tmp/data/vizwizvqa + wget -O https://vizwiz.cs.colorado.edu/VizWiz_final/images/test.zip /tmp/data/vizwizvqa + +Then, run conversion locally +(make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=vizwizvqa + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('vizwizvqa', split='train', data_dir='/tmp/tfds') +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_DESCRIPTION = """VizWiz VQA Dataset.""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{gurari2018vizwiz, + title={Vizwiz grand challenge: Answering visual questions from blind people}, + author={Gurari, Danna and Li, Qing and Stangl, Abigale J and Guo, Anhong and Lin, Chi and Grauman, Kristen and Luo, Jiebo and Bigham, Jeffrey P}, + booktitle={Proceedings of the IEEE conference on computer vision and pattern recognition}, + pages={3608--3617}, + year={2018} +} +} +""" +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_VIZWIZVQA_PATH = '/tmp/data/vizwizvqa/' + + +class VizWizVQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for VizWizVQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'question': tfds.features.Text(), + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'answers': tfds.features.Sequence(tfds.features.Text()), + # can be "yes" "no" and "maybe" strings + 'answer_confidences': tfds.features.Sequence(tfds.features.Text()), + 'answerable': tfds.features.Scalar(np.int32), + 'question_id': tfds.features.Scalar(np.int32), + }), + supervised_keys=None, + homepage='https://vizwiz.org/tasks-and-datasets/vqa/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {split: self._generate_examples(split) + for split in ('val', 'train', 'test',)} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from test set.""" + annot_fname = os.path.join(_VIZWIZVQA_PATH, 'annotations', f'{split}.json') + + with open(annot_fname, 'r') as f: + data = json.loads(f.read()) + + for v in data: + + answers = [] + answer_confidences = [] + + image_file = v['image'] + answerable = -1 + if split != 'test': + for answer in v['answers']: + # A couple of answers in the train set are empty strings. + if not answer['answer']: + continue + answers.append(answer['answer']) + answer_confidences.append(answer['answer_confidence']) + answerable = v['answerable'] + + question_id = image_file[:-4] + question_id = int(question_id.split('_')[-1]) + + yield v['image'], { + 'question': v['question'], + 'image/filename': image_file, + 'question_id': question_id, + 'image': os.path.join(_VIZWIZVQA_PATH, split, image_file), + 'answers': answers, + 'answer_confidences': answer_confidences, + 'answerable': answerable, + } diff --git a/big_vision/datasets/vqa/vqa.py b/big_vision/datasets/vqa/vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..b310ae20cf2efca3eeb18d55819a852bb9a2479a --- /dev/null +++ b/big_vision/datasets/vqa/vqa.py @@ -0,0 +1,147 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Import VQAv2 into TFDS format. Uses coco-2014 images. + +It's small data, so simple to run locally. First, download all the data: + + mkdir /tmp/data/ ; cd /tmp/data + wget http://images.cocodataset.org/zips/{train2014,val2014,test2015}.zip + wget https://s3.amazonaws.com/cvmlp/vqa/mscoco/vqa/v2_Questions_{Train,Val,Test}_mscoco.zip + wget https://s3.amazonaws.com/cvmlp/vqa/mscoco/vqa/v2_Annotations_{Train,Val}_mscoco.zip + unzip '*.zip' + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=vqa + +It runs at around 750 examples/sec, so takes around 25min for the 1.2M questions. +Each question is an example; images are repeated, a bit wasteful, but disk is cheap. + + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load('vqa', split='train', data_dir='/tmp/tfds') +""" +import json +import os + +import numpy as np +import tensorflow_datasets as tfds + + +_VQAV2_PATH = '/tmp/data' +_IMAGE_PATH = '/tmp/data' + + +_CITATION = ( + '@InProceedings{balanced_vqa_v2,' + 'author = {Yash Goyal and Tejas Khot and ' + 'Douglas Summers{-}Stay and Dhruv Batra and Devi Parikh},' + 'title = {Making the {V} in {VQA} Matter: Elevating the Role of Image' + 'Understanding in {V}isual {Q}uestion {A}nswering},' + 'booktitle = {Computer Vision and Pattern Recognition (CVPR)},' + 'year = {2017},}') + + +class Vqa(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for VQAv2 dataset.""" + + VERSION = tfds.core.Version('3.0.0') + RELEASE_NOTES = {'3.0.0': 'Format as needed for PaliGemma'} + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description='The VQAv2 dataset.', + features=tfds.features.FeaturesDict({ + 'image/id': np.int32, + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question_id': np.int32, + 'question_type': tfds.features.Text(), + 'question_text': tfds.features.Text(), + 'answer_type': tfds.features.Text(), + 'answers': tfds.features.Sequence(tfds.features.Text()), + 'answer_confidences': tfds.features.Sequence( + tfds.features.ClassLabel(names=['no', 'maybe', 'yes'])), + 'top_answer': tfds.features.Text(), + }), + homepage='https://visualqa.org/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return { + 'train': self._generate_examples('train2014'), + 'validation': self._generate_examples('val2014'), + 'test': self._generate_examples('test2015'), + 'test-dev': self._generate_examples('test-dev2015', 'test2015'), + } + + def _generate_examples(self, split, image_folder=None): + """Yields (key, example) tuples from test set.""" + image_folder = image_folder or split + + # The questions file has fields image_id, question, question_id. + with open(os.path.join( + _VQAV2_PATH, f'v2_OpenEnded_mscoco_{split}_questions.json')) as f: + examples = json.load(f)['questions'] + + # The questions file has fields: image_id, question_id, answers, + # answer_type, question_type, multiple_choice_answer. + if 'test' not in split: + with open(os.path.join( + _VQAV2_PATH, f'v2_mscoco_{split}_annotations.json')) as f: + annots = {a['question_id']: a for a in json.load(f)['annotations']} + + for ex in examples: + qid = ex['question_id'] + ex = { + 'image/id': ex['image_id'], + 'question_id': qid, + 'question_text': ex['question'], + } + if 'test' not in split: + fname = f'COCO_{image_folder}_{ex["image/id"]:012d}.jpg' + ex['image/filename'] = fname + ex['image'] = os.path.join(_IMAGE_PATH, image_folder, fname) + ann = annots[qid] + ex['question_type'] = ann['question_type'] + ex['answer_type'] = ann['answer_type'] + ex['answers'] = [a['answer'] for a in ann['answers']] + ex['answer_confidences'] = [a['answer_confidence'] + for a in ann['answers']] + ex['top_answer'] = ann['multiple_choice_answer'] + else: + # For test images, a few are from the wrong year... + fname = f'COCO_{image_folder}_{ex["image/id"]:012d}.jpg' + ex['image/filename'] = fname + if os.path.isfile(path := os.path.join(_IMAGE_PATH, image_folder, fname)): + ex['image'] = path + else: + print(ex['image/id']) + continue + ex['question_type'] = '' + ex['answer_type'] = '' + ex['answers'] = [] + ex['answer_confidences'] = [] + ex['top_answer'] = '' + yield qid, ex diff --git a/big_vision/datasets/widgetcap/widgetcap.py b/big_vision/datasets/widgetcap/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..cfb7cab5589291e7f77eda05df3ee02689a1040a --- /dev/null +++ b/big_vision/datasets/widgetcap/widgetcap.py @@ -0,0 +1,151 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Import widgetcap into TFDS format. + + Widget Captioning all requires images from the RICO dataset: + mkdir -p /tmp/data/rico_images ; cd /tmp/data/rico_images + wget + https://storage.googleapis.com/crowdstf-rico-uiuc-4540/rico_dataset_v0.1/unique_uis.tar.gz + tar xvfz unique_uis.tar.gz + rm unique_uis.tar.gz + + Widget Captioning: + mkdir - /tmp/data/widget_captioning ; cd /tmp/data/widget_captioning + git clone https://github.com/google-research-datasets/widget-caption.git + cp widget-caption/widget_captions.csv ./ + cp widget-caption/split/*.txt ./ + rm -rf widget-caption + +Then, run conversion locally (make sure to install tensorflow-datasets for the +`tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=widgetcap + +Example to load: + + import tensorflow_datasets as tfds + dataset_augmented = tfds.load('widgetcap', split='train', + data_dir='/tmp/tfds') +""" +import csv +import json +import os + +import numpy as np +from PIL import Image +import tensorflow_datasets as tfds + +_DATASET_DIR = '/tmp/data/widget_captioning' +# Dataset property indicating the y-dim of the canvas +_RICO_CANVAS_Y = 2560 +_IMAGE_DIR = '/tmp/data/rico_images/combined' + +_CITATION = ( + '@inproceedings{Li2020WidgetCG,title={Widget Captioning: Generating Natural' + ' Language Description for MobileUser Interface Elements},author={Y. Li and' + ' Gang Li and Luheng He and Jingjie Zheng and Hong Li and Zhiwei' + ' Guan},booktitle={Conference on Empirical Methods in Natural Language' + ' Processing},year={2020},}' +) + + +class Widgetcap(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for widgetcap dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'Format as needed for PaliGemma'} + + def _info(self) -> tfds.core.DatasetInfo: + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description='The widgetcap dataset.', + features=tfds.features.FeaturesDict({ + 'image/id': tfds.features.Text(), + 'image/filename': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'texts': tfds.features.Sequence(tfds.features.Text()), + 'bbox': tfds.features.BBoxFeature(), + 'screen_id': tfds.features.Text(), + 'node_id': tfds.features.Text(), + 'height': np.int32, + 'width': np.int32, + }), + homepage='https://github.com/google-research-datasets/widget-caption', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return { + 'train': self._generate_examples('train'), + 'dev': self._generate_examples('dev'), + 'test': self._generate_examples('test'), + } + + def _generate_examples(self, split): + """Yields (key, example) tuples from the dataset.""" + split_screen_ids = set() + with open(os.path.join(_DATASET_DIR, split + '.txt')) as f: + for line in f: + split_screen_ids.add(line.strip()) + + with open(os.path.join(_DATASET_DIR, 'widget_captions.csv')) as f: + for row in csv.DictReader(f): + if row['screenId'] in split_screen_ids: + id_, example = self._get_example( + row['screenId'], row['nodeId'], row['captions'] + ) + yield id_, example + + def _get_node_box(self, screen_id, node_id, height): + index_list = [int(i) for i in node_id.split('.')[1:]] + with open(os.path.join(_IMAGE_DIR, screen_id + '.json')) as f: + view = json.load(f) + curr_node = view['activity']['root'] + for index in index_list: + curr_node = curr_node['children'][index] + normalized_bounds = map( + lambda x: x * height / _RICO_CANVAS_Y, curr_node['bounds'] + ) + return normalized_bounds + + def _get_example(self, screen_id, node_id, captions): + image = Image.open(os.path.join(_IMAGE_DIR, screen_id + '.jpg')) + width, height = image.size + # get bounding box coordinates + xmin, ymin, xmax, ymax = self._get_node_box(screen_id, node_id, height) + + image_id = f'{screen_id}_{node_id}' + example = { + 'image/id': image_id, + 'image/filename': screen_id + '.jpg', + 'image': os.path.join(_IMAGE_DIR, screen_id + '.jpg'), + 'texts': captions.split('|'), + 'bbox': tfds.features.BBox( + ymin=ymin / height, + xmin=xmin / width, + ymax=ymax / height, + xmax=xmax / width, + ), + 'screen_id': screen_id, + 'node_id': node_id, + 'height': height, + 'width': width, + } + return image_id, example diff --git a/big_vision/datasets/xgqa/xgqa.py b/big_vision/datasets/xgqa/xgqa.py new file mode 100644 index 0000000000000000000000000000000000000000..ea94addf8bd04fa1ea48694476b6e9cd7911ffe6 --- /dev/null +++ b/big_vision/datasets/xgqa/xgqa.py @@ -0,0 +1,145 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +"""Generates xGQA in a TFDS-ready structure. + +First, download the data: + mkdir -p /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_bn.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_de.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_en.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_id.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_ko.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_pt.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_ru.json -P /tmp/data/xgqa/annotations + wget https://raw.githubusercontent.com/e-bug/iglue/main/datasets/xGQA/annotations/zero_shot/testdev_balanced_questions_zh.json -P /tmp/data/xgqa/annotations + wget https://downloads.cs.stanford.edu/nlp/data/gqa/images.zip -P /tmp/data/xgqa/ + unzip /tmp/data/xgqa/images.zip -d /tmp/data/xgqa/ + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=xgqa + +Example to load: + +import tensorflow_datasets as tfds +dataset = tfds.load( + 'xgqa', split='test_zs_en', + data_dir='/tmp/tfds') +""" +import json +import os + +import tensorflow_datasets as tfds + +_DESCRIPTION = """xGQA (uses GQA images).""" + +# pylint: disable=line-too-long +_CITATION = ( + '@inproceedings{pfeiffer-etal-2022-xgqa,' + 'title = "x{GQA}: Cross-Lingual Visual Question Answering",' + 'author = "Pfeiffer, Jonas and' + ' Geigle, Gregor and' + ' Kamath, Aishwarya and' + ' Steitz, Jan-Martin and' + ' Roth, Stefan and' + ' Vuli{\'c}, Ivan and' + ' Gurevych, Iryna",' + 'booktitle = "Findings of the Association for Computational Linguistics: ' + 'ACL 2022",' + 'month = may,' + 'year = "2022",' + 'address = "Dublin, Ireland",' + 'publisher = "Association for Computational Linguistics",' + 'url = "https://aclanthology.org/2022.findings-acl.196",' + 'doi = "10.18653/v1/2022.findings-acl.196",' + 'pages = "2497--2511",' + '}' +) +# pylint: enable=line-too-long + +# When running locally (recommended), copy files as above an use these: +_DATA_PATH = '/tmp/data/xgqa/' +_IMAGE_PATH = '/tmp/data/xgqa/images/' + +LANGUAGES = frozenset(['bn', 'de', 'en', 'id', 'ko', 'pt', 'ru', 'zh']) + + +class XGQA(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for XGQA dataset.""" + + VERSION = tfds.core.Version('1.0.0') + RELEASE_NOTES = {'1.0.0': 'First release.'} + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'example_id': tfds.features.Text(), + 'image/id': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'question': tfds.features.Text(), + 'answer': tfds.features.Text(), + }), + supervised_keys=None, + homepage='https://github.com/adapter-hub/xGQA', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + d = dict() + for l in LANGUAGES: + d.update({ + f'test_zs_{l}': self._generate_examples('test', 'zero_shot', l), + f'test_fs_{l}': self._generate_examples('test', 'few_shot', l), + f'dev_fs_{l}': self._generate_examples('test', 'few_shot', l), + f'train_fs1_{l}': self._generate_examples('train_1', 'few_shot', l), + f'train_fs5_{l}': self._generate_examples('train_5', 'few_shot', l), + f'train_fs10_{l}': self._generate_examples('train_10', 'few_shot', l), + f'train_fs20_{l}': self._generate_examples('train_20', 'few_shot', l), + f'train_fs25_{l}': self._generate_examples('train_25', 'few_shot', l), + f'train_fs48_{l}': self._generate_examples('train_48', 'few_shot', l), + }) + return d + + def _generate_examples(self, split, num_shots, lang): + """Yields (key, example) tuples.""" + # Loads the questions for each image. + if num_shots == 'few_shot': + file_path = os.path.join(_DATA_PATH, 'annotations', 'few_shot', lang, + f'{split}.json') + elif num_shots == 'zero_shot': + file_path = os.path.join(_DATA_PATH, 'annotations', 'zero_shot', + f'testdev_balanced_questions_{lang}.json') + else: + raise ValueError(f'Unknown num_shots: {num_shots}') + with open(file_path, 'r') as f: + entries = json.load(f) + + # Make one entry per question-answer pair. + for question_id, question_data in entries.items(): + example_id = f'{question_id}_{lang}' + yield example_id, { + 'example_id': example_id, + 'image/id': question_data['imageId'], + 'image': os.path.join(_IMAGE_PATH, f'{question_data["imageId"]}.jpg'), + 'question': question_data['question'], + 'answer': question_data['answer'], + } diff --git a/big_vision/datasets/xm3600/xm3600.py b/big_vision/datasets/xm3600/xm3600.py new file mode 100644 index 0000000000000000000000000000000000000000..599b3bd4df3fb590f09b24b9d99afe04921f19bd --- /dev/null +++ b/big_vision/datasets/xm3600/xm3600.py @@ -0,0 +1,136 @@ +# Copyright 2024 Big Vision 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. + +# pylint: disable=line-too-long +r"""Generates XM3600 in a TFDS-ready structure. + +First, download the captions from https://google.github.io/crossmodal-3600/ and the images from https://cocodataset.org/#download. +The coco Karpathy split is available at http://cs.stanford.edu/people/karpathy/deepimagesent/caption_datasets.zip: + mkdir -p /tmp/data/xm3600 + wget https://google.github.io/crossmodal-3600/web-data/captions.zip -P /tmp/data/xm3600 + unzip /tmp/data/xm3600/captions.zip -d /tmp/data/xm3600/ + wget https://open-images-dataset.s3.amazonaws.com/crossmodal-3600/images.tgz ta-P /tmp/data/xm3600 + mkdir /tmp/data/xm3600/images + tar -xzf /tmp/data/xm3600/images.tgz -C /tmp/data/xm3600/images + +Then, run conversion locally (make sure to install tensorflow-datasets for the `tfds` util): + + cd big_vision/datasets + env TFDS_DATA_DIR=/tmp/tfds tfds build --datasets=xm3600 + +Example to load: + + import tensorflow_datasets as tfds + dataset = tfds.load( + 'xm3600', split='en', + data_dir='/tmp/tfds') +""" + +import json +import os.path + +import tensorflow_datasets as tfds + +_DESCRIPTION = """ +COCO image + captions, translated from English to 35 languages (English incl.). +""" + +# pylint: disable=line-too-long +_CITATION = """ +@inproceedings{thapliyal-etal-2022-crossmodal, + title = "Crossmodal-3600: A Massively Multilingual Multimodal Evaluation Dataset", + author = "Thapliyal, Ashish V. and + Pont Tuset, Jordi and + Chen, Xi and + Soricut, Radu", + editor = "Goldberg, Yoav and + Kozareva, Zornitsa and + Zhang, Yue", + booktitle = "Proceedings of the 2022 Conference on Empirical Methods in Natural Language Processing", + month = dec, + year = "2022", + address = "Abu Dhabi, United Arab Emirates", + publisher = "Association for Computational Linguistics", + url = "https://aclanthology.org/2022.emnlp-main.45", + doi = "10.18653/v1/2022.emnlp-main.45", + pages = "715--729", +} +""" +# pylint: enable=line-too-long + + +_CAPTIONS_PATH = '/tmp/data/xm3600' +_IMAGES_PATH = '/tmp/data/xm3600/images' + +XM3600_LANGUAGES = [ + 'ar', 'bn', 'cs', 'da', 'de', 'el', 'en', 'es', 'fa', 'fi', 'fil', 'fr', + 'he', 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'mi', 'nl', 'no', 'pl', + 'pt', 'quz', 'ro', 'ru', 'sv', 'sw', 'te', 'th', 'tr', 'uk', 'vi', 'zh' +] + + +class Xm3600(tfds.core.GeneratorBasedBuilder): + """DatasetBuilder for XM3600 dataset.""" + + VERSION = tfds.core.Version('1.0.1') + RELEASE_NOTES = { + '1.0.0': 'First release.', + '1.0.1': 'Add captions/tokenized feature to compute metrics (eg CIDEr).', + } + + def _info(self): + """Returns the metadata.""" + + return tfds.core.DatasetInfo( + builder=self, + description=_DESCRIPTION, + features=tfds.features.FeaturesDict({ + 'image/id': tfds.features.Text(), + 'image': tfds.features.Image(encoding_format='jpeg'), + 'captions': tfds.features.Sequence(tfds.features.Text()), + 'captions/tokenized': tfds.features.Sequence(tfds.features.Text()), + 'language': tfds.features.Text(), + }), + supervised_keys=None, + homepage='https://google.github.io/crossmodal-3600/', + citation=_CITATION, + ) + + def _split_generators(self, dl_manager: tfds.download.DownloadManager): + """Returns SplitGenerators.""" + return {lang: self._generate_examples(lang) for lang in XM3600_LANGUAGES} + + def _generate_examples(self, split: str): + """Yields (key, example) tuples from dataset.""" + language = split + + annot_fname = os.path.join(_CAPTIONS_PATH, 'captions.jsonl') + data = {} + tok_data = {} + with open(annot_fname, 'r') as f: + for line in f: + j = json.loads(line) + image_id = f'{j["image/key"]}_{language}' + captions = j[language]['caption'] + data[image_id] = captions + tok_data[image_id] = j[language]['caption/tokenized'] + + for image_id, captions in data.items(): + yield image_id, { + 'image/id': image_id, + 'image': os.path.join(_IMAGES_PATH, f'{image_id.split("_")[0]}.jpg'), + 'captions': captions, + 'captions/tokenized': tok_data[image_id], + 'language': language, + } diff --git a/big_vision/evaluators/__init__.py b/big_vision/evaluators/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/evaluators/classification.py b/big_vision/evaluators/classification.py new file mode 100644 index 0000000000000000000000000000000000000000..263ead8f5027f4b8e640b9ba42a72b3cbc33adf2 --- /dev/null +++ b/big_vision/evaluators/classification.py @@ -0,0 +1,76 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for the classfication task.""" +# pylint: disable=consider-using-from-import + +import functools + +from big_vision.evaluators import common +import big_vision.utils as u +import jax +import jax.numpy as jnp + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +# To avoid re-compiling the function for every new instance of the same +# evaluator on a different dataset! +@functools.cache +def get_eval_fn(predict_fn, loss_name): + """Produces eval function, also applies pmap.""" + @jax.jit + def _eval_fn(train_state, batch, labels, mask): + logits, *_ = predict_fn(train_state, batch) + + # Ignore the entries with all zero labels for evaluation. + mask *= labels.max(axis=1) + + loss = getattr(u, loss_name)( + logits=logits, labels=labels, reduction=False) + loss = jnp.sum(loss * mask) + + top1_idx = jnp.argmax(logits, axis=1) + # Extracts the label at the highest logit index for each image. + top1_correct = jnp.take_along_axis( + labels, top1_idx[:, None], axis=1)[:, 0] + ncorrect = jnp.sum(top1_correct * mask) + nseen = jnp.sum(mask) + return ncorrect, loss, nseen + return _eval_fn + + +class Evaluator: + """Classification evaluator.""" + + def __init__(self, predict_fn, loss_name, label_key='labels', **kw): + self.get_data_iter, self.steps = common.eval_input_pipeline(**kw) + self.eval_fn = get_eval_fn(predict_fn, loss_name) + self.label_key = label_key + + def run(self, train_state): + """Computes all metrics.""" + ncorrect, loss, nseen = 0, 0, 0 + for _, batch in zip(range(self.steps), self.get_data_iter()): + labels, mask = batch.pop(self.label_key), batch.pop('_mask') + batch_ncorrect, batch_losses, batch_nseen = jax.device_get( + self.eval_fn(train_state, batch, labels, mask)) + ncorrect += batch_ncorrect + loss += batch_losses + nseen += batch_nseen + yield ('prec@1', ncorrect / nseen) + yield ('loss', loss / nseen) diff --git a/big_vision/evaluators/common.py b/big_vision/evaluators/common.py new file mode 100644 index 0000000000000000000000000000000000000000..42dcdbb4b52a5208673821b9c68df246709fcf6d --- /dev/null +++ b/big_vision/evaluators/common.py @@ -0,0 +1,228 @@ +# Copyright 2024 Big Vision 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. + +"""Utils for evaluators in general.""" + +import dataclasses +import functools +import importlib +import json +import os +from typing import Any, Callable + +from absl import flags +from big_vision import input_pipeline +from big_vision.datasets import core as ds_core +from big_vision.pp import builder as pp_builder +import big_vision.utils as u +import flax +import jax +import numpy as np + +from tensorflow.io import gfile + + +def from_config(config, predict_fns, + write_note=lambda s: s, + get_steps=lambda key, cfg: cfg[f"{key}_steps"], + devices=None): + """Creates a list of evaluators based on `config`.""" + evaluators = [] + specs = config.get("evals", {}) + + for name, cfg in specs.items(): + write_note(name) + + # Pop all generic settings off so we're left with eval's kwargs in the end. + cfg = cfg.to_dict() + module = cfg.pop("type", name) + pred_key = cfg.pop("pred", "predict") + pred_kw = cfg.pop("pred_kw", None) + prefix = cfg.pop("prefix", f"{name}/") + cfg.pop("skip_first", None) + logsteps = get_steps("log", cfg) + for typ in ("steps", "epochs", "examples", "percent"): + cfg.pop(f"log_{typ}", None) + + # Use same batch_size as eval by default, to reduce fragmentation. + # TODO: eventually remove all the deprecated names... + cfg["batch_size"] = cfg.get("batch_size") or config.get("batch_size_eval") or config.get("input.batch_size") or config.get("batch_size") # pylint: disable=line-too-long + + module = importlib.import_module(f"big_vision.evaluators.{module}") + + if devices is not None: + cfg["devices"] = devices + + api_type = getattr(module, "API", "pmap") + if api_type == "pmap" and "devices" in cfg: + raise RuntimeError( + "You are seemingly using the old pmap-based evaluator, but with " + "jit-based train loop, see (internal link) for more details.") + if api_type == "jit" and "devices" not in cfg: + raise RuntimeError( + "You are seemingly using new jit-based evaluator, but with " + "old pmap-based train loop, see (internal link) for more details.") + + try: + predict_fn = predict_fns[pred_key] + except KeyError as e: + raise ValueError( + f"Unknown predict_fn '{pred_key}'. Available predict_fns are:\n" + + "\n".join(predict_fns)) from e + if pred_kw is not None: + predict_fn = _CacheablePartial(predict_fn, flax.core.freeze(pred_kw)) + evaluator = module.Evaluator(predict_fn, **cfg) + evaluators.append((name, evaluator, logsteps, prefix)) + + return evaluators + + +@dataclasses.dataclass(frozen=True, eq=True) +class _CacheablePartial: + """partial(fn, **kwargs) that defines hash and eq - to help with jit caches. + + This is particularly common in evaluators when one has many evaluator + instances that run on difference slices of data. + + Example: + + ``` + f1 = _CacheablePartial(fn, a=1) + jax.jit(f1)(...) + jax.jit(_CacheablePartial(fn, a=1))(...) # fn won't be retraced. + del f1 + jax.jit(_CacheablePartial(fn, a=1))(...) # fn will be retraced. + ``` + """ + fn: Callable[..., Any] + kwargs: flax.core.FrozenDict + + def __call__(self, *args, **kwargs): + return functools.partial(self.fn, **self.kwargs)(*args, **kwargs) + + +def eval_input_pipeline( + data, pp_fn, batch_size, devices, keep_on_cpu=(), + cache="pipeline", prefetch=1, warmup=False, +): + """Create an input pipeline in the way used by most evaluators. + + Args: + data: The configuration to create the data source (like for training). + pp_fn: A string representing the preprocessing to be performed. + batch_size: The batch size to use. + devices: The devices that the batches are sharded and pre-fetched onto. + keep_on_cpu: See input_pipeline.start_global. Entries in the batch that + should be kept on the CPU, hence could be ragged or of string type. + cache: One of "none", "pipeline", "raw_data", "final_data". Determines what + part of the input stream should be cached across evaluator runs. They use + more and more RAM, but make evals faster, in that order. + - "none": Entirely re-create and destroy the input pipeline each run. + - "pipeline": Keep the (tf.data) pipeline object alive across runs. + - "raw_data": Cache the full raw data before pre-processing. + - "final_data": Cache the full raw data after pre-processing. + prefetch: How many batches to fetch ahead. + warmup: Start fetching the first batch at creation time (right now), + instead of once the iteration starts. + + Returns: + A tuple (get_iter, steps), the first element is a function that returns + the iterator to be used for an evaluation, the second one is how many steps + should be iterated for doing one evaluation. + """ + assert ( + cache is None + or cache.lower() in ("none", "pipeline", "raw_data", "final_data") + ), f"Unknown value for cache: {cache}" + data_source = ds_core.get(**data) + tfdata, steps = input_pipeline.make_for_inference( + data_source.get_tfdata(ordered=True, allow_cache=cache.lower() != "none"), + batch_size=batch_size, + num_ex_per_process=data_source.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn, str(data)), + cache_final=cache == "raw_data", + cache_raw=cache == "final_data") + get_data_iter = lambda: input_pipeline.start_global( + tfdata, devices, prefetch, keep_on_cpu, warmup) + + # Possibly create one persistent iterator: + if cache in ("pipeline", "raw_data", "final_data"): + data_iter = get_data_iter() + get_data_iter = lambda: data_iter + + return get_data_iter, steps + + +def process_sum(tree): + """Sums the pytree across all processes.""" + if jax.process_count() == 1: # Avoids corner-cases on donuts. + return tree + + with jax.transfer_guard_device_to_host("allow"): + gathered = jax.experimental.multihost_utils.process_allgather(tree) + return jax.tree.map(functools.partial(np.sum, axis=0), gathered) + + +def resolve_outfile(outfile, split="", **kw): + if not outfile: + return None + + # A caveat: when workdir doesn't exist but is in the `outfile`, we should + # skip. This is common in small runs or runlocal debuggings. + if "{workdir}" in outfile and not flags.FLAGS.workdir: + return None + + return outfile.format( + workdir=flags.FLAGS.workdir, + split="".join(c if c not in "[]%:" else "_" for c in split), + step=getattr(u.chrono, "prev_step", None), + **kw, + ) + + +def multiprocess_write_json(outfile, jobj): # jobj = "json object" + """Write a single json file combining all processes' `jobj`s.""" + if not outfile: + return + + outfile = resolve_outfile(outfile) + gfile.makedirs(os.path.dirname(outfile)) + + if isinstance(jobj, list): + combine_fn = list.extend + elif isinstance(jobj, dict): + combine_fn = dict.update + else: + raise TypeError(f"Can only write list or dict jsons, but got {type(jobj)}") + + # First, each process writes its own file. + with gfile.GFile(outfile + f".p{jax.process_index()}", "w+") as f: + f.write(json.dumps(jobj)) + + u.sync() # Wait for all files to be written; `with` above does close/flush. + + # Have process 0 collect, concat, and write final output. + all_json = type(jobj)() + if jax.process_index() == 0: + for pid in range(jax.process_count()): + with gfile.GFile(outfile + f".p{pid}", "r") as f: + combine_fn(all_json, json.loads(f.read())) + with gfile.GFile(outfile, "w+") as f: + f.write(json.dumps(all_json)) + + # Cleanup time + u.sync() + gfile.remove(outfile + f".p{jax.process_index()}") + + return all_json diff --git a/big_vision/evaluators/fewshot_lsr.py b/big_vision/evaluators/fewshot_lsr.py new file mode 100644 index 0000000000000000000000000000000000000000..1b7019ad3fa58936975b631206947b3b33ecdc67 --- /dev/null +++ b/big_vision/evaluators/fewshot_lsr.py @@ -0,0 +1,245 @@ +# Copyright 2024 Big Vision 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. + +"""Utils for few-shot evaluation.""" +# pylint: disable=consider-using-from-import,g-importing-member + +import functools + +import big_vision.datasets.core as ds_core +import big_vision.input_pipeline as input_pipeline +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +import jax +import jax.numpy as jnp +from jax.sharding import NamedSharding as Sharding +from jax.sharding import PartitionSpec as P +import numpy as np + +BIAS_CONSTANT = 100.0 + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +# Setup function for few-shot regression on CPU to avoid "polluting" the TPU. +@u.jit_cpu(static_argnums=(2,)) +def _precompute_cache(x, y, num_classes): + """Cache quantities to speed-up the computation of L2-regularized least-sq.""" + # Whiten + mean = jnp.mean(x, axis=0, keepdims=True) + std = jnp.std(x, axis=0, keepdims=True) + 1e-5 + x = (x - mean) / std + + # Add a constant feature for the bias, large so it's almost unregularized: + x = jnp.pad(x, ((0, 0), (0, 1)), constant_values=BIAS_CONSTANT) + + # To one-hot representation rescaled into {-1, 1} + y = 2.0 * jax.nn.one_hot(y, num_classes) - 1.0 + + num_points, dim = x.shape + # Let N be the number of points, D the dimension and C the number of classes. + # We have x of shape (N, D) and y of shape (N, C). + # For least-squares, we can compute + # + # (A) when N >= D, (x^T x + l2 Id)^{-1} x^T y + # (B) when D > N, x^T (x x^T + l2 Id)^{-1} y + # + # We pre-compute the eigen-decomposition of either x^T x or x x^T which + # becomes q diag(eigs) q^T with q unitary matrix either (D, D) or (N, N) + # and eigs a vector (D,) or (N,). + # + # For any l2 > 0, we can compute (x^T x + l2 Id)^{-1} or (x x^T + l2 Id)^{-1} + # by simply computing q (diag(eigs) + l2 Id)^{-1} q^T. + # (SVD would be more natural here, but it proved slower, so we use eigh) + # + # Both cases (A) and (B) can be viewed as lhs (diag(eigs) + l2 Id)^{-1} rhs, + # where lhs/rhs are pre-computed left/right-hand sides to specify. + # + # Detailed evaluation in terms of time and fewshot metrics can be found in + # (internal link) + # + # Implemented by Rodolphe Jenatton. + if num_points >= dim: + eigs, q = jnp.linalg.eigh(x.T @ x) + rhs = q.T @ (x.T @ y) + lhs = q + else: + eigs, q = jnp.linalg.eigh(x @ x.T) + rhs = q.T @ y + lhs = x.T @ q + + cache = { + "eigs": eigs, + "rhs": rhs, + "lhs": lhs, + "mean": mean, + "std": std + } + return cache + + +@u.jit_cpu() +def _eig_fewshot_acc_fn(cache, x_test, y_test, l2_reg): + """Computes (x,y) linear regression accuracy on (x_test, y_test).""" + + x_test = (x_test - cache["mean"]) / cache["std"] + x_test = jnp.pad(x_test, ((0, 0), (0, 1)), constant_values=BIAS_CONSTANT) + + rhs = cache["rhs"] + lhs = cache["lhs"] + eigs = cache["eigs"] + + # See comments in _precompute_cache for context about the formula. + scaling = 1.0 / (eigs + l2_reg * jnp.ones_like(eigs)) + scaling = scaling.reshape((1, -1)) + w = (lhs * scaling) @ rhs + # Predict test-set values and measure their accuracy + preds = jnp.argmax(x_test @ w, axis=1) + return jnp.mean(preds == y_test) + + +class Evaluator: + """Class for few-shot evaluation.""" + + def __init__(self, predict_fn, batch_size, + datasets, shots, l2_reg, + pp_train, pp_eval, display_first, + representation_layer=None, num_seeds=3, + label_key="label", mask_key="_mask", data_dir=None, *, + devices): + self.datasets = datasets + self.shots = shots + self.l2_reg = l2_reg + self.batch_size = batch_size + self.pp_tr = pp_train + self.pp_te = pp_eval + self.display_first = display_first + self._datasets = {} # Cache for tfds data. Persists while object is alive. + self._repr = {} # Cache for precomputed repr. Persists within the run call. + self.num_seeds = num_seeds + self.label_key = label_key + self.mask_key = mask_key + self.data_dir = data_dir + self.devices = devices + self.mesh = jax.sharding.Mesh(devices, ("devices",)) + self.repr_fn = self.get_representation_fn( + predict_fn, representation_layer) + + def get_representation_fn(self, predict_fn, representation_layer): + # `out_shardings=Sharding(self.mesh, P())` will "all_gather" the outputs. + @functools.partial(jax.jit, out_shardings=Sharding(self.mesh, P())) + def _repr_fn(train_state, batch, labels, mask): + zimg, *_, out = predict_fn(train_state, batch) + if representation_layer is not None: + rep = u.tree_get(out, representation_layer) + else: + rep = zimg + return rep, labels, mask + return _repr_fn + + # Setup input pipeline. + def _get_dataset(self, dataset, train_split, test_split): + """Lazy-loads given dataset.""" + key = (dataset, train_split, test_split) + try: + return self._datasets[key] + except KeyError: + # NOTE: only supporting TFDS data for now for bwd compat/lazyness. + train_data = ds_core.get( + name=dataset, split=train_split, data_dir=self.data_dir + ) + test_data = ds_core.get( + name=dataset, split=test_split, data_dir=self.data_dir + ) + train_ds, batches_tr = input_pipeline.make_for_inference( + train_data.get_tfdata(ordered=True), + num_ex_per_process=train_data.num_examples_per_process(), + batch_size=self.batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(self.pp_tr)) + test_ds, batches_te = input_pipeline.make_for_inference( + test_data.get_tfdata(ordered=True), + num_ex_per_process=test_data.num_examples_per_process(), + batch_size=self.batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(self.pp_te)) + + num_classes = train_data.builder.info.features[self.label_key].num_classes + return self._datasets.setdefault( + key, (train_ds, batches_tr, test_ds, batches_te, num_classes)) + + def _get_repr(self, params, data, steps): + """Compute representation for the whole dataset.""" + pre_logits_list = [] + labels_list = [] + for batch, _ in zip( + input_pipeline.start_global(data, self.devices, 0), range(steps)): + labels, mask = batch.pop(self.label_key), batch.pop(self.mask_key) + pre_logits, labels, mask = jax.device_get(self.repr_fn( + params, batch, labels, mask)) + mask = mask.astype(bool) + pre_logits_list.append(pre_logits[mask]) + labels_list.append(labels[mask]) + pre_logits = np.concatenate(pre_logits_list, axis=0) + labels = np.concatenate(labels_list, axis=0) + + return pre_logits, labels + + def compute_fewshot_metrics(self, train_state, seed, + dataset, train_split, test_split): + """Compute few-shot metrics on one dataset.""" + if dataset in self._repr: + repr_train, labels_train, repr_test, labels_test, num_classes = ( + self._repr[dataset]) + else: + train_ds, steps_tr, test_ds, steps_te, num_classes = self._get_dataset( + dataset, train_split, test_split) + repr_train, labels_train = self._get_repr(train_state, train_ds, steps_tr) + repr_test, labels_test = self._get_repr(train_state, test_ds, steps_te) + self._repr[dataset] = (repr_train, labels_train, + repr_test, labels_test, + num_classes) + + # Collect where we have samples of which classes. + rng = np.random.default_rng(seed) + class_indices = [rng.permutation(np.where(labels_train == cls_i)[0]) + for cls_i in range(num_classes)] + + results = {} + for shots in self.shots: + all_idx = [indices[:shots] for indices in class_indices] + all_idx = np.concatenate(all_idx, axis=0) + x = u.put_cpu(repr_train[all_idx]) + y = u.put_cpu(labels_train[all_idx]) + repr_test, labels_test = u.put_cpu((repr_test, labels_test)) + + # Note the code is optimized to solve multiple LSR tasks for changing l2 + # strength, even though we currently used the fixed l2_reg constant. + cache = _precompute_cache(x, y, num_classes) + acc = _eig_fewshot_acc_fn( + cache, repr_test, labels_test, u.put_cpu(self.l2_reg)) + results[shots] = jax.device_get(acc) + + return results + + def run(self, train_state): + """New API executed in terms of old API.""" + self._repr = {} + for seed in range(self.num_seeds): + for name, dataset_args in self.datasets.items(): + result = self.compute_fewshot_metrics(train_state, seed, *dataset_args) + for shots, v in result.items(): + prefix = "a/" if (name, shots) in self.display_first else "z/" + suffix = f"-seed-{seed}" + yield f"{prefix}{name}_{shots}shot{suffix}", v diff --git a/big_vision/evaluators/mean.py b/big_vision/evaluators/mean.py new file mode 100644 index 0000000000000000000000000000000000000000..d11590667053764bce73897eeaca4ea0d815efef --- /dev/null +++ b/big_vision/evaluators/mean.py @@ -0,0 +1,80 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for computing mean of per-example metrics. + +This evaluator can be used in two ways: + 1. Create a new evaluator with reduced boilerplate by inheriting from it. + 2. For quick prototyping, use this with predict_fns which return the metrics. +""" +from functools import partial +from typing import Mapping + +from big_vision.evaluators import common + +import jax +import jax.numpy as jnp +import numpy as np + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +# Note: global to avoid jax re-compiling across different evaluator instances. +@partial(jax.jit, static_argnums=0) +def _run_predict_fn(predict_fn, train_state, batch): + """Sum per-example metrics weighted by `_mask`.""" + mask = batch['_mask'] + metrics = predict_fn(train_state, batch) + # Sanity check output format of predict_fn. + assert isinstance(metrics, Mapping), 'predict_fn must return a dict' + for y in jax.tree.leaves(metrics): + if y.shape != mask.shape: + raise ValueError( + f'Expected per-example metrics of shape {mask.shape} found ' + f'{jax.tree.map(lambda x: x.shape, metrics)}.') + metrics = {**metrics, '_mask': mask} + return jax.tree.map(lambda x: jnp.sum(jnp.where(mask, x, 0)), metrics) + + +class Evaluator: + """Report the mean of per-example metrics computed by predict_fn. + + `predict_fn(params, batch)` must return a dict from metric name to + per-example metrics of shape [batch_size]. + """ + + def __init__(self, predict_fn, **kw): + self.get_data_iter, self.steps = common.eval_input_pipeline(**kw) + self.predict_fn = partial(_run_predict_fn, predict_fn) + + def run(self, train_state): + """Computes all metrics.""" + metrics = [] + + # Compute batch metrics without blocking. + for _, batch in zip(range(self.steps), self.get_data_iter()): + batch_metrics = self.predict_fn(train_state, batch) + metrics.append(batch_metrics) + + # Transfer metrics (blocking). + metrics = jax.device_get(metrics) + + # Accumulate metrics across batches. + metrics_sum = jax.tree.map(lambda *x: np.sum(x), *metrics) + mask_sum = metrics_sum.pop('_mask') + for key, value_sum in metrics_sum.items(): + yield (key, value_sum / mask_sum) diff --git a/big_vision/evaluators/proj/cappa/perplexity.py b/big_vision/evaluators/proj/cappa/perplexity.py new file mode 100644 index 0000000000000000000000000000000000000000..2ce69398be4243304ef62a7a1276172a4e648787 --- /dev/null +++ b/big_vision/evaluators/proj/cappa/perplexity.py @@ -0,0 +1,50 @@ +# Copyright 2023 Big Vision 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. + +"""Evaluator for perplexity of a model.""" +from big_vision.evaluators import mean +import big_vision.utils as u +import jax.numpy as jnp + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +def perplexity(predict_fn, normalize_by_seqlen): + """Returns a function that computes perplexity.""" + + def _perplexity_fn(train_state, batch, pad_token=0, **kw): + logits, _ = predict_fn(train_state, batch, **kw) + + # Ignore perplexity on the padding label. + weights = jnp.where(batch['labels'] != pad_token, 1, 0).astype(jnp.float32) + if batch.get('label_masks') is not None: + weights = weights * batch['label_masks'] + + losses = u.weighted_softmax_xent( + logits=logits, labels=batch['labels'], + weights=weights, label_smoothing=0.0, + reduction=False, normalize=normalize_by_seqlen) + + return {'perplexity': losses} + return _perplexity_fn + + +class Evaluator(mean.Evaluator): + """Perplexity evaluator.""" + + def __init__(self, predict_fn, *a, normalize_by_seqlen=False, **kw): + super().__init__(perplexity(predict_fn, normalize_by_seqlen), *a, **kw) diff --git a/big_vision/evaluators/proj/cappa/scoring_classifier.py b/big_vision/evaluators/proj/cappa/scoring_classifier.py new file mode 100644 index 0000000000000000000000000000000000000000..60906bacac2e6c0a2cabd106e7d6d82a06c08b8e --- /dev/null +++ b/big_vision/evaluators/proj/cappa/scoring_classifier.py @@ -0,0 +1,63 @@ +# Copyright 2023 Big Vision 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. + +"""Scoring classifier. + +This one is based on a generative perspective for image classification. +Here we input the image as well as all the tokenized labels to compute their +perplexity and select the one with minimum loss as the prediction. +""" +import functools +from big_vision.datasets.imagenet import class_names as imagenet_class_names +from big_vision.evaluators import mean +from big_vision.pp import builder as pp_builder +import jax.numpy as jnp +import numpy as np + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +CLASS_NAMES = { + "imagenet2012": imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES, +} + + +# As a separate function to cache result across instances. +@functools.lru_cache(maxsize=None) +def get_classes(dataset_name, pp_txt): + """Load the class label strings and tokenize them using pp_txt.""" + pp_fn = pp_builder.get_preprocess_fn(pp_txt, log_data=False) + return np.array([pp_fn({"label": name})["labels"] + for name in CLASS_NAMES[dataset_name]]) + + +def scoring(predict_fn, tokenized_labels): + + def _scoring_fn(train_state, batch, *a, **kw): + batch = {"_label_tokens": tokenized_labels, **batch} + scores = predict_fn(train_state, batch, *a, **kw) + predictions = jnp.argmax(scores, axis=-1) + return {"prec@1": predictions == batch["label"]} + + return _scoring_fn + + +class Evaluator(mean.Evaluator): + """Evaluator for classification accuracy based on scoring all classes.""" + + def __init__(self, predict_fn, data, pp_fn, pp_txt, *a, **kw): + cls_tokens = get_classes(data["name"], pp_txt) + super().__init__(scoring(predict_fn, cls_tokens), data, pp_fn, *a, **kw) diff --git a/big_vision/evaluators/proj/distill/distance.py b/big_vision/evaluators/proj/distill/distance.py new file mode 100644 index 0000000000000000000000000000000000000000..2cc35391fe3e8f92ac88b1ba21e137d6685b88ed --- /dev/null +++ b/big_vision/evaluators/proj/distill/distance.py @@ -0,0 +1,151 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for the classfication task.""" +from functools import partial, lru_cache + +from big_vision import input_pipeline +import big_vision.datasets.core as ds_core +import big_vision.pp.builder as pp_builder +import big_vision.utils as u + +import einops +import jax +import jax.numpy as jnp +from jax.sharding import NamedSharding +from jax.sharding import PartitionSpec as P +import numpy as np + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +def dist(student, teacher, kind, feat_axis=-1, + epsilon=1e-12, t=1, ls=0.0, k=1): + """Distance function used for distillation.""" + diff = student - teacher + if kind == 'euclidean': + return jnp.sqrt(jnp.sum(diff * diff, axis=feat_axis) + epsilon) + elif kind == 'l2': + return jnp.sum(diff * diff, axis=feat_axis) + elif kind == 'hard': + pseudolabels = jnp.argmax(teacher, feat_axis) + pl = u.onehot(pseudolabels, teacher.shape[feat_axis]) + if ls: + pl = (1.0 - ls) * pl + (ls / (pl.shape[-1] - 1)) * (1.0 - pl) + return u.softmax_xent(logits=student, labels=pl, + reduction=False, kl=True, axis=feat_axis) + elif kind == 'kl': + return t**2 * u.softmax_xent( + logits=student / t, + labels=jax.nn.softmax(teacher / t), + reduction=False, kl=True, axis=feat_axis) + elif kind == 'logsoftmax_euclidean': + logsoftmax_diff = ( + jax.nn.log_softmax(student, axis=feat_axis) - + jax.nn.log_softmax(teacher, axis=feat_axis)) + return jnp.sqrt( + jnp.sum(logsoftmax_diff * logsoftmax_diff, axis=feat_axis) + epsilon) + elif kind == 'agree': + def get_top_k(arr, k, ax): + return jax.lax.top_k(arr.swapaxes(ax, -1), k)[1].swapaxes(ax, -1) + return (get_top_k(student, k, feat_axis) == + get_top_k(teacher, 1, feat_axis)).sum(feat_axis) + else: + assert False, f'Unknown kind of distance {kind}.' + + +@lru_cache(None) +def get_dist_fn(**kw): + return partial(dist, **kw) + + +# To avoid re-compiling the function for every new instance of the same +# evaluator on a different dataset! +@lru_cache(None) +def get_eval_fn(student_teacher_fwd, what, mesh, distances): + """Produces eval function, also applies pmap.""" + @partial(jax.jit, out_shardings=NamedSharding(mesh, P())) + def _eval_fn(train_state, batch, mask): + (_, out_s), (_, out_t) = student_teacher_fwd(train_state, batch) + repr_s = u.tree_get(out_s, what[0]) + repr_t = u.tree_get(out_t, what[1]) + + # Let's flatten any non-vectors (eg feature-maps). + repr_s = einops.rearrange(repr_s, 'b ... -> b (...)') + repr_t = einops.rearrange(repr_t, 'b ... -> b (...)') + + all_ds = [] + # NOTE: we're gathering and returning all ; if this becomes too slow, we + # can change to compute and return summary stats later on. + for dist_fn in distances: + ds = dist_fn(repr_s, repr_t) + all_ds.append(ds) + all_masks = mask + return all_ds, all_masks + + return _eval_fn + + +class Evaluator: + """Distillation distance evaluator.""" + + def __init__( + self, + student_teacher_fwd, + data, + pp_fn, + distances, + what=('logits', 'logits'), + *, + devices, + **data_kw, + ): + data = ds_core.get(**data) + pp_fn = pp_builder.get_preprocess_fn(pp_fn) + prefetch = data_kw.pop('prefetch', 1) + self.ds, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), + pp_fn, + num_ex_per_process=data.num_examples_per_process(), + **data_kw, + ) + self.data_iter = input_pipeline.start_global(self.ds, devices, prefetch) + dist_fns = tuple(get_dist_fn(**dist) for dist in distances) + self.dist_names = [ + '_'.join(f'{k}={v}' for k, v in dist.items()) for dist in distances + ] + mesh = jax.sharding.Mesh(devices, ('data',)) + self.eval_fn = get_eval_fn(student_teacher_fwd, what, mesh, dist_fns) + + def run(self, train_state): + """Computes all metrics.""" + all_ds = [[] for _ in self.dist_names] + for _, batch in zip(range(self.steps), self.data_iter): + mask = batch.pop('_mask') + batch_ds, batch_ms = self.eval_fn(train_state, batch, mask) + # All results are a replicated array shaped as follows: + # (local_devices, per_device_batch_size, elem_shape...) + # with each local device's entry being identical. + # So let's just take the first one to the host as numpy. + batch_ms = np.array(batch_ms) + for i, val in enumerate(batch_ds): + all_ds[i].append(np.array(val)[batch_ms == 1]) + for name, ds in zip(self.dist_names, all_ds): + ds = np.concatenate(ds) + yield f'{name}/all', ds + yield f'{name}/avg', np.mean(ds) + yield f'{name}/min', np.min(ds) + yield f'{name}/max', np.max(ds) diff --git a/big_vision/evaluators/proj/givt/coco_panoptic.py b/big_vision/evaluators/proj/givt/coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..264f30a80ad51b131c25f59bad4aef2f19125730 --- /dev/null +++ b/big_vision/evaluators/proj/givt/coco_panoptic.py @@ -0,0 +1,401 @@ +# Copyright 2024 Big Vision 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. + +"""COCO17 panoptic evaluation. + +jax.jit-compatible fork of the evaluator from evaluators/proj/uvim. +""" +import functools +import itertools +import json +import os +import tempfile +import time +from typing import Any +import zipfile + +from absl import flags +from absl import logging +from big_vision import input_pipeline +from big_vision import utils +from big_vision.datasets import core as ds_core +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +import numpy as np +from pycocotools.panopticapi import evaluation +import panopticapi_converters.twochannels2panoptic_coco_format as converter +import tensorflow as tf +import tensorflow_datasets as tfds + +from tensorflow.io import gfile + +# Temporary global flag to facilitate backwards compatability. +API = 'jit' + +ROOT = os.environ.get('COCO_DATA_DIR', '.') + +PANOPTIC_COCO_CATS_FILE = f'{ROOT}/panoptic_coco_categories.json' +PANOPTIC_2017 = { + 'train': f'{ROOT}/panoptic_train2017.json', + 'validation': f'{ROOT}/panoptic_val2017.json', +} + +PANOPTIC_GT_ZIP = { + 'train': f'{ROOT}/panoptic_train2017.zip', + 'validation': f'{ROOT}/panoptic_val2017.zip', +} + + +# Note: global to avoid jax re-compiling across different evaluator instances. +@functools.cache +def _get_predict_fn(predict_fn, mesh=None): + """Wrapper for jit-compiled predict function.""" + + # `out_shardings` annotation is needed because of the `all_gather` ops in the + # pmap implementation. + @functools.partial(jax.jit, + out_shardings=jax.sharding.NamedSharding( + mesh, jax.sharding.PartitionSpec())) + def _run_predict_fn(train_state, batch): + """Run predict_fn and gather all outputs on all devices.""" + y = predict_fn(train_state, batch) + res = { + 'image/id': batch['image/id'], + 'mask': batch['_mask'], + 'y': jnp.stack([y['semantics'], y['instances']], axis=-1), + } + return res + return _run_predict_fn + + +class Evaluator: + """Panoptic segmentation evaluator: calls official COCO API.""" + + def __init__( + self, + predict_fn, + pp_fn, + batch_size, + data=None, + cache_final=True, + cache_raw=False, + prefetch=1, + save_dir=None, + *, + devices, + ): + """Panoptic segmentation evaluator: calls official COCO API. + + Args: + predict_fn: jit-compilable function, which accepts arbitrary dictionaries + of parameters and data, where the data dictionary is produced by the + `pp_fn`. It is expected to output a 2-channel mask, where the first + channel encodes semantics, and the second channel encodes instance ids. + pp_fn: Preprocessing function, sepcified as string. + batch_size: Batch size. + data: Dict specifying name and split of the data set. Defaults to the + standard COCO (2017). + cache_final: Whether to cache the data after preprocessing - see + input_pipeline for details. + cache_raw: Whether to cache the raw data - see input_pipline for details. + prefetch: Number of batches to prefetch + save_dir: Directory to save the results in. + devices: List of jax devices. + """ + self.predict_fn = _get_predict_fn( + predict_fn, jax.sharding.Mesh(devices, ('devices',))) + + data_specs = dict(name='coco/2017_panoptic', + data_dir=None, split='validation') + data_specs.update(data or {}) + data = ds_core.get(**data_specs) + self.dataset, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), batch_size=batch_size, + num_ex_per_process=data.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn), + cache_final=cache_final, cache_raw=cache_raw) + self.data_iter = input_pipeline.start_global( + self.dataset, devices, prefetch) + + # Only process 0 runs conversion to png and calls into coco api. + if jax.process_index() == 0: + self.result_dir = tempfile.TemporaryDirectory() + (self.gt_folder, self.gt_json, self.categories_json, + self.remap, self.size_map) = _prepare_ground_truth( + data_specs['name'], data_specs['split'], + data_specs.get('data_dir')) + if save_dir: + self.save_dir = save_dir.format(workdir=flags.FLAGS.workdir) + gfile.makedirs(self.save_dir) + else: + self.save_dir = None + + def _compute_png_predictions( + self, train_state: Any) -> Any: + """Computes predictions and converts then to png to optimize memory use.""" + count = 0 + logging.info('Panoptic eval: running inference.') + for batch in itertools.islice(self.data_iter, self.steps): + out = self.predict_fn(train_state, batch) + + if jax.process_index(): + continue + + out = jax.device_get(out) + mask = out['mask'] + pan_recs = out['y'][mask] + ids = out['image/id'][mask] + + for pan_rec, image_id in zip(pan_recs, ids): + sem = pan_rec[..., 0] + ins = pan_rec[..., 1] + + sem_remapped = np.array(sem) + for v in np.unique(sem): + sem_remapped[sem == v] = self.remap[v] + sem = sem_remapped + + pan_mask = np.stack([sem, ins, np.zeros_like(sem)], axis=-1) + pan_mask = utils.put_cpu(pan_mask) + pan_mask = _resize_nearest(pan_mask, self.size_map[image_id]) + pan_mask_png = tf.io.encode_png(pan_mask.astype('uint8')).numpy() + + fname = f'{self.result_dir.name}/{image_id:012d}.png' + with open(fname, 'wb') as f: + f.write(pan_mask_png) + count += 1 + + logging.log_every_n_seconds( + logging.INFO, 'Panoptic eval: processed %i examples so far.', 30, + count) + + if jax.process_index(): + return None + + logging.info('Panoptic eval: inference done. Processed %d examples.', count) + return self.result_dir + + def run(self, train_state): + """Run panoptic segmentation evaluation. + + Args: + train_state: pytree containing the model parameters. + + Yields: + Tuples consisting of metric name and value. + """ + # Note result_dir is constant, but files inside are mutated. + result_dir = self._compute_png_predictions(train_state) + + if jax.process_index(): + return + + if self.save_dir: + gfile.RecursivelyCopyDir(result_dir.name, self.save_dir, overwrite=True) + + with tempfile.TemporaryDirectory() as pred_folder, \ + tempfile.NamedTemporaryFile(mode='w') as pred_json: + + logging.info('Panoptic eval: running conversion.') + converter.converter( + source_folder=result_dir.name, + images_json_file=self.gt_json, + categories_json_file=self.categories_json, + segmentations_folder=pred_folder, + predictions_json_file=pred_json.name) + logging.info('Panoptic eval: conversion done.') + + logging.info('Panoptic eval: running metrics computation.') + res = evaluation.pq_compute(gt_json_file=self.gt_json, + gt_folder=self.gt_folder, + pred_json_file=pred_json.name, + pred_folder=pred_folder) + logging.info('Panoptic eval: metrics computation done.') + + for k in ['All', 'Stuff', 'Things']: + for m in ['pq', 'rq', 'sq']: + yield f'{k}_{m}', res[k][m] + + +def _prepare_ground_truth(dataset, split, data_dir): + if dataset == 'coco/2017_panoptic' and data_dir is None: + return _prepare_ground_truth_from_zipfiles(split) + else: + return _prepare_ground_truth_from_dataset(dataset, split, data_dir) + + +@functools.lru_cache(maxsize=None) +def _prepare_ground_truth_from_dataset(dataset, split, data_dir): + """Prepare ground truth from a tf.data.Dataset. + + Args: + dataset: TFDS-compatible dataset specification. + split: Data set split to use. + data_dir: Folder containing the data + + Returns: + A tuple containing the folder containing the ground-truth data, the + ground truth annotations loaded from json, the categories loaded form json, + a map for remapping, and a map mapping image id to image size. + + """ + tfds_dataset = tfds.builder( + dataset, data_dir=data_dir).as_dataset(split=split) + + categories_json = _make_local_copy(PANOPTIC_COCO_CATS_FILE) + with gfile.GFile(categories_json, 'rb') as f: + categories = json.loads(f.read()) + + # Build map from tfds class ids to COCO class ids. + remap = {0: 0} + with gfile.GFile(categories_json, 'r') as f: + remap = {**remap, **{(i + 1): x['id'] for i, x in enumerate(categories)}} + + gt_folder = tempfile.mkdtemp() + gfile.makedirs(gt_folder) + size_map = {} + annotations = [] + images = [] + for example in tfds_dataset: + image_id = int(example['image/id']) + panoptic_image = example['panoptic_image'] + ann_ids = example['panoptic_objects']['id'] + ann_labels = example['panoptic_objects']['label'] + ann_iscrowd = example['panoptic_objects']['is_crowd'] + ann_area = example['panoptic_objects']['area'] + + fname = f'{image_id:012d}.png' + with gfile.GFile(os.path.join(gt_folder, fname), 'wb') as f: + f.write(tf.io.encode_png(panoptic_image).numpy()) + + size_map[image_id] = (panoptic_image.shape[0], panoptic_image.shape[1]) + + segments_info = [] + for i in range(len(ann_ids)): + segments_info.append({ + 'id': int(ann_ids[i]), + 'category_id': remap[int(ann_labels[i] + 1)], + 'iscrowd': int(ann_iscrowd[i]), + 'area': int(ann_area[i]), + }) + + annotations.append({ + 'file_name': str(fname), + 'image_id': int(image_id), + 'segments_info': segments_info + }) + images.append({ + 'id': image_id, + 'file_name': f'{image_id:012d}.jpg', + }) + + # Write annotations.json needed for pq_compute. + gt_json = os.path.join(gt_folder, 'annotations.json') + with gfile.GFile(gt_json, 'wb') as f: + f.write(json.dumps({ + 'images': images, + 'annotations': annotations, + 'categories': categories, + })) + + return gt_folder, gt_json, categories_json, remap, size_map + + +def _prepare_ground_truth_from_zipfiles(split): + """Prepare ground truth from coco zip files. + + Args: + split: dataset split to prepare ground truth for. + + Returns: + A tuple containing the folder containing the ground-truth data, the ground + truth annotations loaded from json, the categories loaded form json, a map + for remapping, and a map mapping image id to image size. + """ + split_prefix = split.split('[')[0] + if split_prefix not in ('train', 'validation'): + raise ValueError(f'Split {split} not supported') + + # The following 4 calls are cached. This allows to save significant time + # in use cases like sweeping predict_fn hparams on the same run. + gt_json = _make_local_copy(PANOPTIC_2017[split_prefix]) + gt_folder = _make_local_unzip_copy(PANOPTIC_GT_ZIP[split_prefix]) + categories_json = _make_local_copy(PANOPTIC_COCO_CATS_FILE) + image_ids = _list_image_ids('coco/2017_panoptic', split) + + gt_folder = os.path.join( + gt_folder, 'panoptic_val2017' + if split_prefix == 'validation' else 'panoptic_train2017') + + # Build map from tfds class ids to COCO class ids. + remap = {0: 0} + with gfile.GFile(categories_json, 'r') as f: + remap = {**remap, **{(i + 1): x['id'] for i, x in enumerate(json.load(f))}} + + # Filters gt_json to contain only annotations for images in dataset. + with gfile.GFile(gt_json) as f: + data = json.load(f) + logging.info( + 'Panoptic eval: pre-filter %d annotations.', + len(data['annotations']) + ) + data['images'] = [x for x in data['images'] if x['id'] in image_ids] + data['annotations'] = [ + x for x in data['annotations'] if x['image_id'] in image_ids + ] + logging.info( + 'Panoptic eval: post-filter %d annotations.', + len(data['annotations']) + ) + filtered_gt_json = tempfile.NamedTemporaryFile(delete=False).name + with open(filtered_gt_json, 'w') as f: + json.dump(data, f) + + # Precompute images sizes. + size_map = {x['id']: (x['height'], x['width']) for x in data['images']} + + return gt_folder, filtered_gt_json, categories_json, remap, size_map + + +@functools.lru_cache(maxsize=None) +def _list_image_ids(dataset, split): + d = tfds.load(dataset, split=split).map(lambda x: x['image/id']) + return frozenset(d.as_numpy_iterator()) + + +@functools.lru_cache(maxsize=None) +def _make_local_copy(fname) -> str: + start = time.monotonic() + local_file = tempfile.NamedTemporaryFile(delete=False) + gfile.copy(fname, local_file.name, overwrite=True) + logging.info('Copy %s in %d seconds.', fname, time.monotonic() - start) + return local_file.name + + +@functools.lru_cache(maxsize=None) +def _make_local_unzip_copy(fname) -> str: + start = time.monotonic() + folder = tempfile.mkdtemp() + with tempfile.NamedTemporaryFile() as tmp_zip_file: + gfile.copy(fname, tmp_zip_file.name, overwrite=True) + with zipfile.ZipFile(tmp_zip_file.name, 'r') as f: + f.extractall(folder) + logging.info('Copy %s in %d seconds.', fname, time.monotonic() - start) + return folder + + +@utils.jit_cpu(static_argnums=(1,)) +def _resize_nearest(image, shape): + return jax.image.resize(image, shape + image.shape[-1:], 'nearest') diff --git a/big_vision/evaluators/proj/givt/nyu_depth.py b/big_vision/evaluators/proj/givt/nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..23c6b3559703ed9af18bd1bf7de5371abe458e1c --- /dev/null +++ b/big_vision/evaluators/proj/givt/nyu_depth.py @@ -0,0 +1,191 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluation for NYU depth. + +jax.jit-compatible fork of the evaluator from evaluators/proj/uvim. + +At evaluation time the ground truth is cropped and clipped. Values outside of +the test crop or clipping range are not included in eval calculations. + +In this evaluator, it is assume that the groud truth is already cropped, so the +entire image is evaluated. However, the evaluator does perform the clipping. + +Reference implementations: + https://github.com/zhyever/Monocular-Depth-Estimation-Toolbox/blo(internal link)a0f341244260ff61541191a613dd74bc/depth/datasets/nyu.py + https://github.com/vinvino02/GLPDepth/blob/7f3c78df4ecd6e7c79fd0c4b73c95d61f4aa2121/code/utils/metrics.py + https://github.com/shariqfarooq123/AdaBins/blob/2fb686a66a304f0a719bc53d77412460af97fd61/evaluate.py +""" + +import functools +import itertools + +from big_vision import input_pipeline +from big_vision import utils +from big_vision.datasets import core as ds_core +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +import numpy as np + +# Temporary global flag to facilitate backwards compatability. +API = "jit" + + +# Note: global to avoid jax re-compiling across different evaluator instances. +@functools.cache +def _get_predict_fn(predict_fn, mesh=None): + """Wrapper for jit-compiled predict function.""" + + # `out_shardings` annotation is needed because of the `all_gather` ops in the + # pmap implementation. + @functools.partial(jax.jit, + out_shardings=jax.sharding.NamedSharding( + mesh, jax.sharding.PartitionSpec())) + def _run_predict_fn(train_state, batch): + """Run predict_fn and gather all outputs on all devices.""" + pred = predict_fn(train_state, batch) + return {"mask": batch["_mask"], + "gt": jnp.squeeze(batch["ground_truth"], axis=-1), + "y": pred["depth"]} + return _run_predict_fn + + +class Evaluator: + """Evaluator for NYU depth.""" + + def __init__(self, + predict_fn, + pp_fn, + batch_size, + data, + cache_final=True, + cache_raw=False, + prefetch=1, + min_depth=1e-3, + max_depth=10, + *, + devices): + """Evaluator for NYU depth. + + Args: + predict_fn: jit-compilable function, accepts arbitrary dictionaries of + parameters and data, where the data dictionary is produced by the + `pp_fn` op. It is expected to output a dict with `depth` containing an + 2D array with the predicted depth. The prediction is resized to the + ground_truth size with nearest neighbour. + pp_fn: Preprocessing function, sepcified as string. `pp_fn` must also + output a 'ground_truth' as a 2D array of ground truth. Fruther, it has + to apply a crop, if one wants to compute metrics with the eval crop + typically used for NYU Depth metrics. + batch_size: Batch size. + data: Dict specifying name and split of the data set. Defaults to the + standard COCO (2017). + cache_final: Whether to cache the data after preprocessing - see + input_pipeline for details. + cache_raw: Whether to cache the raw data - see input_pipline for details. + prefetch: Number of batches to prefetch + min_depth: Minimum depth value. + max_depth: Maximum depth value. + devices: List of jax devices. + """ + self.min_depth = min_depth + self.max_depth = max_depth + self.predict_fn = _get_predict_fn( + predict_fn, jax.sharding.Mesh(devices, ("devices",))) + + data = ds_core.get(**data) + self.dataset, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), batch_size=batch_size, + num_ex_per_process=data.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn), + cache_final=cache_final, cache_raw=cache_raw) + self.data_iter = input_pipeline.start_global( + self.dataset, devices, prefetch) + + def run(self, train_state): + """Run NYU depth eval. + + Args: + train_state: pytree containing the model parameters. + + Yields: + Tuples consisting of metric name and value. + """ + rmses = [] + abs_res = [] + abs_logs = [] + d1s = [] + d2s = [] + d3s = [] + for batch in itertools.islice(self.data_iter, self.steps): + # Outputs is a dict with values shaped (gather/same, devices, batch, ...) + out = self.predict_fn(train_state, batch) + + if jax.process_index(): # Host0 gets all preds and does eval. + continue + + out = jax.device_get(out) + # Then the bool-indexing with mask resulting in flat (global_batch, ...) + out = jax.tree_map(lambda x: x[out["mask"]], out) # pylint:disable=cell-var-from-loop + + for gt, pred in zip(out["gt"], out["y"]): + # put_cpu and conversion to numpy arrays below to avoid unwanted + # host-to-device transfers + pred, gt = utils.put_cpu((pred, gt)) + pred = _resize_nearest(pred, (gt.shape[0], gt.shape[1])) + pred, gt = np.array(pred), np.array(gt) + valid_mask = np.logical_and(gt > self.min_depth, gt < self.max_depth) + + rmses.append(_compute_rmse(gt[valid_mask], pred[valid_mask])) + abs_res.append(_compute_abs_re(gt[valid_mask], pred[valid_mask])) + abs_logs.append(_compute_abs_log(gt[valid_mask], pred[valid_mask])) + d1s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=1)) + d2s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=2)) + d3s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=3)) + + if jax.process_index(): # Host0 gets all preds and does eval. + return + + yield "RMSE", np.mean(rmses) + yield "abs_RE", np.mean(abs_res) + yield "log10", np.mean(abs_logs) + yield "delta1", np.mean(d1s) + yield "delta2", np.mean(d2s) + yield "delta3", np.mean(d3s) + + +@utils.jit_cpu(static_argnums=(1,)) +def _resize_nearest(image, shape): + return jax.image.resize(image, shape, "nearest") + + +def _compute_rmse(gt, pred): + diff = gt - pred + return np.sqrt(np.mean(np.power(diff, 2))) + + +def _compute_abs_re(gt, pred): + diff = np.abs(gt - pred) + return np.mean(diff / gt) + + +def _compute_abs_log(gt, pred): + diff = np.abs(np.log10(gt) - np.log10(pred)) + return np.mean(diff) + + +def _compute_delta(gt, pred, order): + rel_diff = np.maximum(gt / pred, pred / gt) + return np.sum(rel_diff < 1.25**order) / rel_diff.size diff --git a/big_vision/evaluators/proj/givt/save_predictions.py b/big_vision/evaluators/proj/givt/save_predictions.py new file mode 100644 index 0000000000000000000000000000000000000000..d72c48e31ee754cc2722c8e062db064c5f110264 --- /dev/null +++ b/big_vision/evaluators/proj/givt/save_predictions.py @@ -0,0 +1,118 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator to save predictions.""" +# pylint: disable=consider-using-from-import +import functools +import io # pylint: disable=unused-import +import itertools +import os + +from absl import flags +from absl import logging +from big_vision import input_pipeline +from big_vision.datasets import core as ds_core +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +import jax +import numpy as np + +from tensorflow.io import gfile # pylint: disable=unused-import + +# Temporary global flag to facilitate backwards compatability. +API = 'jit' + + +# Note: global to avoid jax re-compiling across different evaluator instances. +@functools.cache +def _get_predict_fn(predict_fn, mesh=None): + """Wrapper for jit-compiled predict function.""" + + # `out_shardings` annotation is needed because of the `all_gather` ops in the + # pmap implementation. + @functools.partial(jax.jit, + out_shardings=jax.sharding.NamedSharding( + mesh, jax.sharding.PartitionSpec())) + def _run_predict_fn(train_state, batch): + """Run predict_fn and gather all outputs on all devices.""" + y = predict_fn(train_state, batch) + return {'inputs': batch, 'outputs': y, 'mask': batch['_mask']} + return _run_predict_fn + + +class Evaluator: + """Save predictions in "{FLAGS.workdir}/{outfile}". + + Results can then be easily inspected in a notebook such as: + + ``` + results = utils.load_checkpoint("") + inputs, outputs = (results["inputs"], results["outputs"]) + ``` + """ + + def __init__(self, predict_fn, pp_fn, batch_size, data, outfile, + cache_final=True, cache_raw=False, prefetch=1, *, devices): + self.predict_fn = _get_predict_fn( + predict_fn, jax.sharding.Mesh(devices, ('devices',))) + + # Prepare data for each process and pad with zeros so all processes have the + # same number of batches. + data = ds_core.get(**data) + self.dataset, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), batch_size=batch_size, + num_ex_per_process=data.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn), + cache_final=cache_final, cache_raw=cache_raw) + self.data_iter = input_pipeline.start_global( + self.dataset, devices, prefetch) + + self.path = os.path.join(flags.FLAGS.workdir, outfile) + + def run(self, train_state): + """Compute all predictions, gather in main host and save in outfile.""" + count = 0 + outputs = [] + for batch in itertools.islice(self.data_iter, self.steps): + out = self.predict_fn(train_state, batch) + if jax.process_index(): + continue + + out = jax.device_get(out) + # Note that we need to access `out['mask']` here `x` does not have that + # field during the tree map. + out = jax.tree_map(lambda x: x[out['mask']], out) # pylint: disable=cell-var-from-loop + count += out['mask'].shape[0] + out.pop('mask') + outputs.append(out) + + logging.log_every_n_seconds( + logging.INFO, 'Save predictions: processed %i examples so far.', 30, + count) + + if jax.process_index(): + return + + logging.info('Save predictions: processed %d examples.', count) + + # Actually save in filesystem. + outputs = jax.tree_map(lambda *x: np.concatenate(x, axis=0), *outputs) + names_and_vals, _ = u.tree_flatten_with_names(outputs) + io_buffer = io.BytesIO() + np.savez_compressed(io_buffer, **{k: v for k, v in names_and_vals}) + with gfile.GFile(self.path, 'wb') as f: + f.write(io_buffer.getvalue()) + return + + yield None # pylint: disable=unreachable diff --git a/big_vision/evaluators/proj/image_text/contrastive.py b/big_vision/evaluators/proj/image_text/contrastive.py new file mode 100644 index 0000000000000000000000000000000000000000..0726d2066cb7c406aec47c508e195b2f40399ff5 --- /dev/null +++ b/big_vision/evaluators/proj/image_text/contrastive.py @@ -0,0 +1,99 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for the contrastive task. + +DON'T COMPARE ACROSS RUNS, use for training health monitoring only. + +Note that this evaluator's `ncorrect_minibatch` is only a rough proxy for +training progress and does not report the actual `ncorrect`: when the same +labels found multiple times in a batch, then the reported value is biased +towards lower values. + +Also note that the `ncorrect_minibatch` is a function of batch size (it's a lot +easier to find correct values in small batches). +""" +import functools + +from big_vision import input_pipeline +import big_vision.datasets.core as ds_core +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +import jax +import jax.numpy as jnp +import numpy as np + + +def _all_gather(z): + """All gather and flatten first two dims.""" + gather_flat = lambda x: jnp.concatenate(jax.lax.all_gather(x, "batch"), 0) + return jax.tree_map(gather_flat, z) + + +# To avoid re-compiling the function for every new instance of the same +# evaluator on a different dataset! +@functools.lru_cache(None) +def get_eval_fn(predict_fn, use_global_batch): + """Produces eval function, also applies pmap.""" + + @functools.partial(jax.pmap, axis_name="batch") + def _eval_fn(params, images, labels, mask): + zimg, ztxt, extras = predict_fn(params, images, labels) + + if use_global_batch: + zimg, ztxt, mask = _all_gather((zimg, ztxt, mask)) + + # Temperature won't affect ranking for accuracy, but impacts loss magnitude. + losses, measurements = u.bidirectional_contrastive_loss( + zimg, ztxt, extras["t"], mask, reduction=False) + l = jax.lax.psum(losses * mask, axis_name="batch") + c = jax.lax.psum(measurements["ncorrect"] * mask, axis_name="batch") + n = jax.lax.psum(mask, axis_name="batch") + return c, l, n + + return _eval_fn + + +class Evaluator: + """Contrastive evaluator.""" + + def __init__(self, predict_fn, data, pp_fn, batch_size, + use_global_batch, cache_final=True, + cache_raw=False, prefetch=1, label_key="labels"): + data = ds_core.get(**data) + pp_fn = pp_builder.get_preprocess_fn(pp_fn) + self.ds, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), pp_fn, batch_size, + num_ex_per_process=data.num_examples_per_process(), + cache_final=cache_final, cache_raw=cache_raw) + self.data_iter = input_pipeline.start_input_pipeline(self.ds, prefetch) + self.eval_fn = get_eval_fn(predict_fn, use_global_batch) + self.label_key = label_key + + def run(self, params): + """Computes all metrics.""" + l, c, nseen = 0, 0, 0 + for _, batch in zip(range(self.steps), self.data_iter): + labels, mask = batch.pop(self.label_key), batch.pop("_mask") + batch_ncorrect, batch_losses, batch_n = self.eval_fn( + params, batch["image"], labels, mask) + # All results are a replicated array shaped as follows: + # (local_devices, per_device_batch_size, elem_shape...) + # with each local device's entry being identical as they got psum'd. + # So let's just take the first one to the host as numpy. + c += np.sum(np.array(batch_ncorrect[0])) + l += np.sum(np.array(batch_losses[0])) + nseen += np.sum(np.array(batch_n[0])) + yield ("ncorrect_minibatch", c / nseen) + yield ("loss", l / nseen) diff --git a/big_vision/evaluators/proj/image_text/discriminative_classifier.py b/big_vision/evaluators/proj/image_text/discriminative_classifier.py new file mode 100644 index 0000000000000000000000000000000000000000..e233564404c17ef9e8d898d13a57a96148f6c9ae --- /dev/null +++ b/big_vision/evaluators/proj/image_text/discriminative_classifier.py @@ -0,0 +1,440 @@ +# Copyright 2024 Big Vision 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. + +"""Discriminative zero-shot classification evaluator. +""" + +import functools +import time + +from absl import logging +from big_vision import input_pipeline +from big_vision import utils +from big_vision.evaluators.proj.image_text import prompt_engineering +from big_vision.pp import ops_general # pylint: disable=unused-import +from big_vision.pp import ops_image # pylint: disable=unused-import +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +from jax.sharding import NamedSharding +from jax.sharding import PartitionSpec as P +import numpy as np +import tensorflow as tf +import tensorflow_datasets as tfds + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +DATASET_NAMES = ("imagenet2012", "cifar100", "oxford_iiit_pet") +DEFAULT_OVERRIDES = ( + ("imagenet2012", ( + ("class_names", "clip"), + ("split", "validation"), + )), + ) + + +def _with_infinite_padding(dataset): + """Adds "infinite padding" to the dataset.""" + filler_element = tf.nest.map_structure( + lambda spec: tf.zeros(spec.shape, spec.dtype)[None], dataset.element_spec) + filler_element["mask"] = [False] + filler_dataset = tf.data.Dataset.from_tensor_slices(filler_element) + dataset = dataset.map( + lambda features: dict(mask=True, **features), + num_parallel_calls=tf.data.experimental.AUTOTUNE) + return dataset.concatenate(filler_dataset.repeat(None)) + + +# This is needed so retrieval_test can replace dataset info. +def _get_dataset_info(builder): + return builder.info + + +def prepare_datasets(img_dataset, + class_names, + *, + prompt_templates, + pp_img, + pp_txt, + cache_final=False, + pre_filter_fn=None, + class_name_offset=0): + """Returns unbatched `ds_images, ds_texts` datasets.""" + + assert prompt_templates, "Must specify prompt templates (e.g. simply ['{}'])" + + def expand_aliases(idx, class_name): + class_names = tf.strings.split(class_name, ",") + return tf.data.Dataset.from_tensor_slices(( + tf.repeat([idx + class_name_offset], len(class_names), axis=0), + class_names, + )) + + def add_prompts(idx, class_name): + return tf.data.Dataset.from_tensor_slices({ + "label": tf.repeat([idx], len(prompt_templates), axis=0), + "class_name": tf.repeat([class_name], len(prompt_templates), axis=0), + "prompt_template": prompt_templates, + }) + + def substitute_prompt(features): + parts = tf.strings.split(features["prompt_template"], "{}") + tf.debugging.assert_equal(len(parts), 2, features["prompt_template"]) + return { + "label": features["label"], + "texts": tf.strings.join([parts[0], features["class_name"], parts[1]]) + } + + if pre_filter_fn: + img_dataset = img_dataset.filter(pre_filter_fn) + ds_images = img_dataset.map( + pp_builder.get_preprocess_fn(f"{pp_img}|keep('label', 'image')")) + ds_texts = tf.data.Dataset.from_tensor_slices(list(class_names)).enumerate( + ).flat_map(expand_aliases).flat_map(add_prompts).map(substitute_prompt).map( + pp_builder.get_preprocess_fn(f"{pp_txt}|keep('label', 'labels')")) + + if cache_final: + ds_images, ds_texts = ds_images.cache(), ds_texts.cache() + + return ds_images, ds_texts + + +def _split_and_batch(dataset_name, data_dir, class_names, batch_size, split, + get_ds): + """Splits dataset, calls `get_ds` and returns padded + batched datasets.""" + assert not batch_size % jax.device_count(), ( + f"batch_size={batch_size} % jax.device_count()={jax.device_count()}") + builder = tfds.builder(dataset_name, data_dir=data_dir) + + # Split class names (last process gets remainder). + if len(class_names) < jax.process_count(): + # See (internal link) for more details. + class_names += [""] * (jax.process_count() - len(class_names)) + per_process = len(class_names) // jax.process_count() + class_name_offset = per_process * jax.process_index() + if jax.process_index() == jax.process_count() - 1: + class_names = class_names[class_name_offset:] + else: + class_names = class_names[class_name_offset:class_name_offset + per_process] + + ds_images, ds_texts = get_ds( + builder.as_dataset(split=tfds.split_for_jax_process(split)), + class_names, + class_name_offset=class_name_offset) + return ( + _with_infinite_padding(ds_images).batch(batch_size), + _with_infinite_padding(ds_texts).batch(batch_size), + ) + + +def _average_embeddings(embeddings, *, labels, num_classes, normalize): + """Computes per-class averages of `embeddings`.""" + assert embeddings.ndim == 2, f"Expected {embeddings.ndim}==2" + assert labels.ndim == 1, f"Expected {labels.ndim}==1" + assert len(labels) == len(embeddings), ( + f"Expected {len(labels)}=={len(embeddings)}") + + byidx = [[] for _ in range(num_classes)] + for label, embedding in zip(labels, embeddings): + byidx[label].append(embedding) + missing = set(range(num_classes)) - set( + idx for idx, embs in enumerate(byidx) if len(embs)) + assert not missing, f"Classes without embeddings: {missing}" + embeddings = [np.array(embedding).mean(axis=0) for embedding in byidx] + embeddings = np.stack(embeddings) + + assert len(embeddings) == num_classes + if normalize: + embeddings /= 1e-8 + np.linalg.norm(embeddings, axis=1, keepdims=True) + return embeddings + + +class Evaluator: + """Zero-shot classification evaluator.""" + + def __init__(self, + predict_fn, + *, + batch_size, + devices, + dataset_names=DATASET_NAMES, + data_dir=None, + class_names="dataset_info:label", + split="test", + prompt_templates="clip_paper", + canonicalize=True, + pp_img="resize(224)|value_range(-1,1)", + pp_txt="tokenize(max_len=16, eos='sticky', " + "pad_value=1, inkey='texts', outkey='labels')", + cache_final=False, + pre_filter_fn=None, + first_class_name_only=True, + dataset_overrides=DEFAULT_OVERRIDES, + async_delay=1): + """Initializes a new zero-shot classification evaluator. + + See `prepare_datasets()` for details on how the dataset is pre-processed. + + Args: + predict_fn: Prediction function with signature + `zimg, ztxt, out = predict_fn(params, images, texts)` + batch_size: Global batch size. + devices: list of devices. + dataset_names: Names of TFDS datasets to evaluate on. + data_dir: Optional argument to `tfds.builder()`. + class_names: Usually specified as a string that is interpreted by + `prompt_engineering.get_class_names()` to look up class names. + Alternatively, this attribute can be a list of class names (using "," + to separate multiple aliases). + split: Which dataset split to use for evaluation. + prompt_templates: Specifies which prompt templates to use. See module + big_vision.evaluators.proj.image_text.prompte_engineering + for valid values. + canonicalize: Whether class names and prompt templates should be + canonicalized. See `prompt_engineering.py` for details. + pp_img: Preprocessing string for images. Preprocessed features should + contain key "image" with value that can be batched and is suitable for + the `images` argument of `predict_fn` input``. + pp_txt: Preprocessing string for texts. Can expect "texts" key as an input + (shape=[], dtype=string), and is expected to produce "labels" key that + is suitable for the `text` argument of `predict_fn` input. + cache_final: Wether preprocesse dataset should be cached. + pre_filter_fn: Predicate applied to the dataset for filtering records. + first_class_name_only: Whether only the first class name should be + considered (i.e. not using any aliases). + dataset_overrides: Mapping `dataset_name` to an optional dictionary that + can override parameters `dataset_name`, `data_dir`, `pp_img`, `pp_txt`, + `class_names`, `split`, `pre_filter_fn`, and the extra + `class_names_dataset_name`. + Works with tuple/dict of tuples/dicts. + async_delay: How many steps to wait before checking if all hosts have + finished their batch. A value > 1 allows for more parallelized + processing, but will results in more unnecessary steps with padded data. + """ + t0 = time.monotonic() + self.datasets = {} + self.prompt_templates = prompt_engineering.get_prompt_templates( + prompt_templates, canonicalize=canonicalize) + self._axis_name = "batch" + dataset_overrides = {k: dict(v) for k, v in dict(dataset_overrides).items()} + + for dataset_name in dataset_names: + overrides = dataset_overrides.pop(dataset_name, {}) + dataset_name_ = overrides.pop("dataset_name", dataset_name) + data_dir_ = overrides.pop("data_dir", data_dir) + class_names_dataset_name = overrides.pop("class_names_dataset_name", + dataset_name_) + class_names_ = overrides.pop("class_names", class_names) + class_names_ = prompt_engineering.get_class_names( + dataset_name=class_names_dataset_name, + source=class_names_, + canonicalize=canonicalize) + pp_img_ = overrides.pop("pp_img", pp_img) + pp_txt_ = overrides.pop("pp_txt", pp_txt) + cache_final_ = overrides.pop("cache_final", cache_final) + split_ = overrides.pop("split", split) + pre_filter_fn_ = overrides.pop("pre_filter_fn", pre_filter_fn) + prompt_templates_ = overrides.pop("prompt_templates", prompt_templates) + canonicalize_ = overrides.pop("canonicalize", canonicalize) + prompt_templates_ = prompt_engineering.get_prompt_templates( + prompt_templates_, canonicalize=canonicalize_) + assert not overrides, f"Unknown overrides {dataset_name}: {overrides}" + + if first_class_name_only: + class_names_ = [name.split(",")[0] for name in class_names_] + ds_images, ds_texts = _split_and_batch( + dataset_name=dataset_name_, + data_dir=data_dir_, + class_names=class_names_, + batch_size=batch_size, + split=split_, + get_ds=functools.partial( + prepare_datasets, + pp_img=pp_img_, + pp_txt=pp_txt_, + cache_final=cache_final_, + pre_filter_fn=pre_filter_fn_, + prompt_templates=prompt_templates_)) + self.datasets[dataset_name] = dict( + images=ds_images, texts=ds_texts, class_names=class_names_, + dataset_name=dataset_name_, split=split_) + + assert not dataset_overrides, f"Extra overrides: {dataset_overrides}" + + def embed_texts(train_state, texts): + """Returns text embeddings.""" + _, ztxt, _ = predict_fn(train_state, {"labels": texts}) + return ztxt + + def count_correct(train_state, return_embeddings, *, mask, labels, image, + ztxt): + """Returns count of correct predictions (and optionally embeddings).""" + zimg, _, _ = predict_fn(train_state, {"image": image}) + best_txt = (zimg @ ztxt.T).argmax(axis=1) + # labels has format [[1, -1, -1], [5, -1, -1], [7, 2, -1], ...] + # so here we count "any" correct, such that the counting matches the + # multilabel scenario described in "are we done with imagenet" + # (http://arxiv.org/abs/2006.07159) section 3.1 + if labels.ndim == 1: + labels = labels[..., None] + assert labels.ndim == 2, labels.shape + matching = (best_txt[:, None] == labels).sum(axis=1) + correct = jnp.where(mask, (matching > 0).astype(jnp.int32), 0).sum() + correct = jnp.sum(correct) + if return_embeddings: + return correct, zimg + else: + return correct, None + + self.devices = devices + self.mesh = jax.sharding.Mesh(devices, ("devices",)) + + self._embed_texts_p = jax.jit( + embed_texts, out_shardings=NamedSharding(self.mesh, P())) + self._count_correct_p = jax.jit(count_correct, static_argnums=(1,), + out_shardings=NamedSharding(self.mesh, P())) + self._count_p = jax.jit(jnp.sum, + out_shardings=NamedSharding(self.mesh, P())) + self._all_gather_p = jax.jit( + lambda x: x, out_shardings=NamedSharding(self.mesh, P())) + + self._compiled = set() + assert async_delay > 0, f"async_delay must be >0, not {async_delay}" + self._async_delay = async_delay + logging.info("Initialized evaluator in %.1f seconds", time.monotonic() - t0) + + def _embed_texts(self, train_state, dataset_name): + """Returns per-class averaged text embeddings.""" + t0 = time.monotonic() + logging.info("Starting text embedding...") + ns = [] + embeddings = [] + data = {"label": [], "mask": []} + + ds_b = input_pipeline.start_global( + self.datasets[dataset_name]["texts"], self.devices) + for batch in ds_b: + ns.append(jax.device_get(self._count_p(batch["mask"]))) + if len(ns) >= self._async_delay and ns[-self._async_delay] == 0: + break + + embeddings.append(jax.device_get(self._embed_texts_p( + train_state, batch["labels"]))) + for name in data: + data[name].append(jax.device_get(self._all_gather_p(batch[name]))) + + if self._embed_texts_p not in self._compiled: + logging.info("Compiled text embeddings in %.1fs", time.monotonic() - t0) + t0 = time.monotonic() + self._compiled.add(self._embed_texts_p) + + ns = np.array(ns) + n = ns.sum() + data["embedding"] = embeddings + data = {k: np.concatenate(v, axis=0) for k, v in data.items()} + mask = data.pop("mask").astype(bool) + data = {k: v[mask] for k, v in data.items()} + data["average_embedding"] = _average_embeddings( + data["embedding"], + labels=data["label"], + num_classes=len(self.datasets[dataset_name]["class_names"]), + normalize=True) + + logging.info("Embedded %s text in %d steps - ...%s", dataset_name, len(ns), + ns[-10:]) + logging.info("Totalling %d text in %.1fs", n, time.monotonic() - t0) + logging.info("Total texts embeddings size %.1fM", + data["embedding"].nbytes / 1e6) + return data + + def evaluate(self, + train_state, + dataset_name, + *, + return_embeddings=False): + """Returns evaluation results.""" + texts = self._embed_texts(train_state, dataset_name) + ztxt_p = texts["average_embedding"] + ztxt_p = utils.reshard(ztxt_p, NamedSharding(self.mesh, P())) + + t0 = time.monotonic() + logging.info("Starting image embedding...") + + ns = [] + embeddings = [] + corrects = [] + data = {"mask": [], "label": []} if return_embeddings else {} + + ds_b = input_pipeline.start_global( + self.datasets[dataset_name]["images"], self.devices) + for batch in ds_b: + ns.append(jax.device_get(self._count_p(batch["mask"]))) + if len(ns) >= self._async_delay and ns[-self._async_delay] == 0: + break + + labels = batch["label"] + correct_p, embs_p = self._count_correct_p( + train_state, + return_embeddings, + mask=batch["mask"], + labels=labels, + image=batch["image"], + ztxt=ztxt_p, + ) + corrects.append(jax.device_get(correct_p)) + if self._count_correct_p not in self._compiled: + logging.info("Compiled image embeddings in %.1fs", + time.monotonic() - t0) + t0 = time.monotonic() + self._compiled.add(self._count_correct_p) + + if return_embeddings: + embeddings.append(jax.device_get(self._all_gather_p(embs_p))) + for name in data: + data[name].append(jax.device_get(self._all_gather_p(batch[name]))) + + ns = np.array(ns) + n = ns.sum() + correct = np.array(corrects).sum() + + logging.info("Embedded %s image in %d steps - ...%s", dataset_name, len(ns), + ns[-10:]) + logging.info("Totalling %d image in %.1fs", n, time.monotonic() - t0) + ret = { + "accuracy": correct / n, + "correct": correct, + "count": n, + } + logging.info("Dataset %s, results %s", dataset_name, ret) + + if return_embeddings: + data["embedding"] = embeddings + data = {k: np.concatenate(v, axis=0) for k, v in data.items()} + logging.info("Total images embeddings size %.1fM", + data["embedding"].nbytes / 1e6) + mask = data.pop("mask").astype(bool) + ret["images"] = {k: v[mask] for k, v in data.items()} + ret["texts"] = texts + + return ret + + def run(self, train_state): + """Returns metrics.""" + return [(f"{dataset_name}_accuracy", + self.evaluate(train_state, dataset_name)["accuracy"]) + for dataset_name in self.datasets] diff --git a/big_vision/evaluators/proj/image_text/discriminative_classifier_test.py b/big_vision/evaluators/proj/image_text/discriminative_classifier_test.py new file mode 100644 index 0000000000000000000000000000000000000000..06f4fe28910191555779c21a838032ae8bc506cd --- /dev/null +++ b/big_vision/evaluators/proj/image_text/discriminative_classifier_test.py @@ -0,0 +1,237 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for discriminative_classifier.""" + +from unittest import mock + +from big_vision.evaluators.proj.image_text import discriminative_classifier +from big_vision.pp import ops_general # pylint: disable=unused-import +from big_vision.pp import ops_image # pylint: disable=unused-import +from big_vision.pp.registry import Registry +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np +import tensorflow as tf +import tensorflow_datasets as tfds + + +@Registry.register("preprocess_ops.test_texts2labels") +def _get_test_texts2labels(): + + def pp(features): + features["labels"] = tf.strings.to_number(features["texts"]) + return features + + return pp + + +@Registry.register("preprocess_ops.copy_from") +def _get_copy_from(**key_map): + + def copy_from(d): + d = dict(d) + for k1, k2 in key_map.items(): + d[k1] = d[k2] + return d + + return copy_from + + +class _Model(nn.Module): + + @nn.compact + def __call__(self, image, texts): + self.param("x", lambda _: 0.) + + def z(x): + if x is not None: + # Note that the returned vector is most similar with other vectors + # generated from the same underlying `x[:]`. + return jnp.stack([jnp.cos(x / 10.), jnp.sin(x / 10.)]).T + + if texts is not None: + texts %= 5 # For testing `pre_filter_fn` below. + return z(image), z(texts), None + + + class DiscriminativeClassifierTest(tf.test.TestCase): + + def test_prepare_datasets(self): + + def generator(): + yield { + "image": tf.ones([5, 5, 3], tf.float32), + "label": 1, + } + yield { + "image": tf.ones([4, 4, 3], tf.float32), + "label": 2, + } + + ds = tf.data.Dataset.from_generator( + generator, + output_signature={ + "image": tf.TensorSpec(shape=[None, None, 3], dtype=tf.float32), + "label": tf.TensorSpec(shape=[], dtype=tf.int64), + }) + class_names = [ + "class1,class1a", + "class2", + ] + prompt_templates = [ + "test {}", + "test {} test", + ] + ds_img, ds_txt = discriminative_classifier.prepare_datasets( + ds, + class_names, + prompt_templates=prompt_templates, + pp_img="resize(2)", + pp_txt="copy_from(labels='texts')", + ) + + it_img = iter(ds_img) + batch = next(it_img) + self.assertAllEqual(1, batch["label"]) + self.assertAllEqual(tf.ones([2, 2, 3]), batch["image"]) + batch = next(it_img) + self.assertAllEqual(2, batch["label"]) + self.assertAllEqual(tf.ones([2, 2, 3]), batch["image"]) + + it_txt = iter(ds_txt) + batch = next(it_txt) + self.assertAllEqual(0, batch["label"]) + self.assertAllEqual("test class1", batch["labels"]) + batch = next(it_txt) + self.assertAllEqual(0, batch["label"]) + self.assertAllEqual("test class1 test", batch["labels"]) + batch = next(it_txt) + self.assertAllEqual(0, batch["label"]) + self.assertAllEqual("test class1a", batch["labels"]) + batch = next(it_txt) + self.assertAllEqual(0, batch["label"]) + self.assertAllEqual("test class1a test", batch["labels"]) + batch = next(it_txt) + self.assertAllEqual(1, batch["label"]) + self.assertAllEqual("test class2", batch["labels"]) + batch = next(it_txt) + self.assertAllEqual(1, batch["label"]) + self.assertAllEqual("test class2 test", batch["labels"]) + + def test_average_embeddings(self): + self.assertAllEqual(jnp.array([ + [2.], [4.], [8.], + ]), discriminative_classifier._average_embeddings( + embeddings=jnp.array([ + 1., 3., 3., 1., # label1 + 8., 0., # label2 + 32., 0., 0., 0., # label3 + ])[..., None], + labels=jnp.array([ + 0, 0, # label1 + 0, 0, # label1 (alias) + 1, 1, # label2 + 2, 2, # label3 + 2, 2, # label3 (alias) + ], jnp.int32), + num_classes=3, normalize=False)) + self.assertAllEqual( + jnp.array([ + [2**-.5, 2**-.5], + ]), + discriminative_classifier._average_embeddings( + embeddings=jnp.array([[2., 2.]]), + labels=jnp.array([0], jnp.int32), + num_classes=1, + normalize=True)) + + @mock.patch("big_vision.evaluators.proj." + "image_text.prompt_engineering.get_class_names") + @mock.patch("big_vision.evaluators.proj." + "image_text.prompt_engineering.get_prompt_templates") + @mock.patch("big_vision.evaluators.proj." + "image_text.discriminative_classifier._get_dataset_info") + def test_evaluate(self, get_dataset_info_mock, get_prompt_templates_mock, + get_class_names_mock): + per_device_batch_size = 10 # Make sure we have some unfiltered examples. + global_batch_size = per_device_batch_size * jax.device_count() + per_host_num_examples = int( + np.ceil(global_batch_size / jax.process_count())) + splits = { + "test": + tfds.core.SplitInfo( + name="test", shard_lengths=[per_host_num_examples], num_bytes=0) + } + + model = _Model() + params = model.init(jax.random.PRNGKey(0), None, None)["params"] + + prompt_templates = [ + "test prompt 1 {}", + "test prompt 2 {}", + ] + class_names = [ + f"test_class_{i}" for i in range(10) + ] + + get_prompt_templates_mock.return_value = prompt_templates + get_class_names_mock.return_value = class_names + get_dataset_info_mock.return_value.splits = splits + + def pre_filter_fn(features): + return features["label"] < 5 # matches `texts %= 5` above + + dataset_name = "cifar10_test" + with tfds.testing.mock_data(num_examples=per_host_num_examples): + evaluator = discriminative_classifier.Evaluator( + lambda p, b: model.apply({"params": p}, + b.get("image", None), + b.get("labels", None)), + dataset_names=[dataset_name], + prompt_templates="test_prompts", + batch_size=global_batch_size, + devices=jax.devices(), + pp_img="copy_from(image='label')", + pp_txt="copy_from(labels='label')", + dataset_overrides={ + dataset_name: { + "dataset_name": "cifar10", + "class_names": "test_classes", + "pre_filter_fn": pre_filter_fn, + } + }, + first_class_name_only=True, + ) + results = evaluator.evaluate( + params, + dataset_name, + return_embeddings=True) + metrics = dict(evaluator.run(params)) + + # Assert all examples were processed. + self.assertLen(results["texts"]["embedding"], + len(class_names) * len(prompt_templates)) + self.assertLen(results["texts"]["average_embedding"], len(class_names)) + self.assertAllEqual( + sorted(results["texts"]["label"]), + [0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9]) + # Note that above model makes perfect predictions by design. + self.assertEqual(1.0, results["accuracy"]) + self.assertEqual(1.0, metrics[f"{dataset_name}_accuracy"]) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/evaluators/proj/image_text/image_text_retrieval.py b/big_vision/evaluators/proj/image_text/image_text_retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..8d0f7b2d4b84b121b8a642ee301835d1dc59ed62 --- /dev/null +++ b/big_vision/evaluators/proj/image_text/image_text_retrieval.py @@ -0,0 +1,85 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluates image-text retrieval results.""" +from typing import List, Mapping + +import numpy as np + +RECALL_THRESHOLDS = (1, 5, 10) + + +def text_to_image_retrieval_eval( + dist_matrix: np.ndarray, + text_image_correspondence: List[int]) -> Mapping[str, float]: + """Runs the text-to-image retrieval eval from the distance matrix. + + Args: + dist_matrix: Distance matrix between text and image embeddings (shape + N_IMAGES x N_TEXTS). + text_image_correspondence: Mapping between rows and columns of + `dist_matrix`, that is, a list of N_TEXTS integers n_i that represent that + the text embedding in column i corresponds to the image embedding in row + n_i. Please note that many texts can be assigned to the same image. For + instance, if we have 2 images and 4 texts (i.e. dist_matrix is 2x4), then + `text_image_correspondence = [0, 0, 1, 1]` means that the two first texts + correspond to the first image and the two last texts to the second image. + + Returns: + A dictionary with the Recall@k scores for k in RECALL_THRESHOLDS. + """ + per_text_ranks = dist_matrix.argsort(axis=0) + text_image_correspondence = np.array(text_image_correspondence) + + def recall_at(k): + wins = per_text_ranks[:k, :] == text_image_correspondence[None] + return wins.any(axis=0).mean() + + return { + f'Recall@{k}': recall_at(k) + for k in RECALL_THRESHOLDS + } + + +def image_to_text_retrieval_eval( + dist_matrix: np.ndarray, + text_image_correspondence: List[int]) -> Mapping[str, float]: + """Runs the image-to-text retrieval eval from the distance matrix. + + Args: + dist_matrix: Distance matrix between text and image embeddings (shape + N_IMAGES x N_TEXTS). + text_image_correspondence: Mapping between rows and columns of + `dist_matrix`, that is, a list of N_TEXTS integers n_i that represent that + the text embedding in column i corresponds to the image embedding in row + n_i. Please note that many texts can be assigned to the same image. For + instance, if we have 2 images and 4 texts (i.e. dist_matrix is 2x4), then + `text_image_correspondence = [0, 0, 1, 1]` means that the two first texts + correspond to the first image and the two last texts to the second image. + + Returns: + A dictionary with the Recall@k scores for k in RECALL_THRESHOLDS. + """ + per_image_ranks = dist_matrix.argsort(axis=1) + text_image_correspondence = np.array(text_image_correspondence) + + def recall_at(k): + top_k_images = text_image_correspondence[per_image_ranks[:, :k]] + wins = top_k_images == np.arange(len(per_image_ranks))[:, None] + return wins.any(axis=1).mean() + + return { + f'Recall@{k}': recall_at(k) + for k in RECALL_THRESHOLDS + } diff --git a/big_vision/evaluators/proj/image_text/image_text_retrieval_test.py b/big_vision/evaluators/proj/image_text/image_text_retrieval_test.py new file mode 100644 index 0000000000000000000000000000000000000000..5125b17f77d9c0c3a15f0c358415334af0b6c216 --- /dev/null +++ b/big_vision/evaluators/proj/image_text/image_text_retrieval_test.py @@ -0,0 +1,86 @@ +# Copyright 2024 Big Vision 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. + +"""Unit tests for image_text_retrieval.""" +from typing import Mapping + +from absl.testing import absltest +from absl.testing import parameterized +from big_vision.evaluators.proj.image_text import image_text_retrieval +import numpy as np + + +class ImTextRetrievalTest(parameterized.TestCase): + + @parameterized.parameters( + (np.array([[0.0, 0.0, 0.1, 0.5, 0.1, 0.2, 0.5, 0.1], + [0.5, 0.4, 0.0, 0.0, 0.4, 0.2, 0.6, 0.4], + [0.5, 0.4, 0.1, 0.5, 0.0, 0.0, 0.8, 0.3], + [0.5, 0.4, 0.1, 0.5, 0.3, 0.2, 0.0, 0.0]]), { + 'Recall@1': 1.0, + 'Recall@5': 1.0, + 'Recall@10': 1.0 + }), # + (np.array([[0.8, 0.8, 0.1, 0.5, 0.1, 0.2, 0.5, 0.1], + [0.5, 0.4, 0.0, 0.0, 0.4, 0.2, 0.6, 0.4], + [0.5, 0.4, 0.1, 0.5, 0.0, 0.8, 0.8, 0.3], + [0.5, 0.4, 0.1, 0.5, 0.4, 0.2, 0.3, 0.3]]), { + 'Recall@1': 0.5, + 'Recall@5': 0.75, + 'Recall@10': 1.0 + })) + def test_image_to_text_retrieval_eval(self, dist_matrix: np.ndarray, + expected: Mapping[str, float]): + """Checks `image_to_text_retrieval_eval`. + + Args: + dist_matrix: Distance matrix between image (rows) and text (columns). + expected: Expected eval results. + """ + self.assertEqual( + image_text_retrieval.image_to_text_retrieval_eval( + dist_matrix, [0, 0, 1, 1, 2, 2, 3, 3]), expected) + + @parameterized.parameters( + (np.array([[0.0, 0.0, 0.1, 0.5, 0.1, 0.2, 0.5, 0.1], + [0.5, 0.4, 0.0, 0.0, 0.4, 0.2, 0.6, 0.4], + [0.5, 0.4, 0.1, 0.5, 0.0, 0.0, 0.8, 0.3], + [0.5, 0.4, 0.1, 0.5, 0.3, 0.2, 0.0, 0.0]]), { + 'Recall@1': 1.0, + 'Recall@5': 1.0, + 'Recall@10': 1.0 + }), # + (np.array([[0.8, 0.8, 0.1, 0.5, 0.1, 0.2, 0.1, 0.1], + [0.5, 0.4, 0.0, 0.0, 0.4, 0.2, 0.6, 0.4], + [0.5, 0.4, 0.1, 0.5, 0.0, 0.8, 0.8, 0.3], + [0.5, 0.4, 0.1, 0.5, 0.4, 0.2, 0.3, 0.3]]), { + 'Recall@1': 0.375, + 'Recall@5': 1.0, + 'Recall@10': 1.0 + })) + def test_image_text_retrieval(self, dist_matrix: np.ndarray, + expected: Mapping[str, float]): + """Checks `text_to_image_retrieval_eval`. + + Args: + dist_matrix: Distance matrix between image (rows) and text (columns). + expected: Expected eval results. + """ + self.assertEqual( + image_text_retrieval.text_to_image_retrieval_eval( + dist_matrix, [0, 0, 1, 1, 2, 2, 3, 3]), expected) + + +if __name__ == '__main__': + absltest.main() diff --git a/big_vision/evaluators/proj/image_text/prompt_engineering.py b/big_vision/evaluators/proj/image_text/prompt_engineering.py new file mode 100644 index 0000000000000000000000000000000000000000..52450f6a7640d9865dbf19e11945281c9464657d --- /dev/null +++ b/big_vision/evaluators/proj/image_text/prompt_engineering.py @@ -0,0 +1,112 @@ +# Copyright 2024 Big Vision 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. + +"""Utilities for generating zero-shot prompts.""" + +import re +import string +from typing import Sequence + +from absl import logging +from big_vision.datasets.imagenet import class_names as imagenet_class_names +from big_vision.evaluators.proj.image_text import prompt_engineering_constants +import tensorflow_datasets as tfds + + +_CLASS_NAMES = { # For each dataset, maps from a source to its class names. + "imagenet2012": { + "clip": imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES, + }, + "grand-vision:imagenet2012": { + "clip": imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES, + }, + "imagenet_a": { + "clip": [ + imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES[i] + for i in imagenet_class_names.IMAGENET_A_LABELSET + ] + }, + "imagenet_r": { + "clip": [ + imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES[i] + for i in imagenet_class_names.IMAGENET_R_LABELSET + ] + }, + "imagenet_v2": { + "clip": imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES, + }, +} + +_PROMPT_TEMPLATES = { + "class_name_only": ["{}"], + "clip_paper": prompt_engineering_constants.CLIP_PAPER_PROMPT_TEMPLATES, + "clip_best": prompt_engineering_constants.CLIP_BEST_PROMPT_TEMPLATES, +} + + +def get_class_names(*, dataset_name, source="dataset_info", canonicalize=True): + """Returns class name for `dataset_name` from `source`.""" + if isinstance(source, str): + if source.startswith("dataset_info:"): + name = source[len("dataset_info:"):] + class_names = tfds.builder(dataset_name).info.features[name].names + else: + class_names = _CLASS_NAMES[dataset_name][source] + else: + assert isinstance(source, Sequence) and all( + map(lambda s: isinstance(s, str), source)), source + class_names = source + if canonicalize: + class_names = [ + canonicalize_text(name, keep_punctuation_exact_string=",") + for name in class_names + ] + logging.info("Using %d class_names: %s", len(class_names), class_names) + return class_names + + +def get_prompt_templates(prompt_templates_name, + *, + canonicalize=True): + """Returns prompt templates.""" + prompts_templates = _PROMPT_TEMPLATES[prompt_templates_name] + if canonicalize: + prompts_templates = [ + canonicalize_text(name, keep_punctuation_exact_string="{}") + for name in prompts_templates + ] + logging.info("Using %d prompts_templates: %s", len(prompts_templates), + prompts_templates) + return prompts_templates + + +def canonicalize_text(text, *, keep_punctuation_exact_string=None): + """Returns canonicalized `text` (lowercase and puncuation removed). + + Args: + text: string to be canonicalized. + keep_punctuation_exact_string: If provided, then this exact string kept. + For example providing '{}' will keep any occurrences of '{}' (but will + still remove '{' and '}' that appear separately). + """ + text = text.replace("_", " ") + if keep_punctuation_exact_string: + text = keep_punctuation_exact_string.join( + part.translate(str.maketrans("", "", string.punctuation)) + for part in text.split(keep_punctuation_exact_string)) + else: + text = text.translate(str.maketrans("", "", string.punctuation)) + text = text.lower() + text = re.sub(r"\s+", " ", text) + return text.strip() diff --git a/big_vision/evaluators/proj/image_text/prompt_engineering_constants.py b/big_vision/evaluators/proj/image_text/prompt_engineering_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..0997971f639711e592f83f71daafd0d6d47f6e49 --- /dev/null +++ b/big_vision/evaluators/proj/image_text/prompt_engineering_constants.py @@ -0,0 +1,110 @@ +# Copyright 2024 Big Vision 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. + +"""Constants used by the module `prompt_engineering` in the same directory.""" + +CLIP_PAPER_PROMPT_TEMPLATES = [ + 'a bad photo of a {}.', + 'a photo of many {}.', + 'a sculpture of a {}.', + 'a photo of the hard to see {}.', + 'a low resolution photo of the {}.', + 'a rendering of a {}.', + 'graffiti of a {}.', + 'a bad photo of the {}.', + 'a cropped photo of the {}.', + 'a tattoo of a {}.', + 'the embroidered {}.', + 'a photo of a hard to see {}.', + 'a bright photo of a {}.', + 'a photo of a clean {}.', + 'a photo of a dirty {}.', + 'a dark photo of the {}.', + 'a drawing of a {}.', + 'a photo of my {}.', + 'the plastic {}.', + 'a photo of the cool {}.', + 'a close-up photo of a {}.', + 'a black and white photo of the {}.', + 'a painting of the {}.', + 'a painting of a {}.', + 'a pixelated photo of the {}.', + 'a sculpture of the {}.', + 'a bright photo of the {}.', + 'a cropped photo of a {}.', + 'a plastic {}.', + 'a photo of the dirty {}.', + 'a jpeg corrupted photo of a {}.', + 'a blurry photo of the {}.', + 'a photo of the {}.', + 'a good photo of the {}.', + 'a rendering of the {}.', + 'a {} in a video game.', + 'a photo of one {}.', + 'a doodle of a {}.', + 'a close-up photo of the {}.', + 'a photo of a {}.', + 'the origami {}.', + 'the {} in a video game.', + 'a sketch of a {}.', + 'a doodle of the {}.', + 'a origami {}.', + 'a low resolution photo of a {}.', + 'the toy {}.', + 'a rendition of the {}.', + 'a photo of the clean {}.', + 'a photo of a large {}.', + 'a rendition of a {}.', + 'a photo of a nice {}.', + 'a photo of a weird {}.', + 'a blurry photo of a {}.', + 'a cartoon {}.', + 'art of a {}.', + 'a sketch of the {}.', + 'a embroidered {}.', + 'a pixelated photo of a {}.', + 'itap of the {}.', + 'a jpeg corrupted photo of the {}.', + 'a good photo of a {}.', + 'a plushie {}.', + 'a photo of the nice {}.', + 'a photo of the small {}.', + 'a photo of the weird {}.', + 'the cartoon {}.', + 'art of the {}.', + 'a drawing of the {}.', + 'a photo of the large {}.', + 'a black and white photo of a {}.', + 'the plushie {}.', + 'a dark photo of a {}.', + 'itap of a {}.', + 'graffiti of the {}.', + 'a toy {}.', + 'itap of my {}.', + 'a photo of a cool {}.', + 'a photo of a small {}.', + 'a tattoo of the {}.', + '{}', +] + +CLIP_BEST_PROMPT_TEMPLATES = [ + 'itap of a {}.', + 'a bad photo of the {}.', + 'a origami {}.', + 'a photo of the large {}.', + 'a {} in a video game.', + 'art of the {}.', + 'a photo of the small {}.', + '{}', +] diff --git a/big_vision/evaluators/proj/image_text/prompt_engineering_test.py b/big_vision/evaluators/proj/image_text/prompt_engineering_test.py new file mode 100644 index 0000000000000000000000000000000000000000..e6833c60157d01ba9d787115e643b85506e57d4f --- /dev/null +++ b/big_vision/evaluators/proj/image_text/prompt_engineering_test.py @@ -0,0 +1,48 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for prompt_engineering.""" + +from absl.testing import absltest +from big_vision.evaluators.proj.image_text import prompt_engineering + + +class PromptEngineeringTest(absltest.TestCase): + + def test_canonicalize_text(self): + self.assertEqual(prompt_engineering.canonicalize_text("test_test"), "test test") + self.assertEqual( + prompt_engineering.canonicalize_text("test___test"), "test test") + self.assertEqual(prompt_engineering.canonicalize_text("test"), "test") + self.assertEqual(prompt_engineering.canonicalize_text("test."), "test") + self.assertEqual(prompt_engineering.canonicalize_text(" test "), "test") + self.assertEqual( + prompt_engineering.canonicalize_text("test\ntest"), "test test") + self.assertEqual( + prompt_engineering.canonicalize_text("test test"), "test test") + self.assertEqual(prompt_engineering.canonicalize_text("test {}"), "test") + self.assertEqual( + prompt_engineering.canonicalize_text( + "test {}", keep_punctuation_exact_string="{}"), "test {}") + self.assertEqual( + prompt_engineering.canonicalize_text( + " test {}...", keep_punctuation_exact_string="{}"), "test {}") + self.assertEqual( + prompt_engineering.canonicalize_text( + "test {} {} {}", keep_punctuation_exact_string="{}"), + "test {} {} {}") + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/evaluators/proj/image_text/retrieval.py b/big_vision/evaluators/proj/image_text/retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..daf43927f3273dca1232567578614aa6f783f613 --- /dev/null +++ b/big_vision/evaluators/proj/image_text/retrieval.py @@ -0,0 +1,306 @@ +# Copyright 2024 Big Vision 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. + +"""Multi-host image->text and text->image retrieval evaluation. + +Example how to add to config: + + config.evals {} + config.evals.retieval = dict(log_steps=1200, type='proj.image_text.retrieval') + config.evals.retrieval.dataset = 'coco_captions' + config.evals.retrieval.txt_name = ('captions', 'text') + # Note that initial "decode|" is not needed. + config.evals.retrieval.pp_img = 'resize(224)|value_range(-1,1)' + # Raw text strings use key "texts" in feature dict. The evaluator expects + # tokenized text with key "labels". + config.evals.retrieval.pp_txt = ( + 'tokenize(max_len=16, eos="sticky", pad_value=1, inkey="texts", ' + ' outkey="labels")') + +Example to support precomputed data: +See `big_vision/configs/proj/image_text/lit.py`. +""" + +import functools +import operator +import time + +from absl import logging +from big_vision import input_pipeline +from big_vision.evaluators.proj.image_text import image_text_retrieval +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +from jax.sharding import NamedSharding +from jax.sharding import PartitionSpec as P +import numpy as np +import tensorflow as tf +import tensorflow_datasets as tfds + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +def _with_infinite_padding(dataset): + """Adds "infinite padding" to the dataset.""" + filler_element = tf.nest.map_structure( + lambda spec: tf.zeros(spec.shape, spec.dtype)[None], dataset.element_spec) + filler_element["mask"] = [False] + filler_dataset = tf.data.Dataset.from_tensor_slices(filler_element) + dataset = dataset.map( + lambda features: dict(mask=True, **features), + num_parallel_calls=tf.data.experimental.AUTOTUNE) + return dataset.concatenate(filler_dataset.repeat(None)) + + +# This is needed so retrieval_test can replace dataset info. +def _get_dataset_info(builder): + return builder.info + + +def prepare_datasets( + dataset, *, pp_img, pp_txt, txt_name, offset=0, cache_final=False +): + """Returns unbatched `ds_images, ds_texts` datasets. + + Args: + dataset: An image-text `tf.data.Dataset` that is expected to contain the + following features: "image" (dtype=uint8, shape=[None, None, 3]), + `txt_name` (dtype=string, shape=[None]). + pp_img: String defining pre-processing for images. The pre-processing can + expect the following features to be prepared: "image", "id". The + pre-processing should convert the "image" (dtype=uint8, + shape=[None, None, 3]) to "image" (dtype=float32, shape=[sz, sz, 3]). + pp_txt: String defining pre-processing for text. The pre-processing can + expect the following features to be prepared: "texts", "id", "caption_id". + The pre-processing should convert the "texts" (dtype=string, shape=[]) + into a tokenized "labels" (dtype=int32, shape=[max_len]). + txt_name: Name of the text feature to unroll in the original `dataset`. Can + be a simple string feature name, or an iterable of strings to specify a + nested feature (e.g. for "coco_captions", this would be + `('captions', 'text')`). + offset: Offset that should be added to enumerated examples to generate IDs. + In a multi-host setup, this is typically set to a value large enough to + make all IDs distinct. + cache_final: Whether the dataset should be cached. + + Returns: + Image and text datasets. + """ + + def get_feature_value(data, feature_name): + if isinstance(feature_name, str): + feature_name = [feature_name] + return functools.reduce(operator.getitem, feature_name, data) + + def get_captions(idx, features): + """Returns a dataset with unrolled "caption" for every example.""" + texts = get_feature_value(features, txt_name) + texts = tf.experimental.numpy.atleast_1d(texts) # For single-text GT. + texts_n = tf.shape(texts)[0] + return tf.data.Dataset.from_tensor_slices({ + "id": tf.tile([idx + offset], [texts_n]), + "caption_i": tf.stack(tf.range(texts_n)), + "texts": tf.stack(texts), + }) + + def add_id(idx, features): + return {**features, "id": idx + offset} + + ds_images = dataset.enumerate().map(add_id).map( + pp_builder.get_preprocess_fn(f"{pp_img}|keep('id', 'image')")) + ds_texts = dataset.enumerate().flat_map(get_captions).map( + pp_builder.get_preprocess_fn( + f"{pp_txt}|keep('id', 'caption_i', 'labels')")) + if cache_final: + ds_images, ds_texts = ds_images.cache(), ds_texts.cache() + return ds_images, ds_texts + + +def _split_and_batch(dataset_name, batch_size, split, get_ds, data_dir=None): + """Splits dataset, calls `get_ds` and returns padded + batched datasets.""" + assert not batch_size % jax.device_count(), ( + f"batch_size={batch_size} % jax.device_count()={jax.device_count()}") + builder = tfds.builder(dataset_name, data_dir=data_dir) + info = _get_dataset_info(builder) + num_examples = info.splits[split].num_examples + ds_images, ds_texts = get_ds( + builder.as_dataset(split=tfds.split_for_jax_process(split)), + offset=jax.process_index() * num_examples, + ) + return ( + _with_infinite_padding(ds_images).batch(batch_size), + _with_infinite_padding(ds_texts).batch(batch_size), + ) + + +class Evaluator: + """Image/text retrieval evaluator.""" + + def __init__(self, + predict_fn, + *, + dataset, + pp_img, + pp_txt, + txt_name, + batch_size, + devices, + data_dir=None, + split="test", + cache_final=True): + """Initializes a new zero-shot image/text retrieval evaluator. + + See `prepare_datasets()` for details on how the dataset is pre-processed. + + Args: + predict_fn: Prediction function with signature + `zimg, ztxt, out = predict_fn(params, images, texts)` + dataset: The TFDS dataset name of the eval data. + pp_img: Preprocessing string for images. Preprocessed features should + contain key "image" with value that can be batched and is suitable for + `predict_fn(images)` input``. + pp_txt: Preprocessing string for texts. Can expect "texts" key as an input + (shape=[], dtype=string), and is expected to produce "labels" key that + is suitable for `predict_fn(texts)` input. + txt_name: The name of the feature of captions (can be a tuple to look up a + value in a nested feature dictionary). Expected shape=[None], + dtype=string. specified then items are used as lookup path. + batch_size: Global batch size. + devices: list of devices. + data_dir: Optional dir to load the TFDS dataset from. + split: The split of the eval data. + cache_final: Wether preprocessed dataset should be cached. + """ + self.ds_images, self.ds_texts = _split_and_batch( + dataset, + batch_size, + split, + functools.partial( + prepare_datasets, + pp_img=pp_img, + pp_txt=pp_txt, + txt_name=txt_name, + cache_final=cache_final, + ), + data_dir=data_dir, + ) + self._axis_name = "batch" + + self.devices = devices + mesh = jax.sharding.Mesh(devices, ("devices",)) + + def embed_images(train_state, images): + zimg, _, _ = predict_fn(train_state, {"image": images}) + return zimg + + def embed_texts(train_state, texts): + _, ztxt, _ = predict_fn(train_state, {"labels": texts}) + return ztxt + + self._embed_images_p = jax.jit(embed_images, + out_shardings=NamedSharding(mesh, P())) + self._embed_texts_p = jax.jit(embed_texts, + out_shardings=NamedSharding(mesh, P())) + self._all_gather_p = jax.jit( + lambda x: x, out_shardings=NamedSharding(mesh, P())) + self._count_p = jax.jit(jnp.sum, out_shardings=NamedSharding(mesh, P())) + self._compiled = set() + + def _embed(self, name, train_state, ds, embed_fn, id_names): + """Embeds features name `name` using `embed_fn`. + + Args: + name: Feature name to be embedded. + train_state: train_state for the predict_fn. + ds: The dataset. + embed_fn: A pmapped function that returns the embeddings. + id_names: An iterable of feature names that should be collected. + + Returns: + A dictionary with "embeddings" and `id_names` as keys. + """ + ns = [] + embeddings = [] + ids = {id_name: [] for id_name in list(id_names) + ["mask"]} + + t0 = time.time() + + ds_b = input_pipeline.start_global(ds, self.devices) + for batch in ds_b: + ns.append(jax.device_get(self._count_p(batch["mask"]))) + + # Due to infinite padding, this loop will never end. We will stop once + # all processes only process padded data. We don't check the latest + # DeviceArray `ns[-1]` Because we want to keep our computation async for + # efficiency reasons. + if len(ns) >= 2 and ns[-2] == 0: + break + + embs = embed_fn(train_state, batch[name]) + if embed_fn not in self._compiled: + logging.info("Compiled %s embeddings in %.3fs", name, time.time() - t0) + t0 = time.time() + self._compiled.add(embed_fn) + + embeddings.append(jax.device_get(embs)) + for id_name in ids: + ids[id_name].append(jax.device_get(self._all_gather_p(batch[id_name]))) + + # Only access DeviceArray at end of loop for better efficiency. + ns = np.array(ns) + embeddings = np.concatenate(embeddings) + ids = {k: np.concatenate(v) for k, v in ids.items()} + masks = ids.pop("mask").astype(bool) + logging.info("Processed %s in %d steps - ...%s", name, len(ns), ns[-10:]) + n = ns.sum() + logging.info("Totalling %d %s in %.3fs", n, name, time.time() - t0) + return { + "embeddings": embeddings[masks], + **{k: v[masks] for k, v in ids.items()}, + } + + def evaluate(self, train_state): + """Returns evaluation results.""" + images = self._embed("image", train_state, self.ds_images, + self._embed_images_p, ("id",)) + texts = self._embed("labels", train_state, self.ds_texts, + self._embed_texts_p, ("id", "caption_i")) + # Shapes: (nimg, emb) * (emb, ntxt) -> (nimg, ntxt) + similarities = np.dot(images["embeddings"], texts["embeddings"].T) + + t0 = time.time() + id2img = {id_: i for i, id_ in enumerate(images["id"])} + text_image_correspondence = [id2img[id_] for id_ in texts["id"]] + img2txt = image_text_retrieval.image_to_text_retrieval_eval( + -similarities, text_image_correspondence) + txt2img = image_text_retrieval.text_to_image_retrieval_eval( + -similarities, text_image_correspondence) + logging.info("Computed retrieval metrics in %.3fs", time.time() - t0) + + return dict( + images=images, + texts=texts, + img2txt=img2txt, + txt2img=txt2img, + ) + + def run(self, train_state): + """Returns metrics.""" + results = self.evaluate(train_state) + return [(f"{direction}_{k.lower()}", v) + for direction in ("img2txt", "txt2img") + for k, v in results[direction].items()] diff --git a/big_vision/evaluators/proj/image_text/retrieval_test.py b/big_vision/evaluators/proj/image_text/retrieval_test.py new file mode 100644 index 0000000000000000000000000000000000000000..6a6a993df88819d8d626710bb8135444fba2130a --- /dev/null +++ b/big_vision/evaluators/proj/image_text/retrieval_test.py @@ -0,0 +1,178 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for retrieval.""" + +from unittest import mock + +from big_vision.evaluators.proj.image_text import retrieval +from big_vision.pp import ops_general # pylint: disable=unused-import +from big_vision.pp import ops_image # pylint: disable=unused-import +from big_vision.pp import registry +import chex +import flax.linen as nn +import jax +import jax.numpy as jnp +import tensorflow as tf +import tensorflow_datasets as tfds + + +def _get_test_texts2labels(): + + def pp(features): + features["labels"] = tf.strings.to_number(features["texts"]) + return features + + return pp + + +def _get_copy_from(**key_map): + + def copy_from(d): + d = dict(d) + for k1, k2 in key_map.items(): + d[k1] = d[k2] + return d + + return copy_from + + +class _Model(nn.Module): + + @nn.compact + def __call__(self, image, texts): + self.param("x", lambda _: 0.) + + def z(x): + if x is not None: + batch_size = len(x) + # Note that the returned vector is most similar with other vectors + # generated from the same underlying `x[:]`. + x = jnp.concatenate([100 * jnp.ones([batch_size, 1]), x[:, None]], + axis=1) + return x / jnp.linalg.norm(x, axis=1)[:, None] + + return z(image), z(texts), None + + +def setUpModule(): + chex.set_n_cpu_devices(8) + + +class RetrievalTest(tf.test.TestCase): + + def test_prepare_datasets(self): + + def generator(): + yield { + "image": tf.ones([5, 5, 3], tf.float32), + "captions": { + "text": tf.constant(["11", "12"]) + } + } + yield { + "image": tf.ones([4, 4, 3], tf.float32), + "captions": { + "text": tf.constant(["21", "22", "23"]) + } + } + + ds = tf.data.Dataset.from_generator( + generator, + output_signature={ + "image": tf.TensorSpec(shape=[None, None, 3], dtype=tf.float32), + "captions": { + "text": tf.TensorSpec(shape=[None], dtype=tf.string), + }, + }) + with registry.temporary_ops(test_texts2labels=_get_test_texts2labels): + ds_img, ds_txt = retrieval.prepare_datasets( + ds, + pp_img="resize(2)", + pp_txt="test_texts2labels()", + txt_name=("captions", "text"), + ) + it_img = iter(ds_img) + it_txt = iter(ds_txt) + batch = next(it_img) + self.assertAllEqual(batch["id"], 0) + self.assertAllEqual(batch["image"], tf.ones([2, 2, 3])) + batch = next(it_img) + self.assertAllEqual(batch["id"], 1) + self.assertAllEqual(batch["image"], tf.ones([2, 2, 3])) + batch = next(it_txt) + self.assertAllEqual(batch["id"], 0) + self.assertAllEqual(batch["caption_i"], 0) + self.assertAllEqual(batch["labels"], 11.0) + batch = next(it_txt) + self.assertAllEqual(batch["id"], 0) + self.assertAllEqual(batch["caption_i"], 1) + self.assertAllEqual(batch["labels"], 12.0) + batch = next(it_txt) + self.assertAllEqual(batch["id"], 1) + self.assertAllEqual(batch["caption_i"], 0) + self.assertAllEqual(batch["labels"], 21.0) + batch = next(it_txt) + self.assertAllEqual(batch["id"], 1) + self.assertAllEqual(batch["caption_i"], 1) + self.assertAllEqual(batch["labels"], 22.0) + batch = next(it_txt) + self.assertAllEqual(batch["id"], 1) + self.assertAllEqual(batch["caption_i"], 2) + self.assertAllEqual(batch["labels"], 23.0) + + def test_evaluate(self): + per_device_batch_size = 2 + batch_size = per_device_batch_size * jax.device_count() + num_examples = 1 * batch_size + 1 + splits = { + "test": + tfds.core.SplitInfo( + name="test", shard_lengths=[num_examples], num_bytes=0) + } + + model = _Model() + params = model.init(jax.random.PRNGKey(0), None, None)["params"] + + with tfds.testing.mock_data(num_examples=num_examples): + info_mock = mock.Mock() + info_mock.splits = splits + with mock.patch.object(retrieval, "_get_dataset_info", + lambda _: info_mock): + with registry.temporary_ops(copy_from=_get_copy_from): + evaluator = retrieval.Evaluator( + lambda p, b: model.apply({"params": p}, + b.get("image", None), + b.get("labels", None)), + dataset="coco_captions", + batch_size=batch_size, + devices=jax.devices(), + txt_name=("captions", "text"), + pp_img="copy_from(image='id')", + pp_txt="copy_from(labels='id')", + ) + results = evaluator.evaluate(params) + + # Assert all examples were processed. + self.assertLen(results["images"]["embeddings"], num_examples) + self.assertLen(results["images"]["id"], num_examples) + # Assert no padding was processed (expects exactly one (=first) image.id=0 + self.assertEqual((results["images"]["id"] == 0).sum(), 1) + # Expect perfect ITR with above _Model()... + self.assertEqual(results["img2txt"]["Recall@1"], 1.0) + self.assertEqual(results["txt2img"]["Recall@5"], 1.0) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/evaluators/proj/paligemma/perplexity.py b/big_vision/evaluators/proj/paligemma/perplexity.py new file mode 100644 index 0000000000000000000000000000000000000000..38b5ed077b698ec922785e8e87b462409ef737d2 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/perplexity.py @@ -0,0 +1,58 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for perplexity of a model.""" +import functools + +from big_vision.evaluators import mean +import big_vision.utils as u +import jax.numpy as jnp + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +# Cache the function such that it won't always recompile (in mean evaluator). +@functools.cache +def perplexity( + predict_fn, key='labels', shift_labels=True): + """Returns a function that computes perplexity.""" + + def _perplexity_fn(train_state, batch, **kw): + logits, _ = predict_fn(train_state, batch, **kw) + + labels = batch[key] + weights = batch.get('mask_loss', jnp.ones_like(labels)) + + if shift_labels: + labels = labels[:, 1:] + weights = weights[:, 1:] + + losses = u.weighted_softmax_xent( + logits=logits, labels=labels, weights=weights, + reduction=False, normalize=False) + normalizer = jnp.clip(weights.sum(axis=1), 2e-38) + + return {'sum': losses, 'avg': losses / normalizer} + return _perplexity_fn + + +class Evaluator(mean.Evaluator): + """Perplexity evaluator.""" + + def __init__(self, predict_fn, *a, key='labels', shift_labels=False, **kw): + kw.setdefault('prefetch', 0) # More memory-saving default. + super().__init__(perplexity(predict_fn, key, shift_labels), *a, **kw) diff --git a/big_vision/evaluators/proj/paligemma/transfers/chartqa.py b/big_vision/evaluators/proj/paligemma/transfers/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..41dadb9e1fb148e83ecf4b09673d3346722c7b96 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/chartqa.py @@ -0,0 +1,139 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for ChartQA variants.""" + +import functools + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """Evaluator for simple VQA tasks.""" + + def __init__( + self, predict_fn, tokenizer, to_lower=False, + outfile="{workdir}/{split}.json", + out_question_key="question_id", out_answer_key="answer", + *, data, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answer", "question_id"}, data=data, devices=devices, **kw) + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + self.out_question_key = out_question_key + self.out_answer_key = out_answer_key + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.postproc = (lambda s: s.lower()) if to_lower else lambda s: s + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies = [] + relaxed_accuracies = [] + json_out = [] + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + answer = self.postproc(self.tok.to_str(tokens[i], stop_at_eos=True)) + + gt = self.postproc(batch["answer"][i]) + accuracies.append(float(answer == gt)) + relaxed_accuracies.append(_relaxed_match(gt, answer)) + json_out.append({ + self.out_question_key: batch["question_id"][i].item(), + self.out_answer_key: answer, + "gt": gt, + "relaxed_match": relaxed_accuracies[-1], + }) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs, sum_relaxed_accs, num = c.process_sum( + [sum(accuracies), sum(relaxed_accuracies), len(accuracies)]) + + # Yielding metric_name, value means logging the metric. + yield "acc", sum_accs / num + yield "relaxed_acc", sum_relaxed_accs / num + yield "num", num # Just for sanity checks. + c.multiprocess_write_json(self.outfile, json_out) + + +def _to_float(text: str) -> float | None: + try: + if text.endswith("%"): + # Convert percentages to floats. + return float(text.rstrip("%")) / 100.0 + else: + return float(text) + except ValueError: + return None + + +def _relaxed_match(target: str, + prediction: str, + max_relative_error: float = 0.05) -> bool: + """Calculates relaxed correctness. + + The correctness tolerates certain error ratio defined by max_relative_error. + See https://arxiv.org/pdf/2203.10244.pdf, end of section 5.1: + “Following Methani et al. (2020), we use a relaxed accuracy measure for the + numeric answers to allow a minor inaccuracy that may result from the automatic + data extraction process. We consider an answer to be correct if it is within + 5% of the gold answer. For non-numeric answers, we still need an exact match + to consider an answer to be correct.” + + Args: + target: Target string. + prediction: Predicted string. + max_relative_error: Maximum relative error. + + Returns: + Whether the prediction was correct given the specified tolerance. + """ + prediction_float = _to_float(prediction) + target_float = _to_float(target) + # When the target is 0 is always required an exact match. + if prediction_float is not None and target_float: + relative_error = abs(prediction_float - target_float) / abs(target_float) + return relative_error <= max_relative_error + else: + return prediction == target diff --git a/big_vision/evaluators/proj/paligemma/transfers/coco_caption.py b/big_vision/evaluators/proj/paligemma/transfers/coco_caption.py new file mode 100644 index 0000000000000000000000000000000000000000..69721ff57dc061432c7e06e2cd3f1c5b7f9a4cde --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/coco_caption.py @@ -0,0 +1,145 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for caption generation metrics used for the MS COCO dataset.""" +import collections +import functools +import os +import tempfile + +import big_vision.evaluators.common as c +import big_vision.input_pipeline +import big_vision.pp.builder +import big_vision.pp.tokenizer +import big_vision.utils as u + +from pycocoevalcap.bleu import bleu +from pycocoevalcap.cider import cider +from pycocoevalcap.meteor import meteor +from pycocoevalcap.rouge import rouge +from pycocoevalcap.spice import spice +from pycocoevalcap.tokenizer import ptbtokenizer + +import jax + +from tensorflow.io import gfile + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """Evaluator for caption generation metrics used for the MS COCO dataset. + + See https://arxiv.org/pdf/1504.00325.pdf or the repository implementing it + https://github.com/tylin/coco-caption for details on the metrics. This code + uses the python3 pip package from: https://github.com/salaniz/pycocoevalcap + + Note that both the model caption and the ground truth reference captions are + further processed with the PTBTokenizer before computing scores. + + `predict_fn` accepts arbitrary dictionaries of parameters and data, where + the data dictionary is produced by the `pp_fn` op. It is expected to output a + dict containing tokenized captions. + + `pp_fn` must have fields: "image/id" and "captions". + """ + + def __init__( + self, predict_fn, tokenizer=None, + metrics=("cider",), # Default to only cider. We often just look at that. + preds_outfile="{workdir}/{name}_{split}_preds.json", + annot_outfile="{workdir}/{name}_{split}_annotations.json", + *, data, devices, **kw + ): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"image/id", "captions"}, data=data, devices=devices, **kw) + + self.preds_outfile = c.resolve_outfile( + preds_outfile, name=data.get("name"), split=data.get("split")) + self.annot_outfile = c.resolve_outfile( + annot_outfile, name=data.get("name"), split=data.get("split")) + + self.metrics = metrics + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Run eval.""" + gts = [] + res = [] + + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + image_ids = batch["image/id"][ex_masks] + pred_captions = self.tok.to_str(tokens[ex_masks]) + + for image_id, caption in zip(image_ids, pred_captions): + res.append({"image_id": image_id.item(), "caption": caption}) + + for image_id, captions in zip(image_ids, batch["captions"]): + for caption in captions: + gts.append({"image_id": image_id.item(), "caption": caption.item()}) + + # Write model outputs following: https://cocodataset.org/#format-results + # Use same format for gt although that is not the usual format for them. + res = c.multiprocess_write_json(self.preds_outfile, res) + gts = c.multiprocess_write_json(self.annot_outfile, gts) + + if jax.process_index(): # Host0 gets all preds and does eval. + return + + outs = self.evaluate(gts, res) + for key, score in outs.items(): + yield key, score + + def evaluate(self, gt_annotations, res_annotations): + """Creates scorers and run evaluation.""" + scorers = { + "rouge": rouge.Rouge, + "cider": cider.Cider, + "bleu-4": bleu.Bleu, + "spice": spice.Spice, + "meteor": meteor.Meteor, + } + + # Reformat gts and res from [{"image_id": int|str, "caption": str}] to + # {int_image_id: [{"caption": str}]} as expected by tokenizer and scorers. + # Note there are multiple reference captions for the ground truth but only + # one for the model predictions. + iid_map = collections.defaultdict(lambda: len(iid_map)) + res = {iid_map[x["image_id"]]: [x] for x in res_annotations} + gts = collections.defaultdict(list) + for x in gt_annotations: + gts[iid_map[x["image_id"]]].append(x) + assert sorted(gts.keys()) == sorted(res.keys()) + + # Tokenize captions and predictions using coco tokenizer. + coco_tokenizer = ptbtokenizer.PTBTokenizer() + gts = coco_tokenizer.tokenize(gts) + res = coco_tokenizer.tokenize(res) + + scores = {} + for metric in self.metrics: + scorer = scorers[metric]() + scores[metric], _ = scorer.compute_score(gts, res) + return scores diff --git a/big_vision/evaluators/proj/paligemma/transfers/pope.py b/big_vision/evaluators/proj/paligemma/transfers/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..7ef4df53f89f5929b644e6b95881df7c77c3cdbb --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/pope.py @@ -0,0 +1,135 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for the POPE dataset (https://github.com/RUCAIBox/POPE). + +POPE is a binary classification dataset with ground-truth answers being either +'yes' or 'no'. +""" + +import functools + +import big_vision.datasets.core +import big_vision.evaluators.common as c +import big_vision.input_pipeline +import big_vision.pp.builder +import big_vision.pp.tokenizer +import big_vision.utils as u + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """Evaluator for the POPE task. + + This evaluator expects the batch to contain a field `question_id` and a field + `answer` for single ground truth or `answers` for multiple ground truths. + + The field names used when writting the json result can be controlled with + `out_question_key` and `out_answer_key`. + """ + + def __init__( + self, + predict_fn, + data, + pp_fn, + tokenizer, + batch_size, + *, + devices, + outfile="{workdir}/{split}.json", + out_question_key="question_id", + out_answer_key="answer" + ): + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + self.out_question_key = out_question_key + self.out_answer_key = out_answer_key + # This will mostly look the same across all evaluators, preparing data: + data = big_vision.datasets.core.get(**data) + pp_fn = big_vision.pp.builder.get_preprocess_fn(pp_fn) + self.ds, self.steps = big_vision.input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), + pp_fn, + batch_size, + num_ex_per_process=data.num_examples_per_process(), + ) + # The `keep_on_cpu=` argument lists the data keys that, if they exist, we + # do NOT want to ship to the TPUs and instead just keep in host memory. + # Typically ground-truth and metadata, that is often of string type. + self.data_iter = big_vision.input_pipeline.start_global( + self.ds, devices, keep_on_cpu={"answer", "question_id"} + ) + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token + ) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies = [] + valid = [] + json_out = [] + for _, batch in zip(range(self.steps), self.data_iter): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + answer = self.tok.to_str(tokens[i], stop_at_eos=True).lower() + gt = batch["answer"][i] + accuracies.append(float(answer == gt)) + valid.append(float(answer in ("yes", "no"))) + + json_out.append( + { + self.out_question_key: batch["question_id"][i].item(), + self.out_answer_key: answer, + } + ) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs, sum_valid, num = c.process_sum([ + sum(accuracies), + sum(valid), + len(accuracies), + ]) + + if num: + yield "acc", sum_accs / num + yield "valid_percent", sum_valid / num + yield "num", num + + c.multiprocess_write_json(self.outfile, json_out) diff --git a/big_vision/evaluators/proj/paligemma/transfers/rsvqa.py b/big_vision/evaluators/proj/paligemma/transfers/rsvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..575bd12d1f53b20b1393bba264f5c38ae08f7c6b --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/rsvqa.py @@ -0,0 +1,173 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for simple VQA variants with per answer-type metrics. + +According to the (A-)OKVAQ papers, the eval for these datasets should follow +VQAv2. But here we don't track different answer-types, and don't do any +leave-one-out averaging, as this isn't done in the official implementation at +https://github.com/allenai/aokvqa/blob/main/evaluation/eval_predictions.py +either. +""" + +import functools + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u +import editdistance + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + +QUESTION_TYPES = ("comp", "count", "presence", "rural_urban", "area") + +ACC_SUBSETS = ( + ("nonum", ("comp", "presence", "rural_urban")), # rsvqa_lr + ("nonum", ("comp", "presence")), # rsvqa_hr +) + + +class Evaluator: + """Evaluator for simple VQA tasks.""" + + def __init__( + self, predict_fn, tokenizer, to_lower=False, + outfile="{workdir}/{split}.json", + *, data, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answers", "answer", "question_id", "question_type"}, + data=data, devices=devices, **kw) + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.postproc = (lambda s: s.lower()) if to_lower else lambda s: s + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies = [] + accuracies_any = [] + counts_per_type = {t: 0 for t in QUESTION_TYPES} + accuracies_per_type = {t: [] for t in QUESTION_TYPES} + anls_values = [] + json_out = [] + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) # (B,L,E) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + answer = self.postproc(self.tok.to_str(tokens[i], stop_at_eos=True)) + + # Now we have two commonly used VQA evaluation modes: + if "answer" in batch: + # single GT (eg ocrvqa): just compare to that answer, done. + gt = self.postproc(batch["answer"][i]) + gts = [gt] + accuracies.append(float(answer == gt)) + accuracies_any.append(float(answer == gt)) + anls_values.append(anls_metric(gt, answer)) + elif "answers" in batch and (gt_answers := batch["answers"][i]).size: + # multiple GTs (eg okvqa): introduced by VQA, compare to each of them + # with a threshold, see also: https://visualqa.org/evaluation.html + gts = [self.postproc(a) for a in gt_answers] + num_match = sum([answer == gt for gt in gts]) + accuracies.append(min(1.0, num_match / 3.0)) + accuracies_any.append(min(1.0, float(num_match))) + anls_values.append(max(anls_metric(gt, answer) for gt in gts)) + accuracies_per_type[batch["question_type"][i]].append( + accuracies_any[-1] + ) + counts_per_type[batch["question_type"][i]] += 1 + else: + gts = [] + + json_out.append({ + "question_id": batch["question_id"][i].item(), + "answer": answer} | ({"gts": gts} if gts else {})) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs, sum_accs_any, sum_anls, num_accs, num = c.process_sum( + [sum(accuracies), sum(accuracies_any), sum(anls_values), + len(accuracies), len(json_out)]) + + sum_accs_per_type, sum_cnts_per_type = c.process_sum( + [{k: sum(v) for k, v in accuracies_per_type.items()}, counts_per_type] + ) + + # Yielding metric_name, value means logging the metric. + if num_accs: + yield "acc", sum_accs / num_accs + yield "acc_any", sum_accs_any / num_accs # Overall Accuracy (OA). + yield "anls", sum_anls / num_accs + acc_types = {} + for k, v in sum_accs_per_type.items(): + if sum_cnts_per_type[k]: + acc_types[k] = v / sum_cnts_per_type[k] + yield f"acc_{k}", acc_types[k] + yield "acc_avg", sum(acc_types.values()) / len(acc_types) # Avg acc (AA). + for postfix, types in ACC_SUBSETS: + if all(t in acc_types for t in types): + yield f"acc_avg_{postfix}", sum( + [v for k, v in acc_types.items() if k in types] + ) / len(types) # Average accuracy per question types subset. + yield "num", num # Just for sanity checks. + c.multiprocess_write_json(self.outfile, json_out) + + +def anls_metric(target: str, prediction: str, theta: float = 0.5): + """Calculates ANLS for DocVQA. + + There does not seem to be an official evaluation script. + Public implementation on which this implementation is based: + https://github.com/herobd/layoutlmv2/blob/main/eval_docvqa.py#L92 + + Original paper (see Eq 1): https://arxiv.org/pdf/1907.00490.pdf + + Args: + target: Target string. + prediction: Predicted string. + theta: Filter threshold set to 0.5 for DocVQA. + + Returns: + ANLS score. + """ + if target: + edit_distance = editdistance.eval(target, prediction) + normalized_ld = edit_distance / max(len(target), len(prediction)) + return 1 - normalized_ld if normalized_ld < theta else 0 + else: + return float(prediction == "") diff --git a/big_vision/evaluators/proj/paligemma/transfers/science_qa.py b/big_vision/evaluators/proj/paligemma/transfers/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..cbf61fe3785e631231f016f9b007ff572dca3c2d --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/science_qa.py @@ -0,0 +1,122 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for ScienceQA. + +based on the official implementation at +https://github.com/lupantech/ScienceQA/blob/main/models/run_gpt3.py +""" + +import functools +import re + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" +FAILURE = "failed" + + +class Evaluator: + """Evaluator for simple VQA tasks.""" + + def __init__( + self, predict_fn, tokenizer, + outfile="{workdir}/{split}.json", + out_question_key="question_id", + *, data, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answer", "question_id"}, data=data, devices=devices, **kw) + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + self.out_question_key = out_question_key + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token + ) + + def postproc(self, raw_answer): + """Post-processes the raw answer. extract a, b, c from the string.""" + match = re.match( + pattern=r"the answer is ([a-z])\.", string=raw_answer.lower() + ) + if match: + return match.groups()[0] # 'a', 'b', ... + else: + return FAILURE + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies = [] + fail_parse = [] + json_out = [] + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + raw_answer = self.tok.to_str(tokens[i], stop_at_eos=True) + answer = self.postproc(raw_answer) + if "answer" in batch: + gt = self.postproc(batch["answer"][i]) + gts = [gt] + accuracies.append(float(answer == gt)) + fail_parse.append(float(answer == FAILURE)) + else: + gts = [] + + json_out.append( + { + self.out_question_key: batch["question_id"][i].item(), + "raw_answer": raw_answer, + "answer": answer, + } + | ({"gts": gts} if gts else {}) + ) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs, num_parsefail, num_accs, num = c.process_sum( + [sum(accuracies), sum(fail_parse), len(accuracies), len(json_out)] + ) + + # Yielding metric_name, value means logging the metric. + if num_accs > 0: + yield "acc", sum_accs / num_accs + yield "parsefail", num_parsefail / num_accs + + yield "num", num # Just for sanity checks. + c.multiprocess_write_json(self.outfile, json_out) diff --git a/big_vision/evaluators/proj/paligemma/transfers/segmentation.py b/big_vision/evaluators/proj/paligemma/transfers/segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..bb4d1a004559824dba1438c561b4dcb657323537 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/segmentation.py @@ -0,0 +1,268 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for segmentation.""" + +import functools + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np +import PIL.Image + +from tensorflow.io import gfile + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +def _inrange(a, min_value, max_value): + return (np.clip(a, min_value, max_value) == a).all() + + +def _area(y1, x1, y2, x2): + return max(x2 - x1, 0.0) * max(y2 - y1, 0.0) + + +class Evaluator: + """Evaluator for instance segmentation.""" + + def __init__(self, predict_fn, tokenizer, + model='oi', det_ious=(0.5, 0.75), + *, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={'prefix', 'suffix', 'objects/mask', 'objects/bbox'}, + devices=devices, **kw) + + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.loc0 = np.array(tok.to_int('')) + self.seg0 = np.array(tok.to_int('')) + # Verify tokenizer has `tokensets=("loc", "seg")` + assert self.loc0.shape == (1,), self.loc0 + assert self.seg0.shape == (1,), self.seg0 + self.reconstruct_masks = get_reconstruct_masks(model) + self.det_ious = det_ious + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + ious = [] # NOTE: no point to split in s/m/l: all objects are L (>96px²) + det_by_iou = {iou: [] for iou in self.det_ious} + invalid = total = 0 + for _, batch in zip(range(self.steps), self.get_data_iter()): + + decoded = self.decode(train_state, batch) + + not_padding = u.get_local_slice_from_fsarray(batch['_mask']) + decoded = u.get_local_slice_from_fsarray(decoded)[not_padding] + + # Note, gt masks are in full original image resolution. + gt_masks = [gt[:, :, 0] > 0 for gt in batch['objects/mask'][not_padding]] + gt_bbs = [gt for gt in batch['objects/bbox'][not_padding]] + + valid = [] + tokens = np.zeros([decoded.shape[0], 4 + 16], np.int32) + for i, dec in enumerate(decoded): + # TODO: b/andstein - do we need to optimize this loop? + t = np.r_[dec[:4] - self.loc0, dec[4:4 + 16] - self.seg0] # Ignore rest + if ( + len(t) == 4 + 16 # Full prediction + and _inrange(t[:4], 0, 1023) # Valid box tokens + and _inrange(t[4:], 0, 127) # Valid seg tokens + and t[2] > t[0] and t[3] > t[1] # Valid box + ): + valid.append(True) + tokens[i] = t + else: + valid.append(False) + + tocpu = lambda x: jax.device_put(x, jax.local_devices(backend='cpu')[0]) + seg_indices = np.array(tokens[:, 4:]) + mask64 = jax.device_get(self.reconstruct_masks(tocpu(seg_indices))) + mask64 = mask64[..., 0] + bbox = tokens[:, :4] / 1023 # Back to [0.0 ... 1.0] + + for v, m64, gtm, bb, gtbb in zip(valid, mask64, gt_masks, bbox, gt_bbs): + # TODO: b/andstein - do we need to optimize this loop? + total += 1 + h, w = gtm.shape # gt is full/original image resolution mask. + + # First, compute detection iou, in [0.0 ... 1.0] coordinate space. + y1, x1, y2, x2 = bb + gty1, gtx1, gty2, gtx2 = gtbb + ibb = max(y1, gty1), max(x1, gtx1), min(y2, gty2), min(x2, gtx2) + box_iou = _area(*ibb) / (_area(*bb) + _area(*gtbb) - _area(*ibb)) + for iou_thresh in det_by_iou: + det_by_iou[iou_thresh].append(iou_thresh <= box_iou) + + # Next, we convert to pixel coordinates and compute mask iou. + gt_area = gtm.sum() + y1, x1, y2, x2 = map(int, (y1 * h, x1 * w, y2 * h, x2 * w)) + + # Avoid compute-intensive mask stuff for invalid preds: + if not v or x2 <= x1 or y2 <= y1: # Can still happen after int(). + iou = 0.0 + invalid += 1 + else: + mi = np.asarray(PIL.Image.fromarray(m64).resize( + [x2 - x1, y2 - y1], resample=PIL.Image.BILINEAR # pytype: disable=module-attr + )) # Predicted mask in box-sized image. + mi = mi > 0.0 # Mask decoder output in [-1.0 ... 1.0] + iarea = (gtm[y1:y2, x1:x2] & mi).sum() # Intersection pixels. + iou = iarea / (gt_area + mi.sum() - iarea) + ious.append(iou) + + # Done going over all batches, now collect results from all processes. + sum_ious, num_ious, sum_dets, num_dets, num_invalid, num = c.process_sum([ + sum(ious), len(ious), + {k: sum(v) for k, v in det_by_iou.items()}, + {k: len(v) for k, v in det_by_iou.items()}, + invalid, total + ]) + + yield 'miou', sum_ious / num_ious + for k in sum_dets: + yield f'boxacc/{k}', sum_dets[k] / num_dets[k] + yield 'invalid', num_invalid + yield 'total', num + + +_KNOWN_MODELS = { + # Trained on open images. + 'oi': 'gs://big_vision/paligemma/vae-oid.npz', +} + + +def _get_params(checkpoint): + """Converts PyTorch checkpoint to Flax params.""" + + def transp(kernel): + return np.transpose(kernel, (2, 3, 1, 0)) + + def conv(name): + return { + 'bias': checkpoint[name + '.bias'], + 'kernel': transp(checkpoint[name + '.weight']), + } + + def resblock(name): + return { + 'Conv_0': conv(name + '.0'), + 'Conv_1': conv(name + '.2'), + 'Conv_2': conv(name + '.4'), + } + + return { + '_embeddings': checkpoint['_vq_vae._embedding'], + 'Conv_0': conv('decoder.0'), + 'ResBlock_0': resblock('decoder.2.net'), + 'ResBlock_1': resblock('decoder.3.net'), + 'ConvTranspose_0': conv('decoder.4'), + 'ConvTranspose_1': conv('decoder.6'), + 'ConvTranspose_2': conv('decoder.8'), + 'ConvTranspose_3': conv('decoder.10'), + 'Conv_1': conv('decoder.12'), + } + + +def _quantized_values_from_codebook_indices(codebook_indices, embeddings): + batch_size, num_tokens = codebook_indices.shape + assert num_tokens == 16, codebook_indices.shape + unused_num_embeddings, embedding_dim = embeddings.shape + + encodings = jnp.take(embeddings, codebook_indices.reshape((-1)), axis=0) + encodings = encodings.reshape((batch_size, 4, 4, embedding_dim)) + return encodings + + +class ResBlock(nn.Module): + features: int + + @nn.compact + def __call__(self, x): + original_x = x + x = nn.Conv(features=self.features, kernel_size=(3, 3), padding=1)(x) + x = nn.relu(x) + x = nn.Conv(features=self.features, kernel_size=(3, 3), padding=1)(x) + x = nn.relu(x) + x = nn.Conv(features=self.features, kernel_size=(1, 1), padding=0)(x) + return x + original_x + + +class Decoder(nn.Module): + """Upscales quantized vectors to mask.""" + + @nn.compact + def __call__(self, x): + num_res_blocks = 2 + dim = 128 + num_upsample_layers = 4 + + x = nn.Conv(features=dim, kernel_size=(1, 1), padding=0)(x) + x = nn.relu(x) + + for _ in range(num_res_blocks): + x = ResBlock(features=dim)(x) + + for _ in range(num_upsample_layers): + x = nn.ConvTranspose( + features=dim, + kernel_size=(4, 4), + strides=(2, 2), + padding=2, + transpose_kernel=True, + )(x) + x = nn.relu(x) + dim //= 2 + + x = nn.Conv(features=1, kernel_size=(1, 1), padding=0)(x) + + return x + + +@functools.cache +def get_reconstruct_masks(model): + """Reconstructs masks from codebook indices. + + Based on code from https://arxiv.org/abs/2301.02229 + + Verified in + https://colab.research.google.com/drive/1AOr0cokOpM6-N9Z5HmxoeGxGj6jS37Vl + + Args: + model: Model to use for conversion. + + Returns: + A function that expects indices shaped `[B, 16]` of dtype int32, each + ranging from 0 to 127 (inclusive), and that returns a decoded masks sized + `[B, 64, 64, 1]`, of dtype float32, in range [-1, 1]. + """ + def reconstruct_masks(codebook_indices): + quantized = _quantized_values_from_codebook_indices( + codebook_indices, params['_embeddings'] + ) + return Decoder().apply({'params': params}, quantized) + + with gfile.GFile(_KNOWN_MODELS.get(model, model), 'rb') as f: + params = _get_params(dict(np.load(f))) + + return jax.jit(reconstruct_masks, backend='cpu') diff --git a/big_vision/evaluators/proj/paligemma/transfers/storepreds.py b/big_vision/evaluators/proj/paligemma/transfers/storepreds.py new file mode 100644 index 0000000000000000000000000000000000000000..ee7314754a00c49dc9d7a5583c860607530e07f0 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/storepreds.py @@ -0,0 +1,77 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator to run inference and store results.""" +import functools + +import big_vision.evaluators.common as c +import big_vision.input_pipeline +import big_vision.pp.builder +import big_vision.pp.tokenizer +import big_vision.utils as u + +import jax + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """Evaluator to run inference and store results.""" + + def __init__( + self, predict_fn, tokenizer=None, + preds_outfile="{workdir}/{name}_{split}_preds.json", + annot_outfile="{workdir}/{name}_{split}_annotations.json", + id_key="id", + *, data, devices, **kw + ): + self.id_key = id_key + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={id_key}, data=data, devices=devices, **kw) + + self.preds_outfile = c.resolve_outfile( + preds_outfile, name=data.get("name"), split=data.get("split", "")) + self.annot_outfile = c.resolve_outfile( + annot_outfile, name=data.get("name"), split=data.get("split", "")) + + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Run eval.""" + res = [] + + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + image_ids = batch[self.id_key][ex_masks] + pred_captions = self.tok.to_str(tokens[ex_masks]) + + for image_id, caption in zip(image_ids, pred_captions): + res.append({self.id_key: str(image_id), "caption": caption}) + + res = c.multiprocess_write_json(self.preds_outfile, res) + + if jax.process_index(): # Host0 gets all preds and does eval. + return + + yield "num_examples", len(res) diff --git a/big_vision/evaluators/proj/paligemma/transfers/tallyqa.py b/big_vision/evaluators/proj/paligemma/transfers/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..b2f219a5737ece149798004f0cf3ba6100f75385 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/tallyqa.py @@ -0,0 +1,144 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for TallyQA dataset.""" + +import functools + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +# Largest count we want to track. +_LARGEST_COUNT = 15 + + +class Evaluator: + """TallyQA evaluator.""" + + def __init__(self, predict_fn, tokenizer, *, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answer", "issimple"}, devices=devices, **kw) + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token + ) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies_by_type = {"all": [], "simple": [], "complex": []} + # Add per-count entries. Cannot use a `defaultdict` as we need to `tree_map` + # over keys later in `c.process_sum`. + accuracies_by_type.update( + {f"count_{i}": [] for i in range(_LARGEST_COUNT + 1)} + ) + + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded (generated) token sequences suffixes. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + # We always compare the gt (string digit, e.g. "1") to the answer by the + # model (e.g. "1"). + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + # Extract the suffix/answer from the generated string, skip bos. + answer = self.tok.to_str(tokens[i], stop_at_eos=True) + # Standardize the reponse, i.e., convert number words ("one") to + # numerals ("1"). + answer = _number_word_to_numeral(answer) + + # Always need to do light space-processing: + gt = _number_word_to_numeral(batch["answer"][i]) + accuracies_by_type["all"].append(float(answer == gt)) + + if "issimple" in batch: + # Simple/complex split. + if batch["issimple"][i] == 1: + accuracies_by_type["simple"].append(float(answer == gt)) + elif batch["issimple"][i] == 0: + accuracies_by_type["complex"].append(float(answer == gt)) + else: + # Train set is not annotated with simple/complex (but has dummy + # value of `-1` in this field). + pass + + # Store accuracies per count. + accuracies_by_type[f"count_{gt}"].append(float(answer == gt)) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs = c.process_sum({k: sum(v) for k, v in accuracies_by_type.items()}) + num_accs = c.process_sum({k: len(v) for k, v in accuracies_by_type.items()}) + + if n := num_accs["all"]: + yield "acc", sum_accs["all"] / n + yield "num", n # Just for sanity checks. + for key in sum_accs.keys(): + if (key != "all") and (num_accs[key]): + yield f"acc/{key}", sum_accs[key] / num_accs[key] + yield f"num/{key}", num_accs[key] # Just for sanity checks. + + +def _number_word_to_numeral(s: str) -> str: + """Returns numeral for a given number word, e.g., "one" -> "1" (up to 20).""" + return REPLACEMENTS.get(s.lower(), s) + + +REPLACEMENTS = { + "none": "0", + "zero": "0", + "one": "1", + "two": "2", + "three": "3", + "four": "4", + "five": "5", + "six": "6", + "seven": "7", + "eight": "8", + "nine": "9", + "ten": "10", + "eleven": "11", + "twelve": "12", + "thirteen": "13", + "fourteen": "14", + "fifteen": "15", + "sixteen": "16", + "seventeen": "17", + "eighteen": "18", + "nineteen": "19", + "twenty": "20", +} diff --git a/big_vision/evaluators/proj/paligemma/transfers/vqa.py b/big_vision/evaluators/proj/paligemma/transfers/vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9f6ddccaf3640306ec9bbd9fe62e3a34ca78b78b --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/vqa.py @@ -0,0 +1,163 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for simple VQA variants (OCR-VQA, OKVQA, A-OKVQA). + +According to the (A-)OKVAQ papers, the eval for these datasets should follow +VQAv2. But here we don't track different answer-types, and don't do any +leave-one-out averaging, as this isn't done in the official implementation at +https://github.com/allenai/aokvqa/blob/main/evaluation/eval_predictions.py +either. + +Please read the description of how evaluators work at (internal link). +This evaluator follows the pattern of also parallelizing the CPU computations +(ie postprocessing, score computation) across hosts for more scalability. + +For now, simple decoding is implemented as part of the evaluator. We'll soon +unify and move to a library of decoding functions, including fancier and more +efficient ones. +""" +import functools + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u +import editdistance + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """Evaluator for simple VQA tasks. + + This evaluator expects the batch to contain a field `question_id` and a field + `answer` for single ground truth or `answers` for multiple ground truths. + + The field names used when writting the json result can be controlled with + `out_question_key` and `out_answer_key`. + """ + + def __init__( + self, predict_fn, tokenizer, to_lower=False, + outfile="{workdir}/{split}.json", + out_question_key="question_id", out_answer_key="answer", + *, data, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answers", "answer", "question_id"}, + data=data, devices=devices, **kw) + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + self.out_question_key = out_question_key + self.out_answer_key = out_answer_key + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.postproc = (lambda s: s.lower()) if to_lower else lambda s: s + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + + accuracies = [] + accuracies_any = [] + anls_values = [] + json_out = [] + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded generated tokens. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + answer = self.postproc(self.tok.to_str(tokens[i], stop_at_eos=True)) + + # Now we have two commonly used VQA evaluation modes: + if "answer" in batch: + # single GT (eg ocrvqa): just compare to that answer, done. + gt = self.postproc(batch["answer"][i]) + gts = [gt] + accuracies.append(float(answer == gt)) + accuracies_any.append(float(answer == gt)) + anls_values.append(anls_metric(gt, answer)) + elif "answers" in batch and (gt_answers := batch["answers"][i]).size: + # multiple GTs (eg okvqa): introduced by VQA, compare to each of them + # with a threshold, see also: https://visualqa.org/evaluation.html + gts = [self.postproc(a) for a in gt_answers] + num_match = sum([answer == gt for gt in gts]) + accuracies.append(min(1.0, num_match / 3.0)) + accuracies_any.append(min(1.0, float(num_match))) + anls_values.append(max(anls_metric(gt, answer) for gt in gts)) + else: + gts = [] + + json_out.append({ + self.out_question_key: batch["question_id"][i].item(), + self.out_answer_key: answer} | ({"gts": gts} if gts else {})) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs, sum_accs_any, sum_anls, num_accs, num = c.process_sum( + [sum(accuracies), sum(accuracies_any), sum(anls_values), + len(accuracies), len(json_out)]) + + # Yielding metric_name, value means logging the metric. + if num_accs: + yield "acc", sum_accs / num_accs + yield "acc_any", sum_accs_any / num_accs + yield "anls", sum_anls / num_accs + + yield "num", num # Just for sanity checks. + c.multiprocess_write_json(self.outfile, json_out) + + +def anls_metric(target: str, prediction: str, theta: float = 0.5): + """Calculates ANLS for DocVQA. + + There does not seem to be an official evaluation script. + Public implementation on which this implementation is based: + https://github.com/herobd/layoutlmv2/blob/main/eval_docvqa.py#L92 + + Original paper (see Eq 1): https://arxiv.org/pdf/1907.00490.pdf + + Args: + target: Target string. + prediction: Predicted string. + theta: Filter threshold set to 0.5 for DocVQA. + + Returns: + ANLS score. + """ + if target: + edit_distance = editdistance.eval(target, prediction) + normalized_ld = edit_distance / max(len(target), len(prediction)) + return 1 - normalized_ld if normalized_ld < theta else 0 + else: + return float(prediction == "") diff --git a/big_vision/evaluators/proj/paligemma/transfers/vqav2.py b/big_vision/evaluators/proj/paligemma/transfers/vqav2.py new file mode 100644 index 0000000000000000000000000000000000000000..5f6f2a1839a6f9644674ca20de880edce22ddd11 --- /dev/null +++ b/big_vision/evaluators/proj/paligemma/transfers/vqav2.py @@ -0,0 +1,197 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator for VQAV2 dataset. +""" +import functools +import re + +import big_vision.evaluators.common as c +import big_vision.pp.tokenizer +import big_vision.utils as u +import numpy as np + + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = "jit" + + +class Evaluator: + """VQAv2 evaluator.""" + + def __init__( + self, predict_fn, tokenizer, outfile="{workdir}/{split}.json", + *, data, devices, **kw): + self.get_data_iter, self.steps = c.eval_input_pipeline( + keep_on_cpu={"answers", "answer_type", "question_type", "question_id"}, + data=data, devices=devices, **kw) + + self.outfile = c.resolve_outfile(outfile, split=data.get("split")) + + # We'll need the tokenizer to detokenize the model outputs later. + self.tok = big_vision.pp.tokenizer.get_tokenizer(tokenizer) + self.decode = functools.partial( + predict_fn, devices=devices, eos_token=self.tok.eos_token) + + def run(self, train_state): + """Does one evaluation run, yields metrics.""" + accuracies_by_type = {"yes/no": [], "number": [], "other": []} + json_out = [] + + for _, batch in zip(range(self.steps), self.get_data_iter()): + # (batch, seqlen) array of decoded (generated) token sequences suffixes. + tokens = self.decode(train_state, batch) + + # (local_batch,) that indicates padding examples (0) vs real examples (1). + tokens = u.get_local_slice_from_fsarray(tokens) + ex_masks = u.get_local_slice_from_fsarray(batch["_mask"]) + + # Turn predictions into texts and then scores, one by one. + for i in range(len(tokens)): + if ex_masks[i] == 0: # Skip last-batch padding examples + continue + + # Extract the suffix/answer from the generated string, skip bos. + answer = self.tok.to_str(tokens[i], stop_at_eos=True) + json = {"question_id": batch["question_id"][i].item(), "answer": answer} + + # The rest is computation of VQA-score which compares to multiple GTs. + # This is described better here: https://visualqa.org/evaluation.html + if (gt_answers := batch["answers"][i]).size: + # Always need to do light space-processing: + gt_answers = [stripspace_vqav2(a) for a in gt_answers] + answer = stripspace_vqav2(answer) + + # Only post-process if not all agree. Supposedly avoids postproc OCR: + # https://github.com/GT-Vision-Lab/VQA/issues/14#issuecomment-1334695361 + if len(set(gt_answers)) > 1: + answer = postprocess_vqav2_text(answer) + gt_answers = [postprocess_vqav2_text(a) for a in gt_answers] + + # Accuracy is avg over all ten leave-one-out GT's. + # https://github.com/GT-Vision-Lab/VQA/issues/1#issuecomment-199921352 + # An answer is counted 100% correct as soon as 3 GT's agree with it. + matches = answer == np.array(gt_answers) + acc = np.mean([ + np.clip(np.sum(np.delete(matches, i_leave_out)) / 3, 0, 1) + for i_leave_out in range(10) + ]) + + accuracies_by_type[batch["answer_type"][i]].append(acc) + + # Update json with fully post-processed answer and gt: + json["answer_raw"] = json["answer"] + json["answer"] = answer + json["gts"] = gt_answers + + json_out.append(json) + + # At this point `accuracies` is a list of per-example scores. However, + # remember that each host holds a different subset of the examples! So if + # we were to just return the mean accuracy here, we would effectively only + # have evaluated on the main host's (who writes metrics) subset! + # So now, we need to compute global means. + # There is one more caveat: `process_sum` needs the summands on each host + # to have the same size. So we either need to include dummy values for + # the padding examples (last batch, annoying), or we only sum scalars as in + # sufficient statistics, which we do here. + sum_accs = c.process_sum({k: sum(v) for k, v in accuracies_by_type.items()}) + num_accs = c.process_sum({k: len(v) for k, v in accuracies_by_type.items()}) + num = c.process_sum(len(json_out)) + + # Yielding metric_name, value means logging the metric. + if n := sum(num_accs.values()): + yield "acc", sum(sum_accs.values()) / n + if n := num_accs["yes/no"]: + yield "acc/yesno", sum_accs["yes/no"] / n + yield "num/yesno", n + if n := num_accs["number"]: + yield "acc/number", sum_accs["number"] / n + yield "num/number", n + if n := num_accs["other"]: + yield "acc/other", sum_accs["other"] / n + yield "num/other", n + + yield "num", num # Just for sanity checks. + c.multiprocess_write_json(self.outfile, json_out) + + +# Post-processing required is described at https://visualqa.org/evaluation.html + + +def stripspace_vqav2(txt): + return txt.replace("\n", " ").replace("\t", " ").strip() + + +def postprocess_vqav2_text(txt): + """Cleanup string according to VQA.""" + has_digit_comma = re.search(r"(\d)(\,)(\d)", txt) is not None + + out = txt + for p in PUNCT: + # NOTE: digit_comma here looks like a bug in official code, so we follow it. + if has_digit_comma or f"{p} " in txt or f" {p}" in txt: + out = out.replace(p, "") + else: + out = out.replace(p, " ") + + # Remove full-stops that aren't part of a number. + out = re.sub(r"(?!<=\d)(\.)(?!\d)", "", out, flags=re.UNICODE) + + words = [] + for word in out.lower().split(): + if word not in ARTICLES: + words.append(REPLACEMENTS.get(word, word)) + return " ".join(words) + + +# pylint: disable=line-too-long +REPLACEMENTS = { + # CONTRACTIONS + "aint": "ain't", "arent": "aren't", "cant": "can't", "couldve": "could've", "couldnt": "couldn't", + "couldn'tve": "couldn't've", "couldnt've": "couldn't've", "didnt": "didn't", "doesnt": "doesn't", "dont": "don't", "hadnt": "hadn't", + "hadnt've": "hadn't've", "hadn'tve": "hadn't've", "hasnt": "hasn't", "havent": "haven't", "hed": "he'd", "hed've": "he'd've", + "he'dve": "he'd've", "hes": "he's", "howd": "how'd", "howll": "how'll", "hows": "how's", "Id've": "I'd've", "I'dve": "I'd've", + "Im": "I'm", "Ive": "I've", "isnt": "isn't", "itd": "it'd", "itd've": "it'd've", "it'dve": "it'd've", "itll": "it'll", "let's": "let's", + "maam": "ma'am", "mightnt": "mightn't", "mightnt've": "mightn't've", "mightn'tve": "mightn't've", "mightve": "might've", + "mustnt": "mustn't", "mustve": "must've", "neednt": "needn't", "notve": "not've", "oclock": "o'clock", "oughtnt": "oughtn't", + "ow's'at": "'ow's'at", "'ows'at": "'ow's'at", "'ow'sat": "'ow's'at", "shant": "shan't", "shed've": "she'd've", "she'dve": "she'd've", + "she's": "she's", "shouldve": "should've", "shouldnt": "shouldn't", "shouldnt've": "shouldn't've", "shouldn'tve": "shouldn't've", + "somebody'd": "somebodyd", "somebodyd've": "somebody'd've", "somebody'dve": "somebody'd've", "somebodyll": "somebody'll", + "somebodys": "somebody's", "someoned": "someone'd", "someoned've": "someone'd've", "someone'dve": "someone'd've", + "someonell": "someone'll", "someones": "someone's", "somethingd": "something'd", "somethingd've": "something'd've", + "something'dve": "something'd've", "somethingll": "something'll", "thats": "that's", "thered": "there'd", "thered've": "there'd've", + "there'dve": "there'd've", "therere": "there're", "theres": "there's", "theyd": "they'd", "theyd've": "they'd've", + "they'dve": "they'd've", "theyll": "they'll", "theyre": "they're", "theyve": "they've", "twas": "'twas", "wasnt": "wasn't", + "wed've": "we'd've", "we'dve": "we'd've", "weve": "we've", "werent": "weren't", "whatll": "what'll", "whatre": "what're", + "whats": "what's", "whatve": "what've", "whens": "when's", "whered": "where'd", "wheres": "where's", "whereve": "where've", + "whod": "who'd", "whod've": "who'd've", "who'dve": "who'd've", "wholl": "who'll", "whos": "who's", "whove": "who've", "whyll": "why'll", + "whyre": "why're", "whys": "why's", "wont": "won't", "wouldve": "would've", "wouldnt": "wouldn't", "wouldnt've": "wouldn't've", + "wouldn'tve": "wouldn't've", "yall": "y'all", "yall'll": "y'all'll", "y'allll": "y'all'll", "yall'd've": "y'all'd've", + "y'alld've": "y'all'd've", "y'all'dve": "y'all'd've", "youd": "you'd", "youd've": "you'd've", "you'dve": "you'd've", + "youll": "you'll", "youre": "you're", "youve": "you've", + # NUMBERS + "none": "0", "zero": "0", "one": "1", "two": "2", + "three": "3", "four": "4", "five": "5", "six": "6", + "seven": "7", "eight": "8", "nine": "9", "ten": "10", +} +# pylint: enable=line-too-long + +PUNCT = [ + ";", "/", "[", "]", "\"", "{", "}", + "(", ")", "=", "+", "\\", "_", "-", + ">", "<", "@", "`", ",", "?", "!" +] +ARTICLES = {"a", "an", "the"} diff --git a/big_vision/evaluators/proj/uvim/coco_panoptic.py b/big_vision/evaluators/proj/uvim/coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..b20d4458826dfe568b5a8f85246e1635c91a3f3b --- /dev/null +++ b/big_vision/evaluators/proj/uvim/coco_panoptic.py @@ -0,0 +1,324 @@ +# Copyright 2022 Big Vision 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. + +"""COCO17 panoptic evaluation.""" +import functools +from functools import partial +import json +import os +import tempfile +import time +import zipfile + +from absl import logging +from big_vision.evaluators.proj.uvim import common +import big_vision.pp.builder as pp_builder +import jax +import numpy as np +import panopticapi_converters.twochannels2panoptic_coco_format as converter +from panopticapi.evaluation import pq_compute +import tensorflow as tf +import tensorflow_datasets as tfds + +from tensorflow.io import gfile + + +ROOT = os.environ.get('COCO_DATA_DIR', '.') + +PANOPTIC_COCO_CATS_FILE = f'{ROOT}/panoptic_coco_categories.json' +PANOPTIC_2017 = { + 'train': f'{ROOT}/panoptic_train2017.json', + 'validation': f'{ROOT}/panoptic_val2017.json', +} + +PANOPTIC_GT_ZIP = { + 'train': f'{ROOT}/panoptic_train2017.zip', + 'validation': f'{ROOT}/panoptic_val2017.zip', +} + + +class Evaluator: + """Panoptic segmentation evaluator: calls official COCO API. + + `predict_fn` accepts arbitrary dictionaries of parameters and data, where + the data dictionary is produced by the `pp` op. It is expected to output a + 2-channel mask, where the first channel encodes semantics, and the second + channel encodes instance ids. + """ + + def __init__(self, + predict_fn, + pp_fn, + batch_size, + dataset='coco/2017_panoptic', + dataset_dir=None, + split='validation', + predict_kwargs=None): + # Prepare to run predict on all processes and gather predictions on all + # devices. Note: if needed consider only gather across processes. + def predict(params, batch): + res = { + 'image/id': batch['image/id'], + 'mask': batch['mask'], + 'y': predict_fn(params, batch['input'], **(predict_kwargs or {})), + } + return jax.lax.all_gather(res, axis_name='data', axis=0) + + self.predict_fn = jax.pmap(predict, axis_name='data') + + # Prepare data for each process and pad with zeros so all processes have the + # same number of batches. + def preprocess(example): + return { + 'image/id': example['image/id'], + 'mask': tf.constant(1), + 'input': pp_builder.get_preprocess_fn(pp_fn)(example), + } + + self.data = common.get_jax_process_dataset( + dataset, split, dataset_dir=dataset_dir, + global_batch_size=batch_size, + pp_fn=preprocess) + + # Only process 0 runs conversion to png and calls into coco api. + if jax.process_index() == 0: + self.result_dir = tempfile.TemporaryDirectory() + (self.gt_folder, self.gt_json, self.categories_json, + self.remap, self.size_map) = _prepare_ground_truth( + dataset, split, dataset_dir) + + def _compute_png_predictions(self, params): + """Computes predictions and converts then to png to optimize memory use.""" + count = 0 + logging.info('Panoptic eval: running inference.') + for batch in self.data.as_numpy_iterator(): + out = self.predict_fn(params, batch) + + if jax.process_index(): + continue + + out = jax.device_get(jax.tree_map(lambda x: x[0], out)) + mask = out['mask'] + pan_recs = out['y'][mask != 0] + ids = out['image/id'][mask != 0] + + for pan_rec, image_id in zip(pan_recs, ids): + sem = pan_rec[..., 0] + ins = pan_rec[..., 1] + + sem_remapped = np.array(sem) + for v in np.unique(sem): + sem_remapped[sem == v] = self.remap[v] + sem = sem_remapped + + pan_mask = np.stack([sem, ins, np.zeros_like(sem)], axis=-1) + pan_mask = _resize_nearest(pan_mask, self.size_map[image_id]) + pan_mask_png = tf.io.encode_png(pan_mask.astype('uint8')).numpy() + + fname = f'{self.result_dir.name}/{image_id:012d}.png' + with open(fname, 'wb') as f: + f.write(pan_mask_png) + count += 1 + + logging.log_every_n_seconds( + logging.INFO, 'Panoptic eval: processed %i examples so far.', 30, + count) + + if jax.process_index(): + return None + + logging.info('Panoptic eval: inference done. Processed %d examples.', count) + return self.result_dir + + def run(self, params): + """Run eval.""" + # Note result_dir is constant, but files inside are mutated. + result_dir = self._compute_png_predictions(params) + + if not result_dir: + return + + with tempfile.TemporaryDirectory() as pred_folder, \ + tempfile.NamedTemporaryFile(mode='w') as pred_json: + + logging.info('Panoptic eval: running conversion.') + converter.converter( + source_folder=result_dir.name, + images_json_file=self.gt_json, + categories_json_file=self.categories_json, + segmentations_folder=pred_folder, + predictions_json_file=pred_json.name) + logging.info('Panoptic eval: conversion done.') + + logging.info('Panoptic eval: running metrics computation.') + res = pq_compute(gt_json_file=self.gt_json, + gt_folder=self.gt_folder, + pred_json_file=pred_json.name, + pred_folder=pred_folder) + logging.info('Panoptic eval: metrics computation done.') + + for k in ['All', 'Stuff', 'Things']: + for m in ['pq', 'rq', 'sq']: + yield f'{k}_{m}', res[k][m] + + +def _prepare_ground_truth(dataset, split, data_dir): + """Prepare ground truth from tf.data.Dataset.""" + if dataset == 'coco/2017_panoptic' and data_dir is None: + return _prepare_ground_truth_from_zipfiles(split) + else: + return _prepare_ground_truth_from_dataset(dataset, split, data_dir) + + +@functools.lru_cache(maxsize=None) +def _prepare_ground_truth_from_dataset(dataset, split, data_dir): + """Prepare ground truth from a tf.data.Dataset.""" + dataset = tfds.builder(dataset, data_dir=data_dir).as_dataset(split=split) + + categories_json = _make_local_copy(PANOPTIC_COCO_CATS_FILE) + with gfile.GFile(categories_json, 'rb') as f: + categories = json.loads(f.read()) + + # Build map from tfds class ids to COCO class ids. + remap = {0: 0} + with gfile.GFile(categories_json, 'r') as f: + remap = {**remap, **{(i + 1): x['id'] for i, x in enumerate(categories)}} + + gt_folder = tempfile.mkdtemp() + gfile.makedirs(gt_folder) + size_map = {} + annotations = [] + images = [] + for example in dataset: + image_id = int(example['image/id']) + panoptic_image = example['panoptic_image'] + ann_ids = example['panoptic_objects']['id'] + ann_labels = example['panoptic_objects']['label'] + ann_iscrowd = example['panoptic_objects']['is_crowd'] + ann_area = example['panoptic_objects']['area'] + + fname = f'{image_id:012d}.png' + with gfile.GFile(os.path.join(gt_folder, fname), 'wb') as f: + f.write(tf.io.encode_png(panoptic_image).numpy()) + + size_map[image_id] = (panoptic_image.shape[0], panoptic_image.shape[1]) + + segments_info = [] + for i in range(len(ann_ids)): + segments_info.append({ + 'id': int(ann_ids[i]), + 'category_id': remap[int(ann_labels[i] + 1)], + 'iscrowd': int(ann_iscrowd[i]), + 'area': int(ann_area[i]), + }) + + annotations.append({ + 'file_name': str(fname), + 'image_id': int(image_id), + 'segments_info': segments_info + }) + images.append({ + 'id': image_id, + 'file_name': f'{image_id:012d}.jpg', + }) + + # Write annotations.json needed for pq_compute. + gt_json = os.path.join(gt_folder, 'annotations.json') + with gfile.GFile(gt_json, 'wb') as f: + f.write(json.dumps({ + 'images': images, + 'annotations': annotations, + 'categories': categories, + })) + + return gt_folder, gt_json, categories_json, remap, size_map + + +def _prepare_ground_truth_from_zipfiles(split): + """Prepare ground truth from coco zip files.""" + split_prefix = split.split('[')[0] + if split_prefix not in ('train', 'validation'): + raise ValueError(f'Split {split} not supported') + + # The following 4 calls are cached. This allows to save significant time + # in use cases like sweeping predict_fn hparams on the same run. + gt_json = _make_local_copy(PANOPTIC_2017[split_prefix]) + gt_folder = _make_local_unzip_copy(PANOPTIC_GT_ZIP[split_prefix]) + categories_json = _make_local_copy(PANOPTIC_COCO_CATS_FILE) + image_ids = _list_image_ids('coco/2017_panoptic', split) + + gt_folder = os.path.join( + gt_folder, 'panoptic_val2017' + if split_prefix == 'validation' else 'panoptic_train2017') + + # Build map from tfds class ids to COCO class ids. + remap = {0: 0} + with gfile.GFile(categories_json, 'r') as f: + remap = {**remap, **{(i + 1): x['id'] for i, x in enumerate(json.load(f))}} + + # Filters gt_json to contain only annotations for images in dataset. + with gfile.GFile(gt_json) as f: + data = json.load(f) + logging.info( + 'Panoptic eval: pre-filter %d annotations.', + len(data['annotations']) + ) + data['images'] = [x for x in data['images'] if x['id'] in image_ids] + data['annotations'] = [ + x for x in data['annotations'] if x['image_id'] in image_ids + ] + logging.info( + 'Panoptic eval: post-filter %d annotations.', + len(data['annotations']) + ) + filtered_gt_json = tempfile.NamedTemporaryFile(delete=False).name + with open(filtered_gt_json, 'w') as f: + json.dump(data, f) + + # Precompute images sizes. + size_map = {x['id']: (x['height'], x['width']) for x in data['images']} + + return gt_folder, filtered_gt_json, categories_json, remap, size_map + + +@functools.lru_cache(maxsize=None) +def _list_image_ids(dataset, split): + d = tfds.load(dataset, split=split).map(lambda x: x['image/id']) + return frozenset(d.as_numpy_iterator()) + + +@functools.lru_cache(maxsize=None) +def _make_local_copy(fname) -> str: + start = time.monotonic() + local_file = tempfile.NamedTemporaryFile(delete=False) + gfile.copy(fname, local_file.name, overwrite=True) + logging.info('Copy %s in %d seconds.', fname, time.monotonic() - start) + return local_file.name + + +@functools.lru_cache(maxsize=None) +def _make_local_unzip_copy(fname) -> str: + start = time.monotonic() + folder = tempfile.mkdtemp() + with tempfile.NamedTemporaryFile() as tmp_zip_file: + gfile.copy(fname, tmp_zip_file.name, overwrite=True) + with zipfile.ZipFile(tmp_zip_file.name, 'r') as f: + f.extractall(folder) + logging.info('Copy %s in %d seconds.', fname, time.monotonic() - start) + return folder + + +@partial(jax.jit, static_argnums=(1,), backend='cpu') +def _resize_nearest(image, shape): + return jax.image.resize(image, shape + image.shape[-1:], 'nearest') diff --git a/big_vision/evaluators/proj/uvim/coltran_fid.py b/big_vision/evaluators/proj/uvim/coltran_fid.py new file mode 100644 index 0000000000000000000000000000000000000000..555a7a2b008e6c4a09e172eed250cef92cd04c1b --- /dev/null +++ b/big_vision/evaluators/proj/uvim/coltran_fid.py @@ -0,0 +1,242 @@ +# Copyright 2022 Big Vision 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. + +"""Evaluation producing ColTran FID-5K metric.""" + +import functools +import os + +from absl import logging +import einops +import jax +import numpy as np +import tensorflow as tf +import tensorflow_datasets as tfds +import tensorflow_gan as tfgan +import tensorflow_hub as tfhub + +from tensorflow.io import gfile + + +ROOT = os.environ.get("FID_DATA_DIR", ".") + + +def _preprocess(image, resolution=512): + """ColTran dataset preprocessing. + + See, + github.com/google-research/google-research/blob/master/coltran/datasets.py#L44 + + Args: + image: ImageNet example from TFDS. + resolution: Integer representing output size. + + Returns: + An int32 image of size (resolution, resolution, 3). + """ + image_shape = tf.shape(image) + height, width = image_shape[0], image_shape[1] + side_size = tf.minimum(height, width) + image = tf.image.resize_with_crop_or_pad( + image, target_height=side_size, target_width=side_size) + image = tf.image.resize(image, method="area", antialias=True, + size=(resolution, resolution)) + image = tf.cast(tf.round(image), dtype=tf.int32) + return image + + +def _normalize(x): + """Coltran normalization to expected range for Inception module. + + Args: + x: Image with values in [0,255]. + + Returns: + Image with values in [-1,1]. + """ + x = tf.cast(x, tf.float32) + x = (x / 128.0) - 1.0 # note: 128.0 is the value used in ColTran. + return x + + +class Evaluator: + """ColTran FID-5K Evaluator. + + This Evaluator aims to mirror the evaluation pipeline used by Kumar et.al. + in Colorization Transformer (https://arxiv.org/abs/2102.04432). + + To be clear: much of this code is direct snippets from ColTran code. + + See, + github.com/google-research/google-research/blob/master/coltran/datasets.py#L44 + + The ColTran pipeline has numerous stages, where serialied data is passed + between binaries via file, etc... While we don't physically write the same + files, we simulate the effects of the serialization (e.g., quantization). + """ + + def __init__(self, + predict_fn, + batch_size, # ignored + device_batch_size=5, + coltran_seed=1, + predict_kwargs=None): + """Create Evaluator. + + Args: + predict_fn: Colorization prediction function. Expects grayscale images + of size (512, 512, 3) in keys `image` and `image_ctx` with values in + the range [-1,1]. Outputs `color` image in range [-1,1]. + batch_size: ignored. + device_batch_size: number of images per batch, per device. + coltran_seed: used to specify the block of 5_000 images used to generate + the reference pool. Value of `1` matches default ColTran code. + predict_kwargs: arguments passed to `predict_fn`. + """ + del batch_size + + self.num_devices = jax.local_device_count() + self.device_batch_size = device_batch_size + logging.log(logging.INFO, "Colorizing with batch size %i on %i devices.", + self.device_batch_size, self.num_devices) + assert 5_000 % (self.device_batch_size * self.num_devices) == 0 + + predict = functools.partial(predict_fn, **(predict_kwargs or {})) + self.predict_fn = jax.pmap(predict) + + module = tfhub.load(tfgan.eval.INCEPTION_TFHUB) + def _pools(x): + return np.squeeze(module(x)[tfgan.eval.INCEPTION_FINAL_POOL].numpy()) + + self.inception_pool = _pools + + # Setup the colorization dataset. + # TRICKY: ColTran FID-5k uses the first 5_000 images returned as read by + # default from tensorflow_datasets (that is: with shard interleaving). + # In particular note that it is different than the set of images returned + # by "validation[:5000]". + def _eval_data_preprocess(example): + # Colorization happens at 512x512 resolution. + image = _preprocess(example["image"], resolution=512) + image = _normalize(image) + grayscale = tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image)) + return { + "image": image, + "grayscale": grayscale, + "file_name": example["file_name"] + } + + ds = tfds.load("imagenet2012", split="validation") + ds = ds.map(_eval_data_preprocess) + ds = ds.take(5_000) + ds = ds.batch(self.device_batch_size) + ds = ds.batch(self.num_devices) + self.eval_data = ds.cache().prefetch(tf.data.AUTOTUNE) + + # Setup the reference dataset. + def _reference_data_preprocess(example): + # ColTran eval operates on 256x256. + image = _preprocess(example["image"], resolution=256) + image = _normalize(image) + return {"image": image, "file_name": example["file_name"]} + + ds = tfds.load("imagenet2012", split="validation") + ds = ds.map(_reference_data_preprocess) + # Skip the images used in colorization. + ds = ds.skip(5_000) + # ColTran eval w/ seed=1 effectively uses 10_000:15_000 to + # calculate reference. + ds = ds.skip(coltran_seed * 5_000) + ds = ds.take(5_000) + ds = ds.batch(device_batch_size) + self.reference_data = ds.cache().prefetch(tf.data.AUTOTUNE) + + def _get_file(name): + return os.path.join(ROOT, name) + + with gfile.GFile(_get_file("eval_file_names.txt")) as f: + self.eval_file_names = frozenset(f.read().splitlines()) + + with gfile.GFile(_get_file("reference_file_names.txt")) as f: + self.reference_file_names = frozenset(f.read().splitlines()) + + def run(self, params): + """Run eval.""" + + if jax.process_index(): # Host0 does all work. + return + + color_pools = [] + color_file_names = set() + for i, batch in enumerate(self.eval_data.as_numpy_iterator()): + predict_batch = { + "labels": batch["image"], + "image": batch["grayscale"], + "image_ctx": batch["grayscale"], + } + y = self.predict_fn(params, predict_batch) + y = y["color"] + y = einops.rearrange(y, "d b h w c -> (d b) h w c") + + # Return to the ColTran eval size of 256x256. + y = tf.image.resize(y, (256, 256), "area") + + # Mimic effect of serializing image as integers and map back to [-1, 1]. + y = np.clip(np.round((y + 1.) * 128.), 0, 255) + y = _normalize(y) + + color_pools.append(self.inception_pool(y)) + + file_names = einops.rearrange(batch["file_name"], "d b -> (d b)") + color_file_names.update([f.decode() for f in file_names]) + + logging.log_every_n_seconds( + logging.INFO, + "ColTran FID eval: processed %i colorized examples so far.", 30, + (i + 1) * self.device_batch_size * self.num_devices) + + reference_pools = [] + reference_file_names = set() + for i, batch in enumerate(self.reference_data.as_numpy_iterator()): + image = batch["image"] + assert np.array_equal(image.shape, (self.device_batch_size, 256, 256, 3)) + reference_pools.append(self.inception_pool(image)) + reference_file_names.update([f.decode() for f in batch["file_name"]]) + + logging.log_every_n_seconds( + logging.INFO, + "ColTran FID eval: processed %i reference examples so far.", 30, + (i + 1) * self.device_batch_size) + + if color_file_names != self.eval_file_names: + raise ValueError("unknown: {}\nmissing: {}".format( + color_file_names - self.eval_file_names, + self.eval_file_names - color_file_names)) + + if reference_file_names != self.reference_file_names: + raise ValueError("unknown: {}\nmissing: {}".format( + reference_file_names - self.reference_file_names, + self.reference_file_names - reference_file_names)) + + color = np.concatenate(color_pools, axis=0) + reference = np.concatenate(reference_pools, axis=0) + + if color.shape[0] != 5_000: + raise ValueError(color.shape) + + if reference.shape[0] != 5_000: + raise ValueError(reference.shape) + + yield "FID_5k", tfgan.eval.frechet_classifier_distance_from_activations( + color, reference) diff --git a/big_vision/evaluators/proj/uvim/coltran_fid_data/eval_file_names.txt b/big_vision/evaluators/proj/uvim/coltran_fid_data/eval_file_names.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3a08b9dd2cc6bc41e07ea92039e49b564312153 --- /dev/null +++ b/big_vision/evaluators/proj/uvim/coltran_fid_data/eval_file_names.txt @@ -0,0 +1,5000 @@ +ILSVRC2012_val_00009670.JPEG +ILSVRC2012_val_00036705.JPEG +ILSVRC2012_val_00003545.JPEG +ILSVRC2012_val_00047963.JPEG +ILSVRC2012_val_00023277.JPEG +ILSVRC2012_val_00004014.JPEG +ILSVRC2012_val_00001121.JPEG +ILSVRC2012_val_00011754.JPEG +ILSVRC2012_val_00025035.JPEG +ILSVRC2012_val_00043797.JPEG +ILSVRC2012_val_00046096.JPEG +ILSVRC2012_val_00024105.JPEG +ILSVRC2012_val_00031747.JPEG +ILSVRC2012_val_00024113.JPEG +ILSVRC2012_val_00025971.JPEG +ILSVRC2012_val_00032467.JPEG +ILSVRC2012_val_00021106.JPEG +ILSVRC2012_val_00048369.JPEG +ILSVRC2012_val_00034488.JPEG +ILSVRC2012_val_00037380.JPEG +ILSVRC2012_val_00016846.JPEG +ILSVRC2012_val_00042664.JPEG +ILSVRC2012_val_00003230.JPEG +ILSVRC2012_val_00045510.JPEG +ILSVRC2012_val_00024667.JPEG +ILSVRC2012_val_00033383.JPEG +ILSVRC2012_val_00016559.JPEG +ILSVRC2012_val_00012460.JPEG +ILSVRC2012_val_00015028.JPEG +ILSVRC2012_val_00036694.JPEG +ILSVRC2012_val_00048401.JPEG +ILSVRC2012_val_00008599.JPEG +ILSVRC2012_val_00024724.JPEG +ILSVRC2012_val_00016613.JPEG +ILSVRC2012_val_00012872.JPEG +ILSVRC2012_val_00029464.JPEG +ILSVRC2012_val_00006791.JPEG +ILSVRC2012_val_00040933.JPEG +ILSVRC2012_val_00020980.JPEG +ILSVRC2012_val_00020933.JPEG +ILSVRC2012_val_00041264.JPEG +ILSVRC2012_val_00009098.JPEG +ILSVRC2012_val_00018611.JPEG +ILSVRC2012_val_00034921.JPEG +ILSVRC2012_val_00010025.JPEG +ILSVRC2012_val_00021580.JPEG +ILSVRC2012_val_00037940.JPEG +ILSVRC2012_val_00012585.JPEG +ILSVRC2012_val_00040631.JPEG +ILSVRC2012_val_00043473.JPEG +ILSVRC2012_val_00035336.JPEG +ILSVRC2012_val_00023147.JPEG +ILSVRC2012_val_00013833.JPEG +ILSVRC2012_val_00016418.JPEG +ILSVRC2012_val_00033390.JPEG +ILSVRC2012_val_00047840.JPEG +ILSVRC2012_val_00016048.JPEG +ILSVRC2012_val_00045736.JPEG +ILSVRC2012_val_00008967.JPEG +ILSVRC2012_val_00020593.JPEG +ILSVRC2012_val_00023548.JPEG +ILSVRC2012_val_00026589.JPEG +ILSVRC2012_val_00047233.JPEG +ILSVRC2012_val_00001523.JPEG +ILSVRC2012_val_00028686.JPEG +ILSVRC2012_val_00006172.JPEG +ILSVRC2012_val_00014856.JPEG +ILSVRC2012_val_00023450.JPEG +ILSVRC2012_val_00012349.JPEG +ILSVRC2012_val_00038876.JPEG +ILSVRC2012_val_00029340.JPEG +ILSVRC2012_val_00009986.JPEG +ILSVRC2012_val_00028311.JPEG +ILSVRC2012_val_00048337.JPEG +ILSVRC2012_val_00020541.JPEG +ILSVRC2012_val_00044507.JPEG +ILSVRC2012_val_00024092.JPEG +ILSVRC2012_val_00049283.JPEG +ILSVRC2012_val_00036416.JPEG +ILSVRC2012_val_00016583.JPEG +ILSVRC2012_val_00045446.JPEG +ILSVRC2012_val_00038567.JPEG +ILSVRC2012_val_00033717.JPEG +ILSVRC2012_val_00038475.JPEG +ILSVRC2012_val_00026422.JPEG +ILSVRC2012_val_00044982.JPEG +ILSVRC2012_val_00044802.JPEG +ILSVRC2012_val_00014315.JPEG +ILSVRC2012_val_00009085.JPEG +ILSVRC2012_val_00020613.JPEG +ILSVRC2012_val_00017637.JPEG +ILSVRC2012_val_00030081.JPEG +ILSVRC2012_val_00022925.JPEG +ILSVRC2012_val_00014615.JPEG +ILSVRC2012_val_00033931.JPEG +ILSVRC2012_val_00038691.JPEG +ILSVRC2012_val_00012242.JPEG +ILSVRC2012_val_00004258.JPEG +ILSVRC2012_val_00016300.JPEG +ILSVRC2012_val_00044764.JPEG +ILSVRC2012_val_00048033.JPEG +ILSVRC2012_val_00042120.JPEG +ILSVRC2012_val_00004634.JPEG +ILSVRC2012_val_00030833.JPEG +ILSVRC2012_val_00040507.JPEG +ILSVRC2012_val_00046915.JPEG +ILSVRC2012_val_00000762.JPEG +ILSVRC2012_val_00044717.JPEG +ILSVRC2012_val_00041480.JPEG +ILSVRC2012_val_00021262.JPEG +ILSVRC2012_val_00025761.JPEG +ILSVRC2012_val_00004455.JPEG +ILSVRC2012_val_00035741.JPEG +ILSVRC2012_val_00040331.JPEG +ILSVRC2012_val_00027190.JPEG +ILSVRC2012_val_00031247.JPEG +ILSVRC2012_val_00022364.JPEG +ILSVRC2012_val_00020647.JPEG +ILSVRC2012_val_00045455.JPEG +ILSVRC2012_val_00036387.JPEG +ILSVRC2012_val_00002584.JPEG +ILSVRC2012_val_00028727.JPEG +ILSVRC2012_val_00033509.JPEG +ILSVRC2012_val_00045960.JPEG +ILSVRC2012_val_00018035.JPEG +ILSVRC2012_val_00042294.JPEG +ILSVRC2012_val_00037068.JPEG +ILSVRC2012_val_00026772.JPEG +ILSVRC2012_val_00043286.JPEG +ILSVRC2012_val_00040479.JPEG +ILSVRC2012_val_00029969.JPEG +ILSVRC2012_val_00027908.JPEG +ILSVRC2012_val_00046152.JPEG +ILSVRC2012_val_00024557.JPEG +ILSVRC2012_val_00025049.JPEG +ILSVRC2012_val_00015498.JPEG +ILSVRC2012_val_00005882.JPEG +ILSVRC2012_val_00014682.JPEG +ILSVRC2012_val_00044484.JPEG +ILSVRC2012_val_00004990.JPEG +ILSVRC2012_val_00016420.JPEG +ILSVRC2012_val_00021289.JPEG +ILSVRC2012_val_00013360.JPEG +ILSVRC2012_val_00038600.JPEG +ILSVRC2012_val_00041932.JPEG +ILSVRC2012_val_00021338.JPEG +ILSVRC2012_val_00002296.JPEG +ILSVRC2012_val_00032757.JPEG +ILSVRC2012_val_00010804.JPEG +ILSVRC2012_val_00035707.JPEG +ILSVRC2012_val_00049995.JPEG +ILSVRC2012_val_00013871.JPEG +ILSVRC2012_val_00036383.JPEG +ILSVRC2012_val_00011718.JPEG +ILSVRC2012_val_00012518.JPEG +ILSVRC2012_val_00018637.JPEG +ILSVRC2012_val_00037722.JPEG +ILSVRC2012_val_00000040.JPEG +ILSVRC2012_val_00021312.JPEG +ILSVRC2012_val_00034539.JPEG +ILSVRC2012_val_00049523.JPEG +ILSVRC2012_val_00000347.JPEG +ILSVRC2012_val_00000428.JPEG +ILSVRC2012_val_00027218.JPEG +ILSVRC2012_val_00004346.JPEG +ILSVRC2012_val_00004030.JPEG +ILSVRC2012_val_00035334.JPEG +ILSVRC2012_val_00004368.JPEG +ILSVRC2012_val_00034466.JPEG +ILSVRC2012_val_00045203.JPEG +ILSVRC2012_val_00017630.JPEG +ILSVRC2012_val_00037067.JPEG +ILSVRC2012_val_00006656.JPEG +ILSVRC2012_val_00029811.JPEG +ILSVRC2012_val_00034522.JPEG +ILSVRC2012_val_00003583.JPEG +ILSVRC2012_val_00008683.JPEG +ILSVRC2012_val_00030886.JPEG +ILSVRC2012_val_00004097.JPEG +ILSVRC2012_val_00032031.JPEG +ILSVRC2012_val_00039652.JPEG +ILSVRC2012_val_00005729.JPEG +ILSVRC2012_val_00013205.JPEG +ILSVRC2012_val_00011653.JPEG +ILSVRC2012_val_00038287.JPEG +ILSVRC2012_val_00046447.JPEG +ILSVRC2012_val_00025152.JPEG +ILSVRC2012_val_00035740.JPEG +ILSVRC2012_val_00017399.JPEG +ILSVRC2012_val_00032779.JPEG +ILSVRC2012_val_00034375.JPEG +ILSVRC2012_val_00030992.JPEG +ILSVRC2012_val_00041114.JPEG +ILSVRC2012_val_00023050.JPEG +ILSVRC2012_val_00030819.JPEG +ILSVRC2012_val_00040316.JPEG +ILSVRC2012_val_00019266.JPEG +ILSVRC2012_val_00017289.JPEG +ILSVRC2012_val_00034924.JPEG +ILSVRC2012_val_00031452.JPEG +ILSVRC2012_val_00032174.JPEG +ILSVRC2012_val_00048817.JPEG +ILSVRC2012_val_00012131.JPEG +ILSVRC2012_val_00009882.JPEG +ILSVRC2012_val_00025737.JPEG +ILSVRC2012_val_00010951.JPEG +ILSVRC2012_val_00024919.JPEG +ILSVRC2012_val_00049774.JPEG +ILSVRC2012_val_00041614.JPEG +ILSVRC2012_val_00023960.JPEG +ILSVRC2012_val_00036801.JPEG +ILSVRC2012_val_00006146.JPEG +ILSVRC2012_val_00002008.JPEG +ILSVRC2012_val_00031210.JPEG +ILSVRC2012_val_00030460.JPEG +ILSVRC2012_val_00020539.JPEG +ILSVRC2012_val_00007465.JPEG +ILSVRC2012_val_00001126.JPEG +ILSVRC2012_val_00039167.JPEG +ILSVRC2012_val_00043920.JPEG +ILSVRC2012_val_00029976.JPEG +ILSVRC2012_val_00013459.JPEG +ILSVRC2012_val_00021410.JPEG +ILSVRC2012_val_00039878.JPEG +ILSVRC2012_val_00012414.JPEG +ILSVRC2012_val_00000749.JPEG +ILSVRC2012_val_00011678.JPEG +ILSVRC2012_val_00007644.JPEG +ILSVRC2012_val_00029391.JPEG +ILSVRC2012_val_00009891.JPEG +ILSVRC2012_val_00003378.JPEG +ILSVRC2012_val_00017374.JPEG +ILSVRC2012_val_00031863.JPEG +ILSVRC2012_val_00042312.JPEG +ILSVRC2012_val_00026452.JPEG +ILSVRC2012_val_00030014.JPEG +ILSVRC2012_val_00048330.JPEG +ILSVRC2012_val_00001343.JPEG +ILSVRC2012_val_00043156.JPEG +ILSVRC2012_val_00029457.JPEG +ILSVRC2012_val_00011316.JPEG +ILSVRC2012_val_00020040.JPEG +ILSVRC2012_val_00025723.JPEG +ILSVRC2012_val_00048364.JPEG +ILSVRC2012_val_00016653.JPEG +ILSVRC2012_val_00032241.JPEG +ILSVRC2012_val_00040938.JPEG +ILSVRC2012_val_00003520.JPEG +ILSVRC2012_val_00003617.JPEG +ILSVRC2012_val_00031484.JPEG +ILSVRC2012_val_00019398.JPEG +ILSVRC2012_val_00020670.JPEG +ILSVRC2012_val_00015257.JPEG +ILSVRC2012_val_00032691.JPEG +ILSVRC2012_val_00045452.JPEG +ILSVRC2012_val_00026480.JPEG +ILSVRC2012_val_00042441.JPEG +ILSVRC2012_val_00039575.JPEG +ILSVRC2012_val_00042619.JPEG +ILSVRC2012_val_00043399.JPEG +ILSVRC2012_val_00044429.JPEG +ILSVRC2012_val_00033520.JPEG +ILSVRC2012_val_00046423.JPEG +ILSVRC2012_val_00042039.JPEG +ILSVRC2012_val_00003944.JPEG +ILSVRC2012_val_00047258.JPEG +ILSVRC2012_val_00037770.JPEG +ILSVRC2012_val_00011256.JPEG +ILSVRC2012_val_00010495.JPEG +ILSVRC2012_val_00010520.JPEG +ILSVRC2012_val_00027160.JPEG +ILSVRC2012_val_00032256.JPEG +ILSVRC2012_val_00001610.JPEG +ILSVRC2012_val_00008326.JPEG +ILSVRC2012_val_00041685.JPEG +ILSVRC2012_val_00021443.JPEG +ILSVRC2012_val_00043981.JPEG +ILSVRC2012_val_00029578.JPEG +ILSVRC2012_val_00030561.JPEG +ILSVRC2012_val_00019953.JPEG +ILSVRC2012_val_00049574.JPEG +ILSVRC2012_val_00026189.JPEG +ILSVRC2012_val_00013869.JPEG +ILSVRC2012_val_00048582.JPEG +ILSVRC2012_val_00044818.JPEG +ILSVRC2012_val_00016735.JPEG +ILSVRC2012_val_00036027.JPEG +ILSVRC2012_val_00024036.JPEG +ILSVRC2012_val_00040658.JPEG +ILSVRC2012_val_00010908.JPEG +ILSVRC2012_val_00012616.JPEG +ILSVRC2012_val_00003581.JPEG +ILSVRC2012_val_00028865.JPEG +ILSVRC2012_val_00042945.JPEG +ILSVRC2012_val_00042482.JPEG +ILSVRC2012_val_00047910.JPEG +ILSVRC2012_val_00025495.JPEG +ILSVRC2012_val_00023440.JPEG +ILSVRC2012_val_00020693.JPEG +ILSVRC2012_val_00021337.JPEG +ILSVRC2012_val_00048664.JPEG +ILSVRC2012_val_00038325.JPEG +ILSVRC2012_val_00039572.JPEG +ILSVRC2012_val_00017513.JPEG +ILSVRC2012_val_00042217.JPEG +ILSVRC2012_val_00028507.JPEG +ILSVRC2012_val_00007673.JPEG +ILSVRC2012_val_00037549.JPEG +ILSVRC2012_val_00021631.JPEG +ILSVRC2012_val_00003637.JPEG +ILSVRC2012_val_00013239.JPEG +ILSVRC2012_val_00030430.JPEG +ILSVRC2012_val_00018513.JPEG +ILSVRC2012_val_00000120.JPEG +ILSVRC2012_val_00046658.JPEG +ILSVRC2012_val_00021739.JPEG +ILSVRC2012_val_00040608.JPEG +ILSVRC2012_val_00031069.JPEG +ILSVRC2012_val_00022141.JPEG +ILSVRC2012_val_00015474.JPEG +ILSVRC2012_val_00046918.JPEG +ILSVRC2012_val_00020371.JPEG +ILSVRC2012_val_00011159.JPEG +ILSVRC2012_val_00030799.JPEG +ILSVRC2012_val_00039534.JPEG +ILSVRC2012_val_00015607.JPEG +ILSVRC2012_val_00023513.JPEG +ILSVRC2012_val_00046763.JPEG +ILSVRC2012_val_00016175.JPEG +ILSVRC2012_val_00017190.JPEG +ILSVRC2012_val_00048277.JPEG +ILSVRC2012_val_00015356.JPEG +ILSVRC2012_val_00023226.JPEG +ILSVRC2012_val_00026849.JPEG +ILSVRC2012_val_00011960.JPEG +ILSVRC2012_val_00046460.JPEG +ILSVRC2012_val_00042976.JPEG +ILSVRC2012_val_00029830.JPEG +ILSVRC2012_val_00037081.JPEG +ILSVRC2012_val_00021119.JPEG +ILSVRC2012_val_00002453.JPEG +ILSVRC2012_val_00001721.JPEG +ILSVRC2012_val_00005983.JPEG +ILSVRC2012_val_00031460.JPEG +ILSVRC2012_val_00010029.JPEG +ILSVRC2012_val_00009830.JPEG +ILSVRC2012_val_00004357.JPEG +ILSVRC2012_val_00038664.JPEG +ILSVRC2012_val_00040415.JPEG +ILSVRC2012_val_00046818.JPEG +ILSVRC2012_val_00047026.JPEG +ILSVRC2012_val_00034616.JPEG +ILSVRC2012_val_00004899.JPEG +ILSVRC2012_val_00033706.JPEG +ILSVRC2012_val_00047344.JPEG +ILSVRC2012_val_00038725.JPEG +ILSVRC2012_val_00031925.JPEG +ILSVRC2012_val_00010633.JPEG +ILSVRC2012_val_00020304.JPEG +ILSVRC2012_val_00036520.JPEG +ILSVRC2012_val_00004818.JPEG +ILSVRC2012_val_00035061.JPEG +ILSVRC2012_val_00018945.JPEG +ILSVRC2012_val_00029504.JPEG +ILSVRC2012_val_00015954.JPEG +ILSVRC2012_val_00009697.JPEG +ILSVRC2012_val_00015848.JPEG +ILSVRC2012_val_00013155.JPEG +ILSVRC2012_val_00008563.JPEG +ILSVRC2012_val_00025830.JPEG +ILSVRC2012_val_00014980.JPEG +ILSVRC2012_val_00006878.JPEG +ILSVRC2012_val_00024270.JPEG +ILSVRC2012_val_00000997.JPEG +ILSVRC2012_val_00031141.JPEG +ILSVRC2012_val_00032404.JPEG +ILSVRC2012_val_00024769.JPEG +ILSVRC2012_val_00037682.JPEG +ILSVRC2012_val_00012718.JPEG +ILSVRC2012_val_00047668.JPEG +ILSVRC2012_val_00021383.JPEG +ILSVRC2012_val_00037072.JPEG +ILSVRC2012_val_00001250.JPEG +ILSVRC2012_val_00017418.JPEG +ILSVRC2012_val_00017824.JPEG +ILSVRC2012_val_00045601.JPEG +ILSVRC2012_val_00025044.JPEG +ILSVRC2012_val_00001379.JPEG +ILSVRC2012_val_00029317.JPEG +ILSVRC2012_val_00029827.JPEG +ILSVRC2012_val_00031128.JPEG +ILSVRC2012_val_00005367.JPEG +ILSVRC2012_val_00046985.JPEG +ILSVRC2012_val_00021191.JPEG +ILSVRC2012_val_00009034.JPEG +ILSVRC2012_val_00048819.JPEG +ILSVRC2012_val_00035806.JPEG +ILSVRC2012_val_00048861.JPEG +ILSVRC2012_val_00043602.JPEG +ILSVRC2012_val_00022356.JPEG +ILSVRC2012_val_00025507.JPEG +ILSVRC2012_val_00031636.JPEG +ILSVRC2012_val_00045799.JPEG +ILSVRC2012_val_00015190.JPEG +ILSVRC2012_val_00008358.JPEG +ILSVRC2012_val_00041364.JPEG +ILSVRC2012_val_00039365.JPEG +ILSVRC2012_val_00041226.JPEG +ILSVRC2012_val_00000970.JPEG +ILSVRC2012_val_00046655.JPEG +ILSVRC2012_val_00040114.JPEG +ILSVRC2012_val_00027236.JPEG +ILSVRC2012_val_00020741.JPEG +ILSVRC2012_val_00011499.JPEG +ILSVRC2012_val_00024154.JPEG +ILSVRC2012_val_00031104.JPEG +ILSVRC2012_val_00009162.JPEG +ILSVRC2012_val_00008631.JPEG +ILSVRC2012_val_00031238.JPEG +ILSVRC2012_val_00024195.JPEG +ILSVRC2012_val_00023134.JPEG +ILSVRC2012_val_00022358.JPEG +ILSVRC2012_val_00029017.JPEG +ILSVRC2012_val_00027568.JPEG +ILSVRC2012_val_00013586.JPEG +ILSVRC2012_val_00014427.JPEG +ILSVRC2012_val_00048022.JPEG +ILSVRC2012_val_00015479.JPEG +ILSVRC2012_val_00027975.JPEG +ILSVRC2012_val_00021307.JPEG +ILSVRC2012_val_00010749.JPEG +ILSVRC2012_val_00025668.JPEG +ILSVRC2012_val_00042487.JPEG +ILSVRC2012_val_00003801.JPEG +ILSVRC2012_val_00036559.JPEG +ILSVRC2012_val_00042887.JPEG +ILSVRC2012_val_00020031.JPEG +ILSVRC2012_val_00030935.JPEG +ILSVRC2012_val_00019987.JPEG +ILSVRC2012_val_00005176.JPEG +ILSVRC2012_val_00046180.JPEG +ILSVRC2012_val_00018344.JPEG +ILSVRC2012_val_00009415.JPEG +ILSVRC2012_val_00006726.JPEG +ILSVRC2012_val_00028534.JPEG +ILSVRC2012_val_00022125.JPEG +ILSVRC2012_val_00037831.JPEG +ILSVRC2012_val_00036219.JPEG +ILSVRC2012_val_00038842.JPEG +ILSVRC2012_val_00047945.JPEG +ILSVRC2012_val_00021740.JPEG +ILSVRC2012_val_00011030.JPEG +ILSVRC2012_val_00034726.JPEG +ILSVRC2012_val_00006179.JPEG +ILSVRC2012_val_00010184.JPEG +ILSVRC2012_val_00010484.JPEG +ILSVRC2012_val_00042439.JPEG +ILSVRC2012_val_00002311.JPEG +ILSVRC2012_val_00043871.JPEG +ILSVRC2012_val_00043371.JPEG +ILSVRC2012_val_00046565.JPEG +ILSVRC2012_val_00030975.JPEG +ILSVRC2012_val_00004729.JPEG +ILSVRC2012_val_00015271.JPEG +ILSVRC2012_val_00043420.JPEG +ILSVRC2012_val_00028268.JPEG +ILSVRC2012_val_00000593.JPEG +ILSVRC2012_val_00018261.JPEG +ILSVRC2012_val_00031540.JPEG +ILSVRC2012_val_00047070.JPEG +ILSVRC2012_val_00042394.JPEG +ILSVRC2012_val_00003789.JPEG +ILSVRC2012_val_00038820.JPEG +ILSVRC2012_val_00036316.JPEG +ILSVRC2012_val_00008978.JPEG +ILSVRC2012_val_00031733.JPEG +ILSVRC2012_val_00017741.JPEG +ILSVRC2012_val_00042005.JPEG +ILSVRC2012_val_00007830.JPEG +ILSVRC2012_val_00030950.JPEG +ILSVRC2012_val_00013929.JPEG +ILSVRC2012_val_00019289.JPEG +ILSVRC2012_val_00002957.JPEG +ILSVRC2012_val_00004270.JPEG +ILSVRC2012_val_00041190.JPEG +ILSVRC2012_val_00025684.JPEG +ILSVRC2012_val_00004780.JPEG +ILSVRC2012_val_00040886.JPEG +ILSVRC2012_val_00029638.JPEG +ILSVRC2012_val_00004267.JPEG +ILSVRC2012_val_00004079.JPEG +ILSVRC2012_val_00032281.JPEG +ILSVRC2012_val_00047480.JPEG +ILSVRC2012_val_00042580.JPEG +ILSVRC2012_val_00022829.JPEG +ILSVRC2012_val_00040378.JPEG +ILSVRC2012_val_00026702.JPEG +ILSVRC2012_val_00008544.JPEG +ILSVRC2012_val_00021700.JPEG +ILSVRC2012_val_00012635.JPEG +ILSVRC2012_val_00042632.JPEG +ILSVRC2012_val_00029979.JPEG +ILSVRC2012_val_00016138.JPEG +ILSVRC2012_val_00045738.JPEG +ILSVRC2012_val_00042282.JPEG +ILSVRC2012_val_00001491.JPEG +ILSVRC2012_val_00004524.JPEG +ILSVRC2012_val_00034589.JPEG +ILSVRC2012_val_00040516.JPEG +ILSVRC2012_val_00006792.JPEG +ILSVRC2012_val_00035627.JPEG +ILSVRC2012_val_00015667.JPEG +ILSVRC2012_val_00048559.JPEG +ILSVRC2012_val_00030235.JPEG +ILSVRC2012_val_00045303.JPEG +ILSVRC2012_val_00030447.JPEG +ILSVRC2012_val_00003650.JPEG +ILSVRC2012_val_00022050.JPEG +ILSVRC2012_val_00005320.JPEG +ILSVRC2012_val_00042326.JPEG +ILSVRC2012_val_00009056.JPEG +ILSVRC2012_val_00017185.JPEG +ILSVRC2012_val_00016667.JPEG +ILSVRC2012_val_00043080.JPEG +ILSVRC2012_val_00039706.JPEG +ILSVRC2012_val_00035939.JPEG +ILSVRC2012_val_00037826.JPEG +ILSVRC2012_val_00039492.JPEG +ILSVRC2012_val_00008439.JPEG +ILSVRC2012_val_00045236.JPEG +ILSVRC2012_val_00000447.JPEG +ILSVRC2012_val_00030547.JPEG +ILSVRC2012_val_00034158.JPEG +ILSVRC2012_val_00005860.JPEG +ILSVRC2012_val_00039716.JPEG +ILSVRC2012_val_00005917.JPEG +ILSVRC2012_val_00045222.JPEG +ILSVRC2012_val_00000909.JPEG +ILSVRC2012_val_00016608.JPEG +ILSVRC2012_val_00044721.JPEG +ILSVRC2012_val_00030604.JPEG +ILSVRC2012_val_00041494.JPEG +ILSVRC2012_val_00043208.JPEG +ILSVRC2012_val_00026190.JPEG +ILSVRC2012_val_00029654.JPEG +ILSVRC2012_val_00007899.JPEG +ILSVRC2012_val_00046908.JPEG +ILSVRC2012_val_00017670.JPEG +ILSVRC2012_val_00002322.JPEG +ILSVRC2012_val_00002785.JPEG +ILSVRC2012_val_00037454.JPEG +ILSVRC2012_val_00045836.JPEG +ILSVRC2012_val_00020676.JPEG +ILSVRC2012_val_00049382.JPEG +ILSVRC2012_val_00029772.JPEG +ILSVRC2012_val_00011720.JPEG +ILSVRC2012_val_00028956.JPEG +ILSVRC2012_val_00038182.JPEG +ILSVRC2012_val_00013411.JPEG +ILSVRC2012_val_00046185.JPEG +ILSVRC2012_val_00049014.JPEG +ILSVRC2012_val_00021642.JPEG +ILSVRC2012_val_00046670.JPEG +ILSVRC2012_val_00030910.JPEG +ILSVRC2012_val_00035971.JPEG +ILSVRC2012_val_00045690.JPEG +ILSVRC2012_val_00039432.JPEG +ILSVRC2012_val_00012133.JPEG +ILSVRC2012_val_00046713.JPEG +ILSVRC2012_val_00031823.JPEG +ILSVRC2012_val_00001943.JPEG +ILSVRC2012_val_00024065.JPEG +ILSVRC2012_val_00018502.JPEG +ILSVRC2012_val_00042610.JPEG +ILSVRC2012_val_00003767.JPEG +ILSVRC2012_val_00000393.JPEG +ILSVRC2012_val_00046280.JPEG +ILSVRC2012_val_00046663.JPEG +ILSVRC2012_val_00036336.JPEG +ILSVRC2012_val_00000979.JPEG +ILSVRC2012_val_00026432.JPEG +ILSVRC2012_val_00017613.JPEG +ILSVRC2012_val_00025885.JPEG +ILSVRC2012_val_00007436.JPEG +ILSVRC2012_val_00027102.JPEG +ILSVRC2012_val_00024286.JPEG +ILSVRC2012_val_00046749.JPEG +ILSVRC2012_val_00046151.JPEG +ILSVRC2012_val_00011151.JPEG +ILSVRC2012_val_00001741.JPEG +ILSVRC2012_val_00034881.JPEG +ILSVRC2012_val_00018527.JPEG +ILSVRC2012_val_00048102.JPEG +ILSVRC2012_val_00020819.JPEG +ILSVRC2012_val_00017642.JPEG +ILSVRC2012_val_00026680.JPEG +ILSVRC2012_val_00039142.JPEG +ILSVRC2012_val_00041587.JPEG +ILSVRC2012_val_00013856.JPEG +ILSVRC2012_val_00005611.JPEG +ILSVRC2012_val_00027603.JPEG +ILSVRC2012_val_00048982.JPEG +ILSVRC2012_val_00039851.JPEG +ILSVRC2012_val_00019631.JPEG +ILSVRC2012_val_00036405.JPEG +ILSVRC2012_val_00020916.JPEG +ILSVRC2012_val_00021716.JPEG +ILSVRC2012_val_00048373.JPEG +ILSVRC2012_val_00000244.JPEG +ILSVRC2012_val_00037089.JPEG +ILSVRC2012_val_00040530.JPEG +ILSVRC2012_val_00036487.JPEG +ILSVRC2012_val_00015440.JPEG +ILSVRC2012_val_00008791.JPEG +ILSVRC2012_val_00020410.JPEG +ILSVRC2012_val_00016186.JPEG +ILSVRC2012_val_00021326.JPEG +ILSVRC2012_val_00044095.JPEG +ILSVRC2012_val_00012615.JPEG +ILSVRC2012_val_00002191.JPEG +ILSVRC2012_val_00016885.JPEG +ILSVRC2012_val_00015676.JPEG +ILSVRC2012_val_00027342.JPEG +ILSVRC2012_val_00005590.JPEG +ILSVRC2012_val_00023216.JPEG +ILSVRC2012_val_00004117.JPEG +ILSVRC2012_val_00039457.JPEG +ILSVRC2012_val_00033268.JPEG +ILSVRC2012_val_00020397.JPEG +ILSVRC2012_val_00010419.JPEG +ILSVRC2012_val_00001813.JPEG +ILSVRC2012_val_00037279.JPEG +ILSVRC2012_val_00040026.JPEG +ILSVRC2012_val_00000830.JPEG +ILSVRC2012_val_00022765.JPEG +ILSVRC2012_val_00009740.JPEG +ILSVRC2012_val_00042032.JPEG +ILSVRC2012_val_00033972.JPEG +ILSVRC2012_val_00033314.JPEG +ILSVRC2012_val_00024704.JPEG +ILSVRC2012_val_00021353.JPEG +ILSVRC2012_val_00005989.JPEG +ILSVRC2012_val_00033953.JPEG +ILSVRC2012_val_00006250.JPEG +ILSVRC2012_val_00042862.JPEG +ILSVRC2012_val_00049804.JPEG +ILSVRC2012_val_00037028.JPEG +ILSVRC2012_val_00011245.JPEG +ILSVRC2012_val_00022488.JPEG +ILSVRC2012_val_00049099.JPEG +ILSVRC2012_val_00038906.JPEG +ILSVRC2012_val_00045665.JPEG +ILSVRC2012_val_00049548.JPEG +ILSVRC2012_val_00030884.JPEG +ILSVRC2012_val_00030607.JPEG +ILSVRC2012_val_00002379.JPEG +ILSVRC2012_val_00044441.JPEG +ILSVRC2012_val_00011964.JPEG +ILSVRC2012_val_00005784.JPEG +ILSVRC2012_val_00018498.JPEG +ILSVRC2012_val_00048229.JPEG +ILSVRC2012_val_00016394.JPEG +ILSVRC2012_val_00010374.JPEG +ILSVRC2012_val_00000565.JPEG +ILSVRC2012_val_00013657.JPEG +ILSVRC2012_val_00021903.JPEG +ILSVRC2012_val_00039676.JPEG +ILSVRC2012_val_00018570.JPEG +ILSVRC2012_val_00041762.JPEG +ILSVRC2012_val_00015314.JPEG +ILSVRC2012_val_00023494.JPEG +ILSVRC2012_val_00015060.JPEG +ILSVRC2012_val_00039543.JPEG +ILSVRC2012_val_00030742.JPEG +ILSVRC2012_val_00024456.JPEG +ILSVRC2012_val_00006026.JPEG +ILSVRC2012_val_00022754.JPEG +ILSVRC2012_val_00019574.JPEG +ILSVRC2012_val_00012194.JPEG +ILSVRC2012_val_00031471.JPEG +ILSVRC2012_val_00003205.JPEG +ILSVRC2012_val_00041311.JPEG +ILSVRC2012_val_00049647.JPEG +ILSVRC2012_val_00023757.JPEG +ILSVRC2012_val_00023546.JPEG +ILSVRC2012_val_00030601.JPEG +ILSVRC2012_val_00012923.JPEG +ILSVRC2012_val_00000686.JPEG +ILSVRC2012_val_00006643.JPEG +ILSVRC2012_val_00008587.JPEG +ILSVRC2012_val_00003375.JPEG +ILSVRC2012_val_00036876.JPEG +ILSVRC2012_val_00016907.JPEG +ILSVRC2012_val_00002793.JPEG +ILSVRC2012_val_00040972.JPEG +ILSVRC2012_val_00033005.JPEG +ILSVRC2012_val_00036578.JPEG +ILSVRC2012_val_00015483.JPEG +ILSVRC2012_val_00047713.JPEG +ILSVRC2012_val_00032235.JPEG +ILSVRC2012_val_00016825.JPEG +ILSVRC2012_val_00028952.JPEG +ILSVRC2012_val_00028046.JPEG +ILSVRC2012_val_00026316.JPEG +ILSVRC2012_val_00011268.JPEG +ILSVRC2012_val_00037878.JPEG +ILSVRC2012_val_00007218.JPEG +ILSVRC2012_val_00048006.JPEG +ILSVRC2012_val_00023586.JPEG +ILSVRC2012_val_00037743.JPEG +ILSVRC2012_val_00008175.JPEG +ILSVRC2012_val_00006467.JPEG +ILSVRC2012_val_00040510.JPEG +ILSVRC2012_val_00022978.JPEG +ILSVRC2012_val_00034599.JPEG +ILSVRC2012_val_00040291.JPEG +ILSVRC2012_val_00002668.JPEG +ILSVRC2012_val_00038661.JPEG +ILSVRC2012_val_00015420.JPEG +ILSVRC2012_val_00024723.JPEG +ILSVRC2012_val_00024255.JPEG +ILSVRC2012_val_00012039.JPEG +ILSVRC2012_val_00011522.JPEG +ILSVRC2012_val_00007093.JPEG +ILSVRC2012_val_00012070.JPEG +ILSVRC2012_val_00005579.JPEG +ILSVRC2012_val_00006032.JPEG +ILSVRC2012_val_00006677.JPEG +ILSVRC2012_val_00006448.JPEG +ILSVRC2012_val_00036734.JPEG +ILSVRC2012_val_00021412.JPEG +ILSVRC2012_val_00001170.JPEG +ILSVRC2012_val_00040690.JPEG +ILSVRC2012_val_00007065.JPEG +ILSVRC2012_val_00027621.JPEG +ILSVRC2012_val_00038562.JPEG +ILSVRC2012_val_00028129.JPEG +ILSVRC2012_val_00004292.JPEG +ILSVRC2012_val_00025653.JPEG +ILSVRC2012_val_00029426.JPEG +ILSVRC2012_val_00036764.JPEG +ILSVRC2012_val_00005391.JPEG +ILSVRC2012_val_00043795.JPEG +ILSVRC2012_val_00025315.JPEG +ILSVRC2012_val_00015040.JPEG +ILSVRC2012_val_00016080.JPEG +ILSVRC2012_val_00022589.JPEG +ILSVRC2012_val_00022597.JPEG +ILSVRC2012_val_00021101.JPEG +ILSVRC2012_val_00002776.JPEG +ILSVRC2012_val_00002544.JPEG +ILSVRC2012_val_00030738.JPEG +ILSVRC2012_val_00034745.JPEG +ILSVRC2012_val_00000355.JPEG +ILSVRC2012_val_00024371.JPEG +ILSVRC2012_val_00017001.JPEG +ILSVRC2012_val_00020376.JPEG +ILSVRC2012_val_00047965.JPEG +ILSVRC2012_val_00046081.JPEG +ILSVRC2012_val_00026656.JPEG +ILSVRC2012_val_00000533.JPEG +ILSVRC2012_val_00016148.JPEG +ILSVRC2012_val_00040222.JPEG +ILSVRC2012_val_00027567.JPEG +ILSVRC2012_val_00003168.JPEG +ILSVRC2012_val_00003428.JPEG +ILSVRC2012_val_00011861.JPEG +ILSVRC2012_val_00017930.JPEG +ILSVRC2012_val_00029399.JPEG +ILSVRC2012_val_00000350.JPEG +ILSVRC2012_val_00032129.JPEG +ILSVRC2012_val_00031047.JPEG +ILSVRC2012_val_00027354.JPEG +ILSVRC2012_val_00002201.JPEG +ILSVRC2012_val_00040174.JPEG +ILSVRC2012_val_00037836.JPEG +ILSVRC2012_val_00037101.JPEG +ILSVRC2012_val_00046725.JPEG +ILSVRC2012_val_00021810.JPEG +ILSVRC2012_val_00022231.JPEG +ILSVRC2012_val_00044520.JPEG +ILSVRC2012_val_00046332.JPEG +ILSVRC2012_val_00001050.JPEG +ILSVRC2012_val_00036945.JPEG +ILSVRC2012_val_00032105.JPEG +ILSVRC2012_val_00006924.JPEG +ILSVRC2012_val_00025564.JPEG +ILSVRC2012_val_00012463.JPEG +ILSVRC2012_val_00035272.JPEG +ILSVRC2012_val_00043478.JPEG +ILSVRC2012_val_00032315.JPEG +ILSVRC2012_val_00022895.JPEG +ILSVRC2012_val_00012099.JPEG +ILSVRC2012_val_00038220.JPEG +ILSVRC2012_val_00037819.JPEG +ILSVRC2012_val_00010846.JPEG +ILSVRC2012_val_00024609.JPEG +ILSVRC2012_val_00004395.JPEG +ILSVRC2012_val_00019227.JPEG +ILSVRC2012_val_00047317.JPEG +ILSVRC2012_val_00004345.JPEG +ILSVRC2012_val_00038402.JPEG +ILSVRC2012_val_00048308.JPEG +ILSVRC2012_val_00043282.JPEG +ILSVRC2012_val_00008913.JPEG +ILSVRC2012_val_00019601.JPEG +ILSVRC2012_val_00037964.JPEG +ILSVRC2012_val_00028937.JPEG +ILSVRC2012_val_00045248.JPEG +ILSVRC2012_val_00019028.JPEG +ILSVRC2012_val_00032004.JPEG +ILSVRC2012_val_00030605.JPEG +ILSVRC2012_val_00022992.JPEG +ILSVRC2012_val_00037659.JPEG +ILSVRC2012_val_00000156.JPEG +ILSVRC2012_val_00025741.JPEG +ILSVRC2012_val_00028333.JPEG +ILSVRC2012_val_00011590.JPEG +ILSVRC2012_val_00010809.JPEG +ILSVRC2012_val_00024455.JPEG +ILSVRC2012_val_00010687.JPEG +ILSVRC2012_val_00034189.JPEG +ILSVRC2012_val_00001857.JPEG +ILSVRC2012_val_00005541.JPEG +ILSVRC2012_val_00005749.JPEG +ILSVRC2012_val_00029658.JPEG +ILSVRC2012_val_00000927.JPEG +ILSVRC2012_val_00017486.JPEG +ILSVRC2012_val_00022250.JPEG +ILSVRC2012_val_00002519.JPEG +ILSVRC2012_val_00016240.JPEG +ILSVRC2012_val_00037746.JPEG +ILSVRC2012_val_00023109.JPEG +ILSVRC2012_val_00021537.JPEG +ILSVRC2012_val_00033137.JPEG +ILSVRC2012_val_00047547.JPEG +ILSVRC2012_val_00029004.JPEG +ILSVRC2012_val_00039427.JPEG +ILSVRC2012_val_00022208.JPEG +ILSVRC2012_val_00005460.JPEG +ILSVRC2012_val_00002653.JPEG +ILSVRC2012_val_00042866.JPEG +ILSVRC2012_val_00028155.JPEG +ILSVRC2012_val_00005570.JPEG +ILSVRC2012_val_00047017.JPEG +ILSVRC2012_val_00020689.JPEG +ILSVRC2012_val_00026170.JPEG +ILSVRC2012_val_00002632.JPEG +ILSVRC2012_val_00017362.JPEG +ILSVRC2012_val_00022859.JPEG +ILSVRC2012_val_00023540.JPEG +ILSVRC2012_val_00012148.JPEG +ILSVRC2012_val_00005630.JPEG +ILSVRC2012_val_00031468.JPEG +ILSVRC2012_val_00026852.JPEG +ILSVRC2012_val_00035849.JPEG +ILSVRC2012_val_00040890.JPEG +ILSVRC2012_val_00038960.JPEG +ILSVRC2012_val_00038107.JPEG +ILSVRC2012_val_00032862.JPEG +ILSVRC2012_val_00045724.JPEG +ILSVRC2012_val_00000756.JPEG +ILSVRC2012_val_00006218.JPEG +ILSVRC2012_val_00032762.JPEG +ILSVRC2012_val_00005937.JPEG +ILSVRC2012_val_00023972.JPEG +ILSVRC2012_val_00036225.JPEG +ILSVRC2012_val_00048892.JPEG +ILSVRC2012_val_00000475.JPEG +ILSVRC2012_val_00042930.JPEG +ILSVRC2012_val_00007759.JPEG +ILSVRC2012_val_00033653.JPEG +ILSVRC2012_val_00001839.JPEG +ILSVRC2012_val_00035020.JPEG +ILSVRC2012_val_00047514.JPEG +ILSVRC2012_val_00042320.JPEG +ILSVRC2012_val_00025587.JPEG +ILSVRC2012_val_00030308.JPEG +ILSVRC2012_val_00046153.JPEG +ILSVRC2012_val_00008152.JPEG +ILSVRC2012_val_00030094.JPEG +ILSVRC2012_val_00005840.JPEG +ILSVRC2012_val_00022277.JPEG +ILSVRC2012_val_00017178.JPEG +ILSVRC2012_val_00008213.JPEG +ILSVRC2012_val_00028163.JPEG +ILSVRC2012_val_00035247.JPEG +ILSVRC2012_val_00013425.JPEG +ILSVRC2012_val_00045463.JPEG +ILSVRC2012_val_00044181.JPEG +ILSVRC2012_val_00045763.JPEG +ILSVRC2012_val_00024670.JPEG +ILSVRC2012_val_00002919.JPEG +ILSVRC2012_val_00046524.JPEG +ILSVRC2012_val_00002308.JPEG +ILSVRC2012_val_00032485.JPEG +ILSVRC2012_val_00003587.JPEG +ILSVRC2012_val_00038132.JPEG +ILSVRC2012_val_00008744.JPEG +ILSVRC2012_val_00040162.JPEG +ILSVRC2012_val_00042851.JPEG +ILSVRC2012_val_00010131.JPEG +ILSVRC2012_val_00005634.JPEG +ILSVRC2012_val_00010350.JPEG +ILSVRC2012_val_00003839.JPEG +ILSVRC2012_val_00042553.JPEG +ILSVRC2012_val_00030256.JPEG +ILSVRC2012_val_00020412.JPEG +ILSVRC2012_val_00013918.JPEG +ILSVRC2012_val_00030064.JPEG +ILSVRC2012_val_00020126.JPEG +ILSVRC2012_val_00048228.JPEG +ILSVRC2012_val_00001057.JPEG +ILSVRC2012_val_00043465.JPEG +ILSVRC2012_val_00011865.JPEG +ILSVRC2012_val_00007322.JPEG +ILSVRC2012_val_00032095.JPEG +ILSVRC2012_val_00005695.JPEG +ILSVRC2012_val_00024544.JPEG +ILSVRC2012_val_00041681.JPEG +ILSVRC2012_val_00018953.JPEG +ILSVRC2012_val_00013998.JPEG +ILSVRC2012_val_00043713.JPEG +ILSVRC2012_val_00046611.JPEG +ILSVRC2012_val_00032514.JPEG +ILSVRC2012_val_00036258.JPEG +ILSVRC2012_val_00029579.JPEG +ILSVRC2012_val_00001465.JPEG +ILSVRC2012_val_00025657.JPEG +ILSVRC2012_val_00039262.JPEG +ILSVRC2012_val_00049591.JPEG +ILSVRC2012_val_00040134.JPEG +ILSVRC2012_val_00032625.JPEG +ILSVRC2012_val_00040958.JPEG +ILSVRC2012_val_00015465.JPEG +ILSVRC2012_val_00015520.JPEG +ILSVRC2012_val_00028644.JPEG +ILSVRC2012_val_00030875.JPEG +ILSVRC2012_val_00037694.JPEG +ILSVRC2012_val_00016264.JPEG +ILSVRC2012_val_00045430.JPEG +ILSVRC2012_val_00014490.JPEG +ILSVRC2012_val_00000743.JPEG +ILSVRC2012_val_00002307.JPEG +ILSVRC2012_val_00047825.JPEG +ILSVRC2012_val_00019072.JPEG +ILSVRC2012_val_00002306.JPEG +ILSVRC2012_val_00038462.JPEG +ILSVRC2012_val_00002780.JPEG +ILSVRC2012_val_00042997.JPEG +ILSVRC2012_val_00016922.JPEG +ILSVRC2012_val_00002883.JPEG +ILSVRC2012_val_00004050.JPEG +ILSVRC2012_val_00047691.JPEG +ILSVRC2012_val_00049395.JPEG +ILSVRC2012_val_00036118.JPEG +ILSVRC2012_val_00037136.JPEG +ILSVRC2012_val_00019575.JPEG +ILSVRC2012_val_00041213.JPEG +ILSVRC2012_val_00041843.JPEG +ILSVRC2012_val_00009905.JPEG +ILSVRC2012_val_00015772.JPEG +ILSVRC2012_val_00031533.JPEG +ILSVRC2012_val_00041091.JPEG +ILSVRC2012_val_00014135.JPEG +ILSVRC2012_val_00003309.JPEG +ILSVRC2012_val_00037958.JPEG +ILSVRC2012_val_00016467.JPEG +ILSVRC2012_val_00045053.JPEG +ILSVRC2012_val_00038758.JPEG +ILSVRC2012_val_00036907.JPEG +ILSVRC2012_val_00022420.JPEG +ILSVRC2012_val_00006488.JPEG +ILSVRC2012_val_00007346.JPEG +ILSVRC2012_val_00016392.JPEG +ILSVRC2012_val_00011360.JPEG +ILSVRC2012_val_00011222.JPEG +ILSVRC2012_val_00004152.JPEG +ILSVRC2012_val_00011655.JPEG +ILSVRC2012_val_00009875.JPEG +ILSVRC2012_val_00028367.JPEG +ILSVRC2012_val_00028252.JPEG +ILSVRC2012_val_00027476.JPEG +ILSVRC2012_val_00033846.JPEG +ILSVRC2012_val_00013033.JPEG +ILSVRC2012_val_00038197.JPEG +ILSVRC2012_val_00048852.JPEG +ILSVRC2012_val_00041002.JPEG +ILSVRC2012_val_00013393.JPEG +ILSVRC2012_val_00035931.JPEG +ILSVRC2012_val_00036174.JPEG +ILSVRC2012_val_00048952.JPEG +ILSVRC2012_val_00030911.JPEG +ILSVRC2012_val_00031274.JPEG +ILSVRC2012_val_00028117.JPEG +ILSVRC2012_val_00047606.JPEG +ILSVRC2012_val_00018830.JPEG +ILSVRC2012_val_00029400.JPEG +ILSVRC2012_val_00045088.JPEG +ILSVRC2012_val_00049979.JPEG +ILSVRC2012_val_00046285.JPEG +ILSVRC2012_val_00008480.JPEG +ILSVRC2012_val_00000991.JPEG +ILSVRC2012_val_00004703.JPEG +ILSVRC2012_val_00019156.JPEG +ILSVRC2012_val_00035242.JPEG +ILSVRC2012_val_00012477.JPEG +ILSVRC2012_val_00008155.JPEG +ILSVRC2012_val_00029221.JPEG +ILSVRC2012_val_00009967.JPEG +ILSVRC2012_val_00003061.JPEG +ILSVRC2012_val_00013749.JPEG +ILSVRC2012_val_00031630.JPEG +ILSVRC2012_val_00037538.JPEG +ILSVRC2012_val_00028070.JPEG +ILSVRC2012_val_00047309.JPEG +ILSVRC2012_val_00019380.JPEG +ILSVRC2012_val_00021557.JPEG +ILSVRC2012_val_00007851.JPEG +ILSVRC2012_val_00006060.JPEG +ILSVRC2012_val_00044137.JPEG +ILSVRC2012_val_00036483.JPEG +ILSVRC2012_val_00016764.JPEG +ILSVRC2012_val_00033415.JPEG +ILSVRC2012_val_00013931.JPEG +ILSVRC2012_val_00004293.JPEG +ILSVRC2012_val_00005162.JPEG +ILSVRC2012_val_00012551.JPEG +ILSVRC2012_val_00047951.JPEG +ILSVRC2012_val_00020049.JPEG +ILSVRC2012_val_00008988.JPEG +ILSVRC2012_val_00019163.JPEG +ILSVRC2012_val_00002103.JPEG +ILSVRC2012_val_00045725.JPEG +ILSVRC2012_val_00013505.JPEG +ILSVRC2012_val_00024425.JPEG +ILSVRC2012_val_00006138.JPEG +ILSVRC2012_val_00043650.JPEG +ILSVRC2012_val_00035683.JPEG +ILSVRC2012_val_00023250.JPEG +ILSVRC2012_val_00024823.JPEG +ILSVRC2012_val_00002089.JPEG +ILSVRC2012_val_00045908.JPEG +ILSVRC2012_val_00031659.JPEG +ILSVRC2012_val_00019345.JPEG +ILSVRC2012_val_00003995.JPEG +ILSVRC2012_val_00034957.JPEG +ILSVRC2012_val_00002386.JPEG +ILSVRC2012_val_00026607.JPEG +ILSVRC2012_val_00008639.JPEG +ILSVRC2012_val_00007973.JPEG +ILSVRC2012_val_00032983.JPEG +ILSVRC2012_val_00020700.JPEG +ILSVRC2012_val_00009241.JPEG +ILSVRC2012_val_00032700.JPEG +ILSVRC2012_val_00005507.JPEG +ILSVRC2012_val_00047855.JPEG +ILSVRC2012_val_00047565.JPEG +ILSVRC2012_val_00012868.JPEG +ILSVRC2012_val_00043074.JPEG +ILSVRC2012_val_00020772.JPEG +ILSVRC2012_val_00048977.JPEG +ILSVRC2012_val_00040853.JPEG +ILSVRC2012_val_00008856.JPEG +ILSVRC2012_val_00038137.JPEG +ILSVRC2012_val_00008577.JPEG +ILSVRC2012_val_00025063.JPEG +ILSVRC2012_val_00020776.JPEG +ILSVRC2012_val_00048703.JPEG +ILSVRC2012_val_00019222.JPEG +ILSVRC2012_val_00011391.JPEG +ILSVRC2012_val_00008659.JPEG +ILSVRC2012_val_00014054.JPEG +ILSVRC2012_val_00024640.JPEG +ILSVRC2012_val_00039121.JPEG +ILSVRC2012_val_00029263.JPEG +ILSVRC2012_val_00049198.JPEG +ILSVRC2012_val_00039055.JPEG +ILSVRC2012_val_00026956.JPEG +ILSVRC2012_val_00033099.JPEG +ILSVRC2012_val_00008719.JPEG +ILSVRC2012_val_00028123.JPEG +ILSVRC2012_val_00011843.JPEG +ILSVRC2012_val_00041862.JPEG +ILSVRC2012_val_00024149.JPEG +ILSVRC2012_val_00034611.JPEG +ILSVRC2012_val_00047471.JPEG +ILSVRC2012_val_00011981.JPEG +ILSVRC2012_val_00020906.JPEG +ILSVRC2012_val_00032103.JPEG +ILSVRC2012_val_00002536.JPEG +ILSVRC2012_val_00042972.JPEG +ILSVRC2012_val_00030881.JPEG +ILSVRC2012_val_00007324.JPEG +ILSVRC2012_val_00035794.JPEG +ILSVRC2012_val_00015771.JPEG +ILSVRC2012_val_00015277.JPEG +ILSVRC2012_val_00031183.JPEG +ILSVRC2012_val_00028776.JPEG +ILSVRC2012_val_00008479.JPEG +ILSVRC2012_val_00025897.JPEG +ILSVRC2012_val_00015059.JPEG +ILSVRC2012_val_00040884.JPEG +ILSVRC2012_val_00026043.JPEG +ILSVRC2012_val_00048130.JPEG +ILSVRC2012_val_00000529.JPEG +ILSVRC2012_val_00038747.JPEG +ILSVRC2012_val_00045152.JPEG +ILSVRC2012_val_00022592.JPEG +ILSVRC2012_val_00022496.JPEG +ILSVRC2012_val_00044743.JPEG +ILSVRC2012_val_00034520.JPEG +ILSVRC2012_val_00029307.JPEG +ILSVRC2012_val_00003958.JPEG +ILSVRC2012_val_00019856.JPEG +ILSVRC2012_val_00041077.JPEG +ILSVRC2012_val_00033832.JPEG +ILSVRC2012_val_00002400.JPEG +ILSVRC2012_val_00011760.JPEG +ILSVRC2012_val_00032296.JPEG +ILSVRC2012_val_00038604.JPEG +ILSVRC2012_val_00046405.JPEG +ILSVRC2012_val_00015149.JPEG +ILSVRC2012_val_00042509.JPEG +ILSVRC2012_val_00049643.JPEG +ILSVRC2012_val_00017092.JPEG +ILSVRC2012_val_00028660.JPEG +ILSVRC2012_val_00024830.JPEG +ILSVRC2012_val_00043697.JPEG +ILSVRC2012_val_00035607.JPEG +ILSVRC2012_val_00033790.JPEG +ILSVRC2012_val_00019238.JPEG +ILSVRC2012_val_00033208.JPEG +ILSVRC2012_val_00042927.JPEG +ILSVRC2012_val_00048822.JPEG +ILSVRC2012_val_00023042.JPEG +ILSVRC2012_val_00029271.JPEG +ILSVRC2012_val_00033482.JPEG +ILSVRC2012_val_00009849.JPEG +ILSVRC2012_val_00042792.JPEG +ILSVRC2012_val_00049425.JPEG +ILSVRC2012_val_00039020.JPEG +ILSVRC2012_val_00010490.JPEG +ILSVRC2012_val_00016720.JPEG +ILSVRC2012_val_00007554.JPEG +ILSVRC2012_val_00048529.JPEG +ILSVRC2012_val_00017891.JPEG +ILSVRC2012_val_00016015.JPEG +ILSVRC2012_val_00013151.JPEG +ILSVRC2012_val_00048648.JPEG +ILSVRC2012_val_00004890.JPEG +ILSVRC2012_val_00040127.JPEG +ILSVRC2012_val_00049159.JPEG +ILSVRC2012_val_00019984.JPEG +ILSVRC2012_val_00025114.JPEG +ILSVRC2012_val_00046208.JPEG +ILSVRC2012_val_00011790.JPEG +ILSVRC2012_val_00007901.JPEG +ILSVRC2012_val_00041572.JPEG +ILSVRC2012_val_00045550.JPEG +ILSVRC2012_val_00011115.JPEG +ILSVRC2012_val_00025566.JPEG +ILSVRC2012_val_00009610.JPEG +ILSVRC2012_val_00026207.JPEG +ILSVRC2012_val_00032441.JPEG +ILSVRC2012_val_00018659.JPEG +ILSVRC2012_val_00018167.JPEG +ILSVRC2012_val_00001920.JPEG +ILSVRC2012_val_00041171.JPEG +ILSVRC2012_val_00017795.JPEG +ILSVRC2012_val_00008371.JPEG +ILSVRC2012_val_00048688.JPEG +ILSVRC2012_val_00034689.JPEG +ILSVRC2012_val_00020218.JPEG +ILSVRC2012_val_00009045.JPEG +ILSVRC2012_val_00015289.JPEG +ILSVRC2012_val_00041347.JPEG +ILSVRC2012_val_00026708.JPEG +ILSVRC2012_val_00025533.JPEG +ILSVRC2012_val_00038134.JPEG +ILSVRC2012_val_00043398.JPEG +ILSVRC2012_val_00014728.JPEG +ILSVRC2012_val_00036639.JPEG +ILSVRC2012_val_00022330.JPEG +ILSVRC2012_val_00034615.JPEG +ILSVRC2012_val_00019758.JPEG +ILSVRC2012_val_00032140.JPEG +ILSVRC2012_val_00006486.JPEG +ILSVRC2012_val_00045767.JPEG +ILSVRC2012_val_00019655.JPEG +ILSVRC2012_val_00048700.JPEG +ILSVRC2012_val_00000271.JPEG +ILSVRC2012_val_00022462.JPEG +ILSVRC2012_val_00032627.JPEG +ILSVRC2012_val_00035407.JPEG +ILSVRC2012_val_00039633.JPEG +ILSVRC2012_val_00004198.JPEG +ILSVRC2012_val_00038016.JPEG +ILSVRC2012_val_00020756.JPEG +ILSVRC2012_val_00024042.JPEG +ILSVRC2012_val_00014271.JPEG +ILSVRC2012_val_00042143.JPEG +ILSVRC2012_val_00004550.JPEG +ILSVRC2012_val_00034427.JPEG +ILSVRC2012_val_00027122.JPEG +ILSVRC2012_val_00019177.JPEG +ILSVRC2012_val_00039071.JPEG +ILSVRC2012_val_00030257.JPEG +ILSVRC2012_val_00048385.JPEG +ILSVRC2012_val_00035943.JPEG +ILSVRC2012_val_00035051.JPEG +ILSVRC2012_val_00045030.JPEG +ILSVRC2012_val_00036665.JPEG +ILSVRC2012_val_00031717.JPEG +ILSVRC2012_val_00048803.JPEG +ILSVRC2012_val_00037496.JPEG +ILSVRC2012_val_00042947.JPEG +ILSVRC2012_val_00004996.JPEG +ILSVRC2012_val_00015792.JPEG +ILSVRC2012_val_00020440.JPEG +ILSVRC2012_val_00032131.JPEG +ILSVRC2012_val_00014082.JPEG +ILSVRC2012_val_00008541.JPEG +ILSVRC2012_val_00010656.JPEG +ILSVRC2012_val_00042816.JPEG +ILSVRC2012_val_00044235.JPEG +ILSVRC2012_val_00001677.JPEG +ILSVRC2012_val_00049380.JPEG +ILSVRC2012_val_00011331.JPEG +ILSVRC2012_val_00003250.JPEG +ILSVRC2012_val_00002093.JPEG +ILSVRC2012_val_00039968.JPEG +ILSVRC2012_val_00036862.JPEG +ILSVRC2012_val_00003271.JPEG +ILSVRC2012_val_00010363.JPEG +ILSVRC2012_val_00011065.JPEG +ILSVRC2012_val_00044252.JPEG +ILSVRC2012_val_00027441.JPEG +ILSVRC2012_val_00041536.JPEG +ILSVRC2012_val_00038055.JPEG +ILSVRC2012_val_00016065.JPEG +ILSVRC2012_val_00014448.JPEG +ILSVRC2012_val_00018300.JPEG +ILSVRC2012_val_00012362.JPEG +ILSVRC2012_val_00014531.JPEG +ILSVRC2012_val_00045987.JPEG +ILSVRC2012_val_00008507.JPEG +ILSVRC2012_val_00035787.JPEG +ILSVRC2012_val_00047898.JPEG +ILSVRC2012_val_00034565.JPEG +ILSVRC2012_val_00013483.JPEG +ILSVRC2012_val_00011965.JPEG +ILSVRC2012_val_00016313.JPEG +ILSVRC2012_val_00049255.JPEG +ILSVRC2012_val_00035663.JPEG +ILSVRC2012_val_00005235.JPEG +ILSVRC2012_val_00017694.JPEG +ILSVRC2012_val_00029142.JPEG +ILSVRC2012_val_00046709.JPEG +ILSVRC2012_val_00035652.JPEG +ILSVRC2012_val_00000064.JPEG +ILSVRC2012_val_00014071.JPEG +ILSVRC2012_val_00040381.JPEG +ILSVRC2012_val_00037088.JPEG +ILSVRC2012_val_00003202.JPEG +ILSVRC2012_val_00012863.JPEG +ILSVRC2012_val_00024408.JPEG +ILSVRC2012_val_00030827.JPEG +ILSVRC2012_val_00020602.JPEG +ILSVRC2012_val_00028839.JPEG +ILSVRC2012_val_00011966.JPEG +ILSVRC2012_val_00010521.JPEG +ILSVRC2012_val_00020467.JPEG +ILSVRC2012_val_00048823.JPEG +ILSVRC2012_val_00039047.JPEG +ILSVRC2012_val_00018855.JPEG +ILSVRC2012_val_00039687.JPEG +ILSVRC2012_val_00038864.JPEG +ILSVRC2012_val_00021511.JPEG +ILSVRC2012_val_00013243.JPEG +ILSVRC2012_val_00008668.JPEG +ILSVRC2012_val_00023300.JPEG +ILSVRC2012_val_00002959.JPEG +ILSVRC2012_val_00045028.JPEG +ILSVRC2012_val_00039313.JPEG +ILSVRC2012_val_00021724.JPEG +ILSVRC2012_val_00035742.JPEG +ILSVRC2012_val_00018341.JPEG +ILSVRC2012_val_00002594.JPEG +ILSVRC2012_val_00012435.JPEG +ILSVRC2012_val_00045098.JPEG +ILSVRC2012_val_00028664.JPEG +ILSVRC2012_val_00016549.JPEG +ILSVRC2012_val_00042229.JPEG +ILSVRC2012_val_00024197.JPEG +ILSVRC2012_val_00005416.JPEG +ILSVRC2012_val_00022368.JPEG +ILSVRC2012_val_00002096.JPEG +ILSVRC2012_val_00049648.JPEG +ILSVRC2012_val_00020529.JPEG +ILSVRC2012_val_00017845.JPEG +ILSVRC2012_val_00047113.JPEG +ILSVRC2012_val_00009877.JPEG +ILSVRC2012_val_00002483.JPEG +ILSVRC2012_val_00002831.JPEG +ILSVRC2012_val_00006568.JPEG +ILSVRC2012_val_00002169.JPEG +ILSVRC2012_val_00027812.JPEG +ILSVRC2012_val_00046603.JPEG +ILSVRC2012_val_00018295.JPEG +ILSVRC2012_val_00009389.JPEG +ILSVRC2012_val_00048649.JPEG +ILSVRC2012_val_00021357.JPEG +ILSVRC2012_val_00040190.JPEG +ILSVRC2012_val_00006610.JPEG +ILSVRC2012_val_00003903.JPEG +ILSVRC2012_val_00036455.JPEG +ILSVRC2012_val_00028314.JPEG +ILSVRC2012_val_00000939.JPEG +ILSVRC2012_val_00026815.JPEG +ILSVRC2012_val_00043592.JPEG +ILSVRC2012_val_00034770.JPEG +ILSVRC2012_val_00025436.JPEG +ILSVRC2012_val_00001508.JPEG +ILSVRC2012_val_00017608.JPEG +ILSVRC2012_val_00033371.JPEG +ILSVRC2012_val_00006660.JPEG +ILSVRC2012_val_00002475.JPEG +ILSVRC2012_val_00003979.JPEG +ILSVRC2012_val_00007582.JPEG +ILSVRC2012_val_00015809.JPEG +ILSVRC2012_val_00016929.JPEG +ILSVRC2012_val_00012636.JPEG +ILSVRC2012_val_00006796.JPEG +ILSVRC2012_val_00014756.JPEG +ILSVRC2012_val_00045859.JPEG +ILSVRC2012_val_00008067.JPEG +ILSVRC2012_val_00043924.JPEG +ILSVRC2012_val_00043566.JPEG +ILSVRC2012_val_00035598.JPEG +ILSVRC2012_val_00011476.JPEG +ILSVRC2012_val_00030448.JPEG +ILSVRC2012_val_00033252.JPEG +ILSVRC2012_val_00023921.JPEG +ILSVRC2012_val_00043464.JPEG +ILSVRC2012_val_00017705.JPEG +ILSVRC2012_val_00002430.JPEG +ILSVRC2012_val_00013144.JPEG +ILSVRC2012_val_00046957.JPEG +ILSVRC2012_val_00003657.JPEG +ILSVRC2012_val_00009191.JPEG +ILSVRC2012_val_00041021.JPEG +ILSVRC2012_val_00010893.JPEG +ILSVRC2012_val_00041492.JPEG +ILSVRC2012_val_00037181.JPEG +ILSVRC2012_val_00018399.JPEG +ILSVRC2012_val_00002871.JPEG +ILSVRC2012_val_00017934.JPEG +ILSVRC2012_val_00044354.JPEG +ILSVRC2012_val_00048738.JPEG +ILSVRC2012_val_00032995.JPEG +ILSVRC2012_val_00026662.JPEG +ILSVRC2012_val_00028621.JPEG +ILSVRC2012_val_00022416.JPEG +ILSVRC2012_val_00001122.JPEG +ILSVRC2012_val_00047103.JPEG +ILSVRC2012_val_00013296.JPEG +ILSVRC2012_val_00048553.JPEG +ILSVRC2012_val_00036223.JPEG +ILSVRC2012_val_00040931.JPEG +ILSVRC2012_val_00040780.JPEG +ILSVRC2012_val_00011744.JPEG +ILSVRC2012_val_00018607.JPEG +ILSVRC2012_val_00045840.JPEG +ILSVRC2012_val_00035515.JPEG +ILSVRC2012_val_00015845.JPEG +ILSVRC2012_val_00012866.JPEG +ILSVRC2012_val_00009160.JPEG +ILSVRC2012_val_00036299.JPEG +ILSVRC2012_val_00016127.JPEG +ILSVRC2012_val_00035042.JPEG +ILSVRC2012_val_00013952.JPEG +ILSVRC2012_val_00035681.JPEG +ILSVRC2012_val_00018902.JPEG +ILSVRC2012_val_00001928.JPEG +ILSVRC2012_val_00020534.JPEG +ILSVRC2012_val_00026825.JPEG +ILSVRC2012_val_00024390.JPEG +ILSVRC2012_val_00035400.JPEG +ILSVRC2012_val_00005350.JPEG +ILSVRC2012_val_00042218.JPEG +ILSVRC2012_val_00028305.JPEG +ILSVRC2012_val_00048533.JPEG +ILSVRC2012_val_00017773.JPEG +ILSVRC2012_val_00002682.JPEG +ILSVRC2012_val_00033249.JPEG +ILSVRC2012_val_00011750.JPEG +ILSVRC2012_val_00034409.JPEG +ILSVRC2012_val_00025482.JPEG +ILSVRC2012_val_00006051.JPEG +ILSVRC2012_val_00003953.JPEG +ILSVRC2012_val_00003355.JPEG +ILSVRC2012_val_00039970.JPEG +ILSVRC2012_val_00020045.JPEG +ILSVRC2012_val_00027306.JPEG +ILSVRC2012_val_00019551.JPEG +ILSVRC2012_val_00041592.JPEG +ILSVRC2012_val_00041928.JPEG +ILSVRC2012_val_00041135.JPEG +ILSVRC2012_val_00030093.JPEG +ILSVRC2012_val_00007727.JPEG +ILSVRC2012_val_00013486.JPEG +ILSVRC2012_val_00027470.JPEG +ILSVRC2012_val_00020454.JPEG +ILSVRC2012_val_00036218.JPEG +ILSVRC2012_val_00019828.JPEG +ILSVRC2012_val_00033342.JPEG +ILSVRC2012_val_00041935.JPEG +ILSVRC2012_val_00038898.JPEG +ILSVRC2012_val_00014670.JPEG +ILSVRC2012_val_00012320.JPEG +ILSVRC2012_val_00021553.JPEG +ILSVRC2012_val_00047647.JPEG +ILSVRC2012_val_00026646.JPEG +ILSVRC2012_val_00021094.JPEG +ILSVRC2012_val_00010627.JPEG +ILSVRC2012_val_00002100.JPEG +ILSVRC2012_val_00048098.JPEG +ILSVRC2012_val_00014584.JPEG +ILSVRC2012_val_00021875.JPEG +ILSVRC2012_val_00009189.JPEG +ILSVRC2012_val_00049161.JPEG +ILSVRC2012_val_00042937.JPEG +ILSVRC2012_val_00026346.JPEG +ILSVRC2012_val_00018522.JPEG +ILSVRC2012_val_00031552.JPEG +ILSVRC2012_val_00040727.JPEG +ILSVRC2012_val_00048157.JPEG +ILSVRC2012_val_00009411.JPEG +ILSVRC2012_val_00036844.JPEG +ILSVRC2012_val_00025463.JPEG +ILSVRC2012_val_00006942.JPEG +ILSVRC2012_val_00015545.JPEG +ILSVRC2012_val_00022865.JPEG +ILSVRC2012_val_00031888.JPEG +ILSVRC2012_val_00023278.JPEG +ILSVRC2012_val_00027084.JPEG +ILSVRC2012_val_00007051.JPEG +ILSVRC2012_val_00047956.JPEG +ILSVRC2012_val_00010808.JPEG +ILSVRC2012_val_00015412.JPEG +ILSVRC2012_val_00035912.JPEG +ILSVRC2012_val_00048499.JPEG +ILSVRC2012_val_00007502.JPEG +ILSVRC2012_val_00030389.JPEG +ILSVRC2012_val_00015723.JPEG +ILSVRC2012_val_00045788.JPEG +ILSVRC2012_val_00006797.JPEG +ILSVRC2012_val_00031077.JPEG +ILSVRC2012_val_00012240.JPEG +ILSVRC2012_val_00018650.JPEG +ILSVRC2012_val_00002292.JPEG +ILSVRC2012_val_00006352.JPEG +ILSVRC2012_val_00028561.JPEG +ILSVRC2012_val_00007654.JPEG +ILSVRC2012_val_00024628.JPEG +ILSVRC2012_val_00014159.JPEG +ILSVRC2012_val_00009901.JPEG +ILSVRC2012_val_00011253.JPEG +ILSVRC2012_val_00009785.JPEG +ILSVRC2012_val_00015680.JPEG +ILSVRC2012_val_00012673.JPEG +ILSVRC2012_val_00017751.JPEG +ILSVRC2012_val_00017695.JPEG +ILSVRC2012_val_00045978.JPEG +ILSVRC2012_val_00003098.JPEG +ILSVRC2012_val_00038844.JPEG +ILSVRC2012_val_00007997.JPEG +ILSVRC2012_val_00010210.JPEG +ILSVRC2012_val_00013626.JPEG +ILSVRC2012_val_00036256.JPEG +ILSVRC2012_val_00019421.JPEG +ILSVRC2012_val_00042756.JPEG +ILSVRC2012_val_00036780.JPEG +ILSVRC2012_val_00049405.JPEG +ILSVRC2012_val_00004052.JPEG +ILSVRC2012_val_00032203.JPEG +ILSVRC2012_val_00029416.JPEG +ILSVRC2012_val_00042738.JPEG +ILSVRC2012_val_00037080.JPEG +ILSVRC2012_val_00034323.JPEG +ILSVRC2012_val_00025471.JPEG +ILSVRC2012_val_00020181.JPEG +ILSVRC2012_val_00009028.JPEG +ILSVRC2012_val_00014014.JPEG +ILSVRC2012_val_00024754.JPEG +ILSVRC2012_val_00042010.JPEG +ILSVRC2012_val_00002183.JPEG +ILSVRC2012_val_00043121.JPEG +ILSVRC2012_val_00008583.JPEG +ILSVRC2012_val_00048876.JPEG +ILSVRC2012_val_00022088.JPEG +ILSVRC2012_val_00022229.JPEG +ILSVRC2012_val_00045498.JPEG +ILSVRC2012_val_00040461.JPEG +ILSVRC2012_val_00005642.JPEG +ILSVRC2012_val_00040335.JPEG +ILSVRC2012_val_00049148.JPEG +ILSVRC2012_val_00022899.JPEG +ILSVRC2012_val_00019453.JPEG +ILSVRC2012_val_00036950.JPEG +ILSVRC2012_val_00012715.JPEG +ILSVRC2012_val_00035640.JPEG +ILSVRC2012_val_00030699.JPEG +ILSVRC2012_val_00023621.JPEG +ILSVRC2012_val_00014399.JPEG +ILSVRC2012_val_00043517.JPEG +ILSVRC2012_val_00022839.JPEG +ILSVRC2012_val_00016554.JPEG +ILSVRC2012_val_00030253.JPEG +ILSVRC2012_val_00006407.JPEG +ILSVRC2012_val_00038893.JPEG +ILSVRC2012_val_00017657.JPEG +ILSVRC2012_val_00020963.JPEG +ILSVRC2012_val_00000214.JPEG +ILSVRC2012_val_00016120.JPEG +ILSVRC2012_val_00013366.JPEG +ILSVRC2012_val_00004528.JPEG +ILSVRC2012_val_00031793.JPEG +ILSVRC2012_val_00036181.JPEG +ILSVRC2012_val_00022251.JPEG +ILSVRC2012_val_00001243.JPEG +ILSVRC2012_val_00049614.JPEG +ILSVRC2012_val_00025892.JPEG +ILSVRC2012_val_00049178.JPEG +ILSVRC2012_val_00015559.JPEG +ILSVRC2012_val_00004886.JPEG +ILSVRC2012_val_00032770.JPEG +ILSVRC2012_val_00002329.JPEG +ILSVRC2012_val_00014312.JPEG +ILSVRC2012_val_00009080.JPEG +ILSVRC2012_val_00023172.JPEG +ILSVRC2012_val_00020222.JPEG +ILSVRC2012_val_00004731.JPEG +ILSVRC2012_val_00022875.JPEG +ILSVRC2012_val_00049660.JPEG +ILSVRC2012_val_00047241.JPEG +ILSVRC2012_val_00035907.JPEG +ILSVRC2012_val_00048044.JPEG +ILSVRC2012_val_00020309.JPEG +ILSVRC2012_val_00038000.JPEG +ILSVRC2012_val_00019572.JPEG +ILSVRC2012_val_00017969.JPEG +ILSVRC2012_val_00010032.JPEG +ILSVRC2012_val_00004989.JPEG +ILSVRC2012_val_00031061.JPEG +ILSVRC2012_val_00046862.JPEG +ILSVRC2012_val_00016226.JPEG +ILSVRC2012_val_00010398.JPEG +ILSVRC2012_val_00049439.JPEG +ILSVRC2012_val_00038870.JPEG +ILSVRC2012_val_00015639.JPEG +ILSVRC2012_val_00022987.JPEG +ILSVRC2012_val_00044054.JPEG +ILSVRC2012_val_00037555.JPEG +ILSVRC2012_val_00007514.JPEG +ILSVRC2012_val_00016663.JPEG +ILSVRC2012_val_00036758.JPEG +ILSVRC2012_val_00032834.JPEG +ILSVRC2012_val_00033200.JPEG +ILSVRC2012_val_00029042.JPEG +ILSVRC2012_val_00037804.JPEG +ILSVRC2012_val_00048264.JPEG +ILSVRC2012_val_00045954.JPEG +ILSVRC2012_val_00002003.JPEG +ILSVRC2012_val_00030895.JPEG +ILSVRC2012_val_00006624.JPEG +ILSVRC2012_val_00025492.JPEG +ILSVRC2012_val_00007371.JPEG +ILSVRC2012_val_00005608.JPEG +ILSVRC2012_val_00020167.JPEG +ILSVRC2012_val_00026567.JPEG +ILSVRC2012_val_00037416.JPEG +ILSVRC2012_val_00034306.JPEG +ILSVRC2012_val_00034688.JPEG +ILSVRC2012_val_00041551.JPEG +ILSVRC2012_val_00041966.JPEG +ILSVRC2012_val_00026370.JPEG +ILSVRC2012_val_00020804.JPEG +ILSVRC2012_val_00018845.JPEG +ILSVRC2012_val_00012052.JPEG +ILSVRC2012_val_00026985.JPEG +ILSVRC2012_val_00007615.JPEG +ILSVRC2012_val_00027589.JPEG +ILSVRC2012_val_00031696.JPEG +ILSVRC2012_val_00008662.JPEG +ILSVRC2012_val_00041147.JPEG +ILSVRC2012_val_00043429.JPEG +ILSVRC2012_val_00001089.JPEG +ILSVRC2012_val_00041460.JPEG +ILSVRC2012_val_00041101.JPEG +ILSVRC2012_val_00043554.JPEG +ILSVRC2012_val_00014388.JPEG +ILSVRC2012_val_00016486.JPEG +ILSVRC2012_val_00015673.JPEG +ILSVRC2012_val_00033281.JPEG +ILSVRC2012_val_00045916.JPEG +ILSVRC2012_val_00023388.JPEG +ILSVRC2012_val_00032190.JPEG +ILSVRC2012_val_00010227.JPEG +ILSVRC2012_val_00021052.JPEG +ILSVRC2012_val_00000056.JPEG +ILSVRC2012_val_00031921.JPEG +ILSVRC2012_val_00016209.JPEG +ILSVRC2012_val_00001600.JPEG +ILSVRC2012_val_00049397.JPEG +ILSVRC2012_val_00042665.JPEG +ILSVRC2012_val_00045917.JPEG +ILSVRC2012_val_00006931.JPEG +ILSVRC2012_val_00021900.JPEG +ILSVRC2012_val_00004526.JPEG +ILSVRC2012_val_00010975.JPEG +ILSVRC2012_val_00006573.JPEG +ILSVRC2012_val_00034883.JPEG +ILSVRC2012_val_00032120.JPEG +ILSVRC2012_val_00009606.JPEG +ILSVRC2012_val_00039745.JPEG +ILSVRC2012_val_00036624.JPEG +ILSVRC2012_val_00034139.JPEG +ILSVRC2012_val_00026600.JPEG +ILSVRC2012_val_00035856.JPEG +ILSVRC2012_val_00039822.JPEG +ILSVRC2012_val_00025545.JPEG +ILSVRC2012_val_00011946.JPEG +ILSVRC2012_val_00028736.JPEG +ILSVRC2012_val_00030298.JPEG +ILSVRC2012_val_00024148.JPEG +ILSVRC2012_val_00011624.JPEG +ILSVRC2012_val_00035100.JPEG +ILSVRC2012_val_00019330.JPEG +ILSVRC2012_val_00045205.JPEG +ILSVRC2012_val_00024442.JPEG +ILSVRC2012_val_00049086.JPEG +ILSVRC2012_val_00017679.JPEG +ILSVRC2012_val_00039384.JPEG +ILSVRC2012_val_00032356.JPEG +ILSVRC2012_val_00002679.JPEG +ILSVRC2012_val_00044150.JPEG +ILSVRC2012_val_00044301.JPEG +ILSVRC2012_val_00044703.JPEG +ILSVRC2012_val_00040281.JPEG +ILSVRC2012_val_00035360.JPEG +ILSVRC2012_val_00002826.JPEG +ILSVRC2012_val_00011048.JPEG +ILSVRC2012_val_00044247.JPEG +ILSVRC2012_val_00033556.JPEG +ILSVRC2012_val_00019066.JPEG +ILSVRC2012_val_00027208.JPEG +ILSVRC2012_val_00041850.JPEG +ILSVRC2012_val_00028771.JPEG +ILSVRC2012_val_00037561.JPEG +ILSVRC2012_val_00018385.JPEG +ILSVRC2012_val_00000474.JPEG +ILSVRC2012_val_00025888.JPEG +ILSVRC2012_val_00006282.JPEG +ILSVRC2012_val_00018780.JPEG +ILSVRC2012_val_00003970.JPEG +ILSVRC2012_val_00015294.JPEG +ILSVRC2012_val_00036770.JPEG +ILSVRC2012_val_00039511.JPEG +ILSVRC2012_val_00042783.JPEG +ILSVRC2012_val_00021458.JPEG +ILSVRC2012_val_00005289.JPEG +ILSVRC2012_val_00037116.JPEG +ILSVRC2012_val_00039495.JPEG +ILSVRC2012_val_00046083.JPEG +ILSVRC2012_val_00034938.JPEG +ILSVRC2012_val_00012282.JPEG +ILSVRC2012_val_00032655.JPEG +ILSVRC2012_val_00005677.JPEG +ILSVRC2012_val_00014735.JPEG +ILSVRC2012_val_00024027.JPEG +ILSVRC2012_val_00034736.JPEG +ILSVRC2012_val_00040671.JPEG +ILSVRC2012_val_00045781.JPEG +ILSVRC2012_val_00019205.JPEG +ILSVRC2012_val_00047756.JPEG +ILSVRC2012_val_00034827.JPEG +ILSVRC2012_val_00042515.JPEG +ILSVRC2012_val_00015061.JPEG +ILSVRC2012_val_00007267.JPEG +ILSVRC2012_val_00021405.JPEG +ILSVRC2012_val_00034718.JPEG +ILSVRC2012_val_00003662.JPEG +ILSVRC2012_val_00036137.JPEG +ILSVRC2012_val_00002856.JPEG +ILSVRC2012_val_00021647.JPEG +ILSVRC2012_val_00028606.JPEG +ILSVRC2012_val_00029886.JPEG +ILSVRC2012_val_00028169.JPEG +ILSVRC2012_val_00047804.JPEG +ILSVRC2012_val_00046069.JPEG +ILSVRC2012_val_00038345.JPEG +ILSVRC2012_val_00025485.JPEG +ILSVRC2012_val_00009950.JPEG +ILSVRC2012_val_00036214.JPEG +ILSVRC2012_val_00042459.JPEG +ILSVRC2012_val_00017378.JPEG +ILSVRC2012_val_00016437.JPEG +ILSVRC2012_val_00041304.JPEG +ILSVRC2012_val_00026942.JPEG +ILSVRC2012_val_00019682.JPEG +ILSVRC2012_val_00023072.JPEG +ILSVRC2012_val_00038500.JPEG +ILSVRC2012_val_00036677.JPEG +ILSVRC2012_val_00042950.JPEG +ILSVRC2012_val_00038601.JPEG +ILSVRC2012_val_00044350.JPEG +ILSVRC2012_val_00012539.JPEG +ILSVRC2012_val_00038743.JPEG +ILSVRC2012_val_00000332.JPEG +ILSVRC2012_val_00018147.JPEG +ILSVRC2012_val_00025257.JPEG +ILSVRC2012_val_00020358.JPEG +ILSVRC2012_val_00028135.JPEG +ILSVRC2012_val_00032194.JPEG +ILSVRC2012_val_00038078.JPEG +ILSVRC2012_val_00043137.JPEG +ILSVRC2012_val_00014555.JPEG +ILSVRC2012_val_00015114.JPEG +ILSVRC2012_val_00033450.JPEG +ILSVRC2012_val_00030624.JPEG +ILSVRC2012_val_00033839.JPEG +ILSVRC2012_val_00014992.JPEG +ILSVRC2012_val_00013794.JPEG +ILSVRC2012_val_00029575.JPEG +ILSVRC2012_val_00005414.JPEG +ILSVRC2012_val_00036124.JPEG +ILSVRC2012_val_00019736.JPEG +ILSVRC2012_val_00013671.JPEG +ILSVRC2012_val_00017682.JPEG +ILSVRC2012_val_00000386.JPEG +ILSVRC2012_val_00009750.JPEG +ILSVRC2012_val_00007126.JPEG +ILSVRC2012_val_00043183.JPEG +ILSVRC2012_val_00000129.JPEG +ILSVRC2012_val_00029149.JPEG +ILSVRC2012_val_00030337.JPEG +ILSVRC2012_val_00003994.JPEG +ILSVRC2012_val_00010641.JPEG +ILSVRC2012_val_00003724.JPEG +ILSVRC2012_val_00005869.JPEG +ILSVRC2012_val_00026865.JPEG +ILSVRC2012_val_00003268.JPEG +ILSVRC2012_val_00012836.JPEG +ILSVRC2012_val_00007036.JPEG +ILSVRC2012_val_00017849.JPEG +ILSVRC2012_val_00040650.JPEG +ILSVRC2012_val_00001700.JPEG +ILSVRC2012_val_00028207.JPEG +ILSVRC2012_val_00047630.JPEG +ILSVRC2012_val_00009296.JPEG +ILSVRC2012_val_00016094.JPEG +ILSVRC2012_val_00013982.JPEG +ILSVRC2012_val_00039923.JPEG +ILSVRC2012_val_00008717.JPEG +ILSVRC2012_val_00045220.JPEG +ILSVRC2012_val_00021786.JPEG +ILSVRC2012_val_00012607.JPEG +ILSVRC2012_val_00004361.JPEG +ILSVRC2012_val_00038176.JPEG +ILSVRC2012_val_00012939.JPEG +ILSVRC2012_val_00008075.JPEG +ILSVRC2012_val_00018009.JPEG +ILSVRC2012_val_00049520.JPEG +ILSVRC2012_val_00011023.JPEG +ILSVRC2012_val_00014212.JPEG +ILSVRC2012_val_00026140.JPEG +ILSVRC2012_val_00022816.JPEG +ILSVRC2012_val_00007918.JPEG +ILSVRC2012_val_00002947.JPEG +ILSVRC2012_val_00040138.JPEG +ILSVRC2012_val_00015292.JPEG +ILSVRC2012_val_00031952.JPEG +ILSVRC2012_val_00047595.JPEG +ILSVRC2012_val_00000985.JPEG +ILSVRC2012_val_00000814.JPEG +ILSVRC2012_val_00047175.JPEG +ILSVRC2012_val_00034220.JPEG +ILSVRC2012_val_00032733.JPEG +ILSVRC2012_val_00012293.JPEG +ILSVRC2012_val_00005306.JPEG +ILSVRC2012_val_00037049.JPEG +ILSVRC2012_val_00043913.JPEG +ILSVRC2012_val_00038622.JPEG +ILSVRC2012_val_00033495.JPEG +ILSVRC2012_val_00008254.JPEG +ILSVRC2012_val_00045683.JPEG +ILSVRC2012_val_00006659.JPEG +ILSVRC2012_val_00022537.JPEG +ILSVRC2012_val_00013226.JPEG +ILSVRC2012_val_00028726.JPEG +ILSVRC2012_val_00014015.JPEG +ILSVRC2012_val_00029321.JPEG +ILSVRC2012_val_00000086.JPEG +ILSVRC2012_val_00021922.JPEG +ILSVRC2012_val_00006030.JPEG +ILSVRC2012_val_00016381.JPEG +ILSVRC2012_val_00033329.JPEG +ILSVRC2012_val_00013165.JPEG +ILSVRC2012_val_00007154.JPEG +ILSVRC2012_val_00020356.JPEG +ILSVRC2012_val_00036756.JPEG +ILSVRC2012_val_00009178.JPEG +ILSVRC2012_val_00010716.JPEG +ILSVRC2012_val_00029184.JPEG +ILSVRC2012_val_00026863.JPEG +ILSVRC2012_val_00013206.JPEG +ILSVRC2012_val_00027247.JPEG +ILSVRC2012_val_00003971.JPEG +ILSVRC2012_val_00018591.JPEG +ILSVRC2012_val_00021649.JPEG +ILSVRC2012_val_00029629.JPEG +ILSVRC2012_val_00033631.JPEG +ILSVRC2012_val_00037862.JPEG +ILSVRC2012_val_00019484.JPEG +ILSVRC2012_val_00025015.JPEG +ILSVRC2012_val_00013061.JPEG +ILSVRC2012_val_00035167.JPEG +ILSVRC2012_val_00000400.JPEG +ILSVRC2012_val_00015421.JPEG +ILSVRC2012_val_00039408.JPEG +ILSVRC2012_val_00048896.JPEG +ILSVRC2012_val_00004736.JPEG +ILSVRC2012_val_00047215.JPEG +ILSVRC2012_val_00038868.JPEG +ILSVRC2012_val_00031639.JPEG +ILSVRC2012_val_00023487.JPEG +ILSVRC2012_val_00019449.JPEG +ILSVRC2012_val_00018767.JPEG +ILSVRC2012_val_00046022.JPEG +ILSVRC2012_val_00026512.JPEG +ILSVRC2012_val_00046621.JPEG +ILSVRC2012_val_00022283.JPEG +ILSVRC2012_val_00007804.JPEG +ILSVRC2012_val_00009364.JPEG +ILSVRC2012_val_00022912.JPEG +ILSVRC2012_val_00000928.JPEG +ILSVRC2012_val_00024604.JPEG +ILSVRC2012_val_00030035.JPEG +ILSVRC2012_val_00037444.JPEG +ILSVRC2012_val_00022365.JPEG +ILSVRC2012_val_00022269.JPEG +ILSVRC2012_val_00013882.JPEG +ILSVRC2012_val_00016490.JPEG +ILSVRC2012_val_00011472.JPEG +ILSVRC2012_val_00049433.JPEG +ILSVRC2012_val_00015918.JPEG +ILSVRC2012_val_00005991.JPEG +ILSVRC2012_val_00031214.JPEG +ILSVRC2012_val_00006422.JPEG +ILSVRC2012_val_00045370.JPEG +ILSVRC2012_val_00045870.JPEG +ILSVRC2012_val_00000724.JPEG +ILSVRC2012_val_00039926.JPEG +ILSVRC2012_val_00024500.JPEG +ILSVRC2012_val_00002101.JPEG +ILSVRC2012_val_00029812.JPEG +ILSVRC2012_val_00015610.JPEG +ILSVRC2012_val_00049246.JPEG +ILSVRC2012_val_00047001.JPEG +ILSVRC2012_val_00030037.JPEG +ILSVRC2012_val_00007608.JPEG +ILSVRC2012_val_00021615.JPEG +ILSVRC2012_val_00019751.JPEG +ILSVRC2012_val_00032910.JPEG +ILSVRC2012_val_00044803.JPEG +ILSVRC2012_val_00036367.JPEG +ILSVRC2012_val_00023207.JPEG +ILSVRC2012_val_00009318.JPEG +ILSVRC2012_val_00031114.JPEG +ILSVRC2012_val_00047589.JPEG +ILSVRC2012_val_00004136.JPEG +ILSVRC2012_val_00043823.JPEG +ILSVRC2012_val_00027106.JPEG +ILSVRC2012_val_00033686.JPEG +ILSVRC2012_val_00045409.JPEG +ILSVRC2012_val_00027909.JPEG +ILSVRC2012_val_00040572.JPEG +ILSVRC2012_val_00034483.JPEG +ILSVRC2012_val_00046956.JPEG +ILSVRC2012_val_00039190.JPEG +ILSVRC2012_val_00018068.JPEG +ILSVRC2012_val_00037952.JPEG +ILSVRC2012_val_00026652.JPEG +ILSVRC2012_val_00034494.JPEG +ILSVRC2012_val_00002133.JPEG +ILSVRC2012_val_00000508.JPEG +ILSVRC2012_val_00000051.JPEG +ILSVRC2012_val_00005187.JPEG +ILSVRC2012_val_00033221.JPEG +ILSVRC2012_val_00005072.JPEG +ILSVRC2012_val_00030476.JPEG +ILSVRC2012_val_00047496.JPEG +ILSVRC2012_val_00039816.JPEG +ILSVRC2012_val_00031849.JPEG +ILSVRC2012_val_00030715.JPEG +ILSVRC2012_val_00036409.JPEG +ILSVRC2012_val_00026523.JPEG +ILSVRC2012_val_00046349.JPEG +ILSVRC2012_val_00039622.JPEG +ILSVRC2012_val_00025192.JPEG +ILSVRC2012_val_00036702.JPEG +ILSVRC2012_val_00012329.JPEG +ILSVRC2012_val_00037844.JPEG +ILSVRC2012_val_00005323.JPEG +ILSVRC2012_val_00020824.JPEG +ILSVRC2012_val_00042283.JPEG +ILSVRC2012_val_00037259.JPEG +ILSVRC2012_val_00012772.JPEG +ILSVRC2012_val_00048844.JPEG +ILSVRC2012_val_00017697.JPEG +ILSVRC2012_val_00012992.JPEG +ILSVRC2012_val_00010104.JPEG +ILSVRC2012_val_00029937.JPEG +ILSVRC2012_val_00022953.JPEG +ILSVRC2012_val_00002114.JPEG +ILSVRC2012_val_00037442.JPEG +ILSVRC2012_val_00023028.JPEG +ILSVRC2012_val_00036926.JPEG +ILSVRC2012_val_00030251.JPEG +ILSVRC2012_val_00003076.JPEG +ILSVRC2012_val_00015385.JPEG +ILSVRC2012_val_00001464.JPEG +ILSVRC2012_val_00011218.JPEG +ILSVRC2012_val_00016569.JPEG +ILSVRC2012_val_00043881.JPEG +ILSVRC2012_val_00008623.JPEG +ILSVRC2012_val_00031923.JPEG +ILSVRC2012_val_00028247.JPEG +ILSVRC2012_val_00021504.JPEG +ILSVRC2012_val_00018312.JPEG +ILSVRC2012_val_00013954.JPEG +ILSVRC2012_val_00012805.JPEG +ILSVRC2012_val_00007206.JPEG +ILSVRC2012_val_00043862.JPEG +ILSVRC2012_val_00026038.JPEG +ILSVRC2012_val_00041761.JPEG +ILSVRC2012_val_00013831.JPEG +ILSVRC2012_val_00024245.JPEG +ILSVRC2012_val_00020113.JPEG +ILSVRC2012_val_00007191.JPEG +ILSVRC2012_val_00042112.JPEG +ILSVRC2012_val_00037389.JPEG +ILSVRC2012_val_00009489.JPEG +ILSVRC2012_val_00045945.JPEG +ILSVRC2012_val_00002014.JPEG +ILSVRC2012_val_00000561.JPEG +ILSVRC2012_val_00015322.JPEG +ILSVRC2012_val_00037156.JPEG +ILSVRC2012_val_00023140.JPEG +ILSVRC2012_val_00033642.JPEG +ILSVRC2012_val_00017688.JPEG +ILSVRC2012_val_00021674.JPEG +ILSVRC2012_val_00006100.JPEG +ILSVRC2012_val_00006838.JPEG +ILSVRC2012_val_00040675.JPEG +ILSVRC2012_val_00040668.JPEG +ILSVRC2012_val_00014695.JPEG +ILSVRC2012_val_00012893.JPEG +ILSVRC2012_val_00009587.JPEG +ILSVRC2012_val_00030442.JPEG +ILSVRC2012_val_00048435.JPEG +ILSVRC2012_val_00035095.JPEG +ILSVRC2012_val_00010296.JPEG +ILSVRC2012_val_00028913.JPEG +ILSVRC2012_val_00030883.JPEG +ILSVRC2012_val_00048886.JPEG +ILSVRC2012_val_00017481.JPEG +ILSVRC2012_val_00015336.JPEG +ILSVRC2012_val_00042392.JPEG +ILSVRC2012_val_00035433.JPEG +ILSVRC2012_val_00021212.JPEG +ILSVRC2012_val_00005539.JPEG +ILSVRC2012_val_00028149.JPEG +ILSVRC2012_val_00006848.JPEG +ILSVRC2012_val_00001112.JPEG +ILSVRC2012_val_00025166.JPEG +ILSVRC2012_val_00018163.JPEG +ILSVRC2012_val_00013191.JPEG +ILSVRC2012_val_00014803.JPEG +ILSVRC2012_val_00016499.JPEG +ILSVRC2012_val_00016474.JPEG +ILSVRC2012_val_00010325.JPEG +ILSVRC2012_val_00025880.JPEG +ILSVRC2012_val_00047328.JPEG +ILSVRC2012_val_00032642.JPEG +ILSVRC2012_val_00015913.JPEG +ILSVRC2012_val_00023118.JPEG +ILSVRC2012_val_00049509.JPEG +ILSVRC2012_val_00008373.JPEG +ILSVRC2012_val_00028188.JPEG +ILSVRC2012_val_00007774.JPEG +ILSVRC2012_val_00022460.JPEG +ILSVRC2012_val_00021777.JPEG +ILSVRC2012_val_00038066.JPEG +ILSVRC2012_val_00009957.JPEG +ILSVRC2012_val_00044176.JPEG +ILSVRC2012_val_00041218.JPEG +ILSVRC2012_val_00010664.JPEG +ILSVRC2012_val_00000440.JPEG +ILSVRC2012_val_00036279.JPEG +ILSVRC2012_val_00011206.JPEG +ILSVRC2012_val_00021773.JPEG +ILSVRC2012_val_00011454.JPEG +ILSVRC2012_val_00037821.JPEG +ILSVRC2012_val_00003558.JPEG +ILSVRC2012_val_00019642.JPEG +ILSVRC2012_val_00047424.JPEG +ILSVRC2012_val_00024166.JPEG +ILSVRC2012_val_00036165.JPEG +ILSVRC2012_val_00022523.JPEG +ILSVRC2012_val_00030616.JPEG +ILSVRC2012_val_00014869.JPEG +ILSVRC2012_val_00016709.JPEG +ILSVRC2012_val_00001285.JPEG +ILSVRC2012_val_00045733.JPEG +ILSVRC2012_val_00039136.JPEG +ILSVRC2012_val_00024580.JPEG +ILSVRC2012_val_00042506.JPEG +ILSVRC2012_val_00020960.JPEG +ILSVRC2012_val_00048840.JPEG +ILSVRC2012_val_00028503.JPEG +ILSVRC2012_val_00042354.JPEG +ILSVRC2012_val_00030356.JPEG +ILSVRC2012_val_00048926.JPEG +ILSVRC2012_val_00006539.JPEG +ILSVRC2012_val_00007641.JPEG +ILSVRC2012_val_00019245.JPEG +ILSVRC2012_val_00008861.JPEG +ILSVRC2012_val_00020174.JPEG +ILSVRC2012_val_00030569.JPEG +ILSVRC2012_val_00019779.JPEG +ILSVRC2012_val_00019936.JPEG +ILSVRC2012_val_00019986.JPEG +ILSVRC2012_val_00011611.JPEG +ILSVRC2012_val_00025648.JPEG +ILSVRC2012_val_00043804.JPEG +ILSVRC2012_val_00030551.JPEG +ILSVRC2012_val_00036865.JPEG +ILSVRC2012_val_00019097.JPEG +ILSVRC2012_val_00006957.JPEG +ILSVRC2012_val_00023828.JPEG +ILSVRC2012_val_00047810.JPEG +ILSVRC2012_val_00028482.JPEG +ILSVRC2012_val_00030726.JPEG +ILSVRC2012_val_00005319.JPEG +ILSVRC2012_val_00017881.JPEG +ILSVRC2012_val_00020811.JPEG +ILSVRC2012_val_00008682.JPEG +ILSVRC2012_val_00033423.JPEG +ILSVRC2012_val_00039984.JPEG +ILSVRC2012_val_00022711.JPEG +ILSVRC2012_val_00044466.JPEG +ILSVRC2012_val_00001845.JPEG +ILSVRC2012_val_00007295.JPEG +ILSVRC2012_val_00016184.JPEG +ILSVRC2012_val_00017238.JPEG +ILSVRC2012_val_00033321.JPEG +ILSVRC2012_val_00008217.JPEG +ILSVRC2012_val_00009956.JPEG +ILSVRC2012_val_00035831.JPEG +ILSVRC2012_val_00044961.JPEG +ILSVRC2012_val_00025984.JPEG +ILSVRC2012_val_00004067.JPEG +ILSVRC2012_val_00038202.JPEG +ILSVRC2012_val_00049432.JPEG +ILSVRC2012_val_00023617.JPEG +ILSVRC2012_val_00013989.JPEG +ILSVRC2012_val_00014158.JPEG +ILSVRC2012_val_00036853.JPEG +ILSVRC2012_val_00024117.JPEG +ILSVRC2012_val_00042702.JPEG +ILSVRC2012_val_00030711.JPEG +ILSVRC2012_val_00036921.JPEG +ILSVRC2012_val_00028859.JPEG +ILSVRC2012_val_00005377.JPEG +ILSVRC2012_val_00045319.JPEG +ILSVRC2012_val_00001821.JPEG +ILSVRC2012_val_00016575.JPEG +ILSVRC2012_val_00020535.JPEG +ILSVRC2012_val_00009592.JPEG +ILSVRC2012_val_00020492.JPEG +ILSVRC2012_val_00019164.JPEG +ILSVRC2012_val_00034722.JPEG +ILSVRC2012_val_00037278.JPEG +ILSVRC2012_val_00046570.JPEG +ILSVRC2012_val_00013926.JPEG +ILSVRC2012_val_00031041.JPEG +ILSVRC2012_val_00042092.JPEG +ILSVRC2012_val_00012565.JPEG +ILSVRC2012_val_00018643.JPEG +ILSVRC2012_val_00005793.JPEG +ILSVRC2012_val_00015794.JPEG +ILSVRC2012_val_00001414.JPEG +ILSVRC2012_val_00046896.JPEG +ILSVRC2012_val_00015428.JPEG +ILSVRC2012_val_00004235.JPEG +ILSVRC2012_val_00043450.JPEG +ILSVRC2012_val_00025036.JPEG +ILSVRC2012_val_00038798.JPEG +ILSVRC2012_val_00048325.JPEG +ILSVRC2012_val_00034096.JPEG +ILSVRC2012_val_00023003.JPEG +ILSVRC2012_val_00002276.JPEG +ILSVRC2012_val_00034132.JPEG +ILSVRC2012_val_00045454.JPEG +ILSVRC2012_val_00008448.JPEG +ILSVRC2012_val_00011686.JPEG +ILSVRC2012_val_00046617.JPEG +ILSVRC2012_val_00032890.JPEG +ILSVRC2012_val_00042011.JPEG +ILSVRC2012_val_00015602.JPEG +ILSVRC2012_val_00046269.JPEG +ILSVRC2012_val_00047960.JPEG +ILSVRC2012_val_00033593.JPEG +ILSVRC2012_val_00022352.JPEG +ILSVRC2012_val_00042910.JPEG +ILSVRC2012_val_00014482.JPEG +ILSVRC2012_val_00029668.JPEG +ILSVRC2012_val_00014740.JPEG +ILSVRC2012_val_00019972.JPEG +ILSVRC2012_val_00011129.JPEG +ILSVRC2012_val_00001851.JPEG +ILSVRC2012_val_00026886.JPEG +ILSVRC2012_val_00011127.JPEG +ILSVRC2012_val_00017273.JPEG +ILSVRC2012_val_00028861.JPEG +ILSVRC2012_val_00018977.JPEG +ILSVRC2012_val_00024473.JPEG +ILSVRC2012_val_00023480.JPEG +ILSVRC2012_val_00047688.JPEG +ILSVRC2012_val_00014628.JPEG +ILSVRC2012_val_00022489.JPEG +ILSVRC2012_val_00030848.JPEG +ILSVRC2012_val_00029237.JPEG +ILSVRC2012_val_00047041.JPEG +ILSVRC2012_val_00013750.JPEG +ILSVRC2012_val_00011702.JPEG +ILSVRC2012_val_00016917.JPEG +ILSVRC2012_val_00029055.JPEG +ILSVRC2012_val_00010769.JPEG +ILSVRC2012_val_00039573.JPEG +ILSVRC2012_val_00039339.JPEG +ILSVRC2012_val_00021669.JPEG +ILSVRC2012_val_00002043.JPEG +ILSVRC2012_val_00008143.JPEG +ILSVRC2012_val_00012961.JPEG +ILSVRC2012_val_00037945.JPEG +ILSVRC2012_val_00019317.JPEG +ILSVRC2012_val_00033525.JPEG +ILSVRC2012_val_00009797.JPEG +ILSVRC2012_val_00006405.JPEG +ILSVRC2012_val_00011098.JPEG +ILSVRC2012_val_00034261.JPEG +ILSVRC2012_val_00009224.JPEG +ILSVRC2012_val_00023122.JPEG +ILSVRC2012_val_00047460.JPEG +ILSVRC2012_val_00013896.JPEG +ILSVRC2012_val_00007122.JPEG +ILSVRC2012_val_00039218.JPEG +ILSVRC2012_val_00037667.JPEG +ILSVRC2012_val_00033809.JPEG +ILSVRC2012_val_00042049.JPEG +ILSVRC2012_val_00009536.JPEG +ILSVRC2012_val_00010235.JPEG +ILSVRC2012_val_00034428.JPEG +ILSVRC2012_val_00024526.JPEG +ILSVRC2012_val_00000266.JPEG +ILSVRC2012_val_00003374.JPEG +ILSVRC2012_val_00003414.JPEG +ILSVRC2012_val_00004756.JPEG +ILSVRC2012_val_00001460.JPEG +ILSVRC2012_val_00005415.JPEG +ILSVRC2012_val_00015703.JPEG +ILSVRC2012_val_00046801.JPEG +ILSVRC2012_val_00047462.JPEG +ILSVRC2012_val_00018368.JPEG +ILSVRC2012_val_00048476.JPEG +ILSVRC2012_val_00029322.JPEG +ILSVRC2012_val_00033398.JPEG +ILSVRC2012_val_00033699.JPEG +ILSVRC2012_val_00000955.JPEG +ILSVRC2012_val_00001294.JPEG +ILSVRC2012_val_00013308.JPEG +ILSVRC2012_val_00000459.JPEG +ILSVRC2012_val_00028450.JPEG +ILSVRC2012_val_00045161.JPEG +ILSVRC2012_val_00029286.JPEG +ILSVRC2012_val_00008876.JPEG +ILSVRC2012_val_00001222.JPEG +ILSVRC2012_val_00006870.JPEG +ILSVRC2012_val_00018422.JPEG +ILSVRC2012_val_00014642.JPEG +ILSVRC2012_val_00008649.JPEG +ILSVRC2012_val_00026704.JPEG +ILSVRC2012_val_00049661.JPEG +ILSVRC2012_val_00011242.JPEG +ILSVRC2012_val_00032783.JPEG +ILSVRC2012_val_00029541.JPEG +ILSVRC2012_val_00035132.JPEG +ILSVRC2012_val_00042521.JPEG +ILSVRC2012_val_00010700.JPEG +ILSVRC2012_val_00007159.JPEG +ILSVRC2012_val_00032279.JPEG +ILSVRC2012_val_00040201.JPEG +ILSVRC2012_val_00013589.JPEG +ILSVRC2012_val_00009703.JPEG +ILSVRC2012_val_00000915.JPEG +ILSVRC2012_val_00015923.JPEG +ILSVRC2012_val_00032736.JPEG +ILSVRC2012_val_00012252.JPEG +ILSVRC2012_val_00020372.JPEG +ILSVRC2012_val_00003760.JPEG +ILSVRC2012_val_00017274.JPEG +ILSVRC2012_val_00033072.JPEG +ILSVRC2012_val_00006369.JPEG +ILSVRC2012_val_00023713.JPEG +ILSVRC2012_val_00003141.JPEG +ILSVRC2012_val_00002281.JPEG +ILSVRC2012_val_00040788.JPEG +ILSVRC2012_val_00011124.JPEG +ILSVRC2012_val_00003178.JPEG +ILSVRC2012_val_00024549.JPEG +ILSVRC2012_val_00045457.JPEG +ILSVRC2012_val_00007902.JPEG +ILSVRC2012_val_00036920.JPEG +ILSVRC2012_val_00047698.JPEG +ILSVRC2012_val_00004510.JPEG +ILSVRC2012_val_00021893.JPEG +ILSVRC2012_val_00032693.JPEG +ILSVRC2012_val_00044184.JPEG +ILSVRC2012_val_00013652.JPEG +ILSVRC2012_val_00045178.JPEG +ILSVRC2012_val_00016785.JPEG +ILSVRC2012_val_00022571.JPEG +ILSVRC2012_val_00019441.JPEG +ILSVRC2012_val_00002062.JPEG +ILSVRC2012_val_00007505.JPEG +ILSVRC2012_val_00045676.JPEG +ILSVRC2012_val_00015093.JPEG +ILSVRC2012_val_00013526.JPEG +ILSVRC2012_val_00024021.JPEG +ILSVRC2012_val_00011819.JPEG +ILSVRC2012_val_00010406.JPEG +ILSVRC2012_val_00017838.JPEG +ILSVRC2012_val_00031778.JPEG +ILSVRC2012_val_00032878.JPEG +ILSVRC2012_val_00005740.JPEG +ILSVRC2012_val_00039357.JPEG +ILSVRC2012_val_00001991.JPEG +ILSVRC2012_val_00015280.JPEG +ILSVRC2012_val_00025429.JPEG +ILSVRC2012_val_00024645.JPEG +ILSVRC2012_val_00016285.JPEG +ILSVRC2012_val_00036271.JPEG +ILSVRC2012_val_00000982.JPEG +ILSVRC2012_val_00034315.JPEG +ILSVRC2012_val_00045608.JPEG +ILSVRC2012_val_00047365.JPEG +ILSVRC2012_val_00007195.JPEG +ILSVRC2012_val_00017014.JPEG +ILSVRC2012_val_00030401.JPEG +ILSVRC2012_val_00017113.JPEG +ILSVRC2012_val_00047896.JPEG +ILSVRC2012_val_00004268.JPEG +ILSVRC2012_val_00004568.JPEG +ILSVRC2012_val_00045663.JPEG +ILSVRC2012_val_00010306.JPEG +ILSVRC2012_val_00022990.JPEG +ILSVRC2012_val_00006455.JPEG +ILSVRC2012_val_00020924.JPEG +ILSVRC2012_val_00035821.JPEG +ILSVRC2012_val_00022932.JPEG +ILSVRC2012_val_00022864.JPEG +ILSVRC2012_val_00005083.JPEG +ILSVRC2012_val_00007911.JPEG +ILSVRC2012_val_00036667.JPEG +ILSVRC2012_val_00024652.JPEG +ILSVRC2012_val_00042543.JPEG +ILSVRC2012_val_00009398.JPEG +ILSVRC2012_val_00035097.JPEG +ILSVRC2012_val_00049905.JPEG +ILSVRC2012_val_00026734.JPEG +ILSVRC2012_val_00030929.JPEG +ILSVRC2012_val_00030920.JPEG +ILSVRC2012_val_00005056.JPEG +ILSVRC2012_val_00029837.JPEG +ILSVRC2012_val_00039182.JPEG +ILSVRC2012_val_00017823.JPEG +ILSVRC2012_val_00008850.JPEG +ILSVRC2012_val_00006533.JPEG +ILSVRC2012_val_00011289.JPEG +ILSVRC2012_val_00018666.JPEG +ILSVRC2012_val_00026224.JPEG +ILSVRC2012_val_00033906.JPEG +ILSVRC2012_val_00028084.JPEG +ILSVRC2012_val_00004072.JPEG +ILSVRC2012_val_00035301.JPEG +ILSVRC2012_val_00046365.JPEG +ILSVRC2012_val_00034624.JPEG +ILSVRC2012_val_00003734.JPEG +ILSVRC2012_val_00004028.JPEG +ILSVRC2012_val_00030128.JPEG +ILSVRC2012_val_00036993.JPEG +ILSVRC2012_val_00007133.JPEG +ILSVRC2012_val_00007468.JPEG +ILSVRC2012_val_00008438.JPEG +ILSVRC2012_val_00021028.JPEG +ILSVRC2012_val_00023403.JPEG +ILSVRC2012_val_00034393.JPEG +ILSVRC2012_val_00019495.JPEG +ILSVRC2012_val_00031441.JPEG +ILSVRC2012_val_00006935.JPEG +ILSVRC2012_val_00029141.JPEG +ILSVRC2012_val_00016628.JPEG +ILSVRC2012_val_00008375.JPEG +ILSVRC2012_val_00036822.JPEG +ILSVRC2012_val_00023965.JPEG +ILSVRC2012_val_00006037.JPEG +ILSVRC2012_val_00014560.JPEG +ILSVRC2012_val_00009661.JPEG +ILSVRC2012_val_00044711.JPEG +ILSVRC2012_val_00015249.JPEG +ILSVRC2012_val_00042583.JPEG +ILSVRC2012_val_00032273.JPEG +ILSVRC2012_val_00028995.JPEG +ILSVRC2012_val_00043643.JPEG +ILSVRC2012_val_00008411.JPEG +ILSVRC2012_val_00036142.JPEG +ILSVRC2012_val_00023654.JPEG +ILSVRC2012_val_00001926.JPEG +ILSVRC2012_val_00014874.JPEG +ILSVRC2012_val_00016511.JPEG +ILSVRC2012_val_00042984.JPEG +ILSVRC2012_val_00019621.JPEG +ILSVRC2012_val_00008624.JPEG +ILSVRC2012_val_00038385.JPEG +ILSVRC2012_val_00004882.JPEG +ILSVRC2012_val_00001702.JPEG +ILSVRC2012_val_00045999.JPEG +ILSVRC2012_val_00025883.JPEG +ILSVRC2012_val_00016854.JPEG +ILSVRC2012_val_00033128.JPEG +ILSVRC2012_val_00006916.JPEG +ILSVRC2012_val_00031994.JPEG +ILSVRC2012_val_00041278.JPEG +ILSVRC2012_val_00049162.JPEG +ILSVRC2012_val_00030591.JPEG +ILSVRC2012_val_00000981.JPEG +ILSVRC2012_val_00009102.JPEG +ILSVRC2012_val_00003367.JPEG +ILSVRC2012_val_00012725.JPEG +ILSVRC2012_val_00046971.JPEG +ILSVRC2012_val_00018567.JPEG +ILSVRC2012_val_00047353.JPEG +ILSVRC2012_val_00037938.JPEG +ILSVRC2012_val_00016095.JPEG +ILSVRC2012_val_00010137.JPEG +ILSVRC2012_val_00013461.JPEG +ILSVRC2012_val_00042599.JPEG +ILSVRC2012_val_00022581.JPEG +ILSVRC2012_val_00026040.JPEG +ILSVRC2012_val_00007228.JPEG +ILSVRC2012_val_00034875.JPEG +ILSVRC2012_val_00016242.JPEG +ILSVRC2012_val_00048550.JPEG +ILSVRC2012_val_00035249.JPEG +ILSVRC2012_val_00045901.JPEG +ILSVRC2012_val_00008905.JPEG +ILSVRC2012_val_00036605.JPEG +ILSVRC2012_val_00030845.JPEG +ILSVRC2012_val_00025531.JPEG +ILSVRC2012_val_00017312.JPEG +ILSVRC2012_val_00041078.JPEG +ILSVRC2012_val_00022349.JPEG +ILSVRC2012_val_00019956.JPEG +ILSVRC2012_val_00033695.JPEG +ILSVRC2012_val_00047770.JPEG +ILSVRC2012_val_00023390.JPEG +ILSVRC2012_val_00011701.JPEG +ILSVRC2012_val_00027808.JPEG +ILSVRC2012_val_00014342.JPEG +ILSVRC2012_val_00002121.JPEG +ILSVRC2012_val_00023938.JPEG +ILSVRC2012_val_00023462.JPEG +ILSVRC2012_val_00038666.JPEG +ILSVRC2012_val_00035793.JPEG +ILSVRC2012_val_00028806.JPEG +ILSVRC2012_val_00002565.JPEG +ILSVRC2012_val_00031826.JPEG +ILSVRC2012_val_00043319.JPEG +ILSVRC2012_val_00017447.JPEG +ILSVRC2012_val_00039008.JPEG +ILSVRC2012_val_00019934.JPEG +ILSVRC2012_val_00006570.JPEG +ILSVRC2012_val_00040875.JPEG +ILSVRC2012_val_00032361.JPEG +ILSVRC2012_val_00007257.JPEG +ILSVRC2012_val_00008461.JPEG +ILSVRC2012_val_00049667.JPEG +ILSVRC2012_val_00038643.JPEG +ILSVRC2012_val_00041027.JPEG +ILSVRC2012_val_00022999.JPEG +ILSVRC2012_val_00025162.JPEG +ILSVRC2012_val_00043744.JPEG +ILSVRC2012_val_00028528.JPEG +ILSVRC2012_val_00032207.JPEG +ILSVRC2012_val_00027183.JPEG +ILSVRC2012_val_00011281.JPEG +ILSVRC2012_val_00046308.JPEG +ILSVRC2012_val_00009409.JPEG +ILSVRC2012_val_00040768.JPEG +ILSVRC2012_val_00038415.JPEG +ILSVRC2012_val_00045521.JPEG +ILSVRC2012_val_00019458.JPEG +ILSVRC2012_val_00011344.JPEG +ILSVRC2012_val_00002802.JPEG +ILSVRC2012_val_00009310.JPEG +ILSVRC2012_val_00033987.JPEG +ILSVRC2012_val_00018792.JPEG +ILSVRC2012_val_00044030.JPEG +ILSVRC2012_val_00041470.JPEG +ILSVRC2012_val_00008834.JPEG +ILSVRC2012_val_00032118.JPEG +ILSVRC2012_val_00022691.JPEG +ILSVRC2012_val_00029962.JPEG +ILSVRC2012_val_00042474.JPEG +ILSVRC2012_val_00024044.JPEG +ILSVRC2012_val_00038352.JPEG +ILSVRC2012_val_00048240.JPEG +ILSVRC2012_val_00005569.JPEG +ILSVRC2012_val_00027875.JPEG +ILSVRC2012_val_00049116.JPEG +ILSVRC2012_val_00044154.JPEG +ILSVRC2012_val_00035678.JPEG +ILSVRC2012_val_00042235.JPEG +ILSVRC2012_val_00011743.JPEG +ILSVRC2012_val_00035685.JPEG +ILSVRC2012_val_00041547.JPEG +ILSVRC2012_val_00043486.JPEG +ILSVRC2012_val_00027046.JPEG +ILSVRC2012_val_00016405.JPEG +ILSVRC2012_val_00010204.JPEG +ILSVRC2012_val_00004645.JPEG +ILSVRC2012_val_00037697.JPEG +ILSVRC2012_val_00002711.JPEG +ILSVRC2012_val_00043536.JPEG +ILSVRC2012_val_00004925.JPEG +ILSVRC2012_val_00048416.JPEG +ILSVRC2012_val_00009141.JPEG +ILSVRC2012_val_00029283.JPEG +ILSVRC2012_val_00017569.JPEG +ILSVRC2012_val_00042662.JPEG +ILSVRC2012_val_00049551.JPEG +ILSVRC2012_val_00029382.JPEG +ILSVRC2012_val_00030118.JPEG +ILSVRC2012_val_00048754.JPEG +ILSVRC2012_val_00001015.JPEG +ILSVRC2012_val_00038250.JPEG +ILSVRC2012_val_00018040.JPEG +ILSVRC2012_val_00043767.JPEG +ILSVRC2012_val_00034627.JPEG +ILSVRC2012_val_00040254.JPEG +ILSVRC2012_val_00027864.JPEG +ILSVRC2012_val_00024926.JPEG +ILSVRC2012_val_00007382.JPEG +ILSVRC2012_val_00001118.JPEG +ILSVRC2012_val_00014365.JPEG +ILSVRC2012_val_00026060.JPEG +ILSVRC2012_val_00045094.JPEG +ILSVRC2012_val_00029874.JPEG +ILSVRC2012_val_00049716.JPEG +ILSVRC2012_val_00019158.JPEG +ILSVRC2012_val_00031197.JPEG +ILSVRC2012_val_00037899.JPEG +ILSVRC2012_val_00014669.JPEG +ILSVRC2012_val_00036906.JPEG +ILSVRC2012_val_00043079.JPEG +ILSVRC2012_val_00017658.JPEG +ILSVRC2012_val_00008167.JPEG +ILSVRC2012_val_00045959.JPEG +ILSVRC2012_val_00024534.JPEG +ILSVRC2012_val_00015118.JPEG +ILSVRC2012_val_00022355.JPEG +ILSVRC2012_val_00048281.JPEG +ILSVRC2012_val_00048720.JPEG +ILSVRC2012_val_00009113.JPEG +ILSVRC2012_val_00027806.JPEG +ILSVRC2012_val_00009491.JPEG +ILSVRC2012_val_00046486.JPEG +ILSVRC2012_val_00041279.JPEG +ILSVRC2012_val_00045544.JPEG +ILSVRC2012_val_00001635.JPEG +ILSVRC2012_val_00001165.JPEG +ILSVRC2012_val_00033772.JPEG +ILSVRC2012_val_00023686.JPEG +ILSVRC2012_val_00024802.JPEG +ILSVRC2012_val_00004862.JPEG +ILSVRC2012_val_00028501.JPEG +ILSVRC2012_val_00019547.JPEG +ILSVRC2012_val_00002408.JPEG +ILSVRC2012_val_00041442.JPEG +ILSVRC2012_val_00010787.JPEG +ILSVRC2012_val_00040870.JPEG +ILSVRC2012_val_00021500.JPEG +ILSVRC2012_val_00048230.JPEG +ILSVRC2012_val_00005058.JPEG +ILSVRC2012_val_00023279.JPEG +ILSVRC2012_val_00026048.JPEG +ILSVRC2012_val_00024234.JPEG +ILSVRC2012_val_00017941.JPEG +ILSVRC2012_val_00002357.JPEG +ILSVRC2012_val_00032158.JPEG +ILSVRC2012_val_00024738.JPEG +ILSVRC2012_val_00002258.JPEG +ILSVRC2012_val_00034780.JPEG +ILSVRC2012_val_00002527.JPEG +ILSVRC2012_val_00046290.JPEG +ILSVRC2012_val_00006150.JPEG +ILSVRC2012_val_00045024.JPEG +ILSVRC2012_val_00005738.JPEG +ILSVRC2012_val_00001777.JPEG +ILSVRC2012_val_00024719.JPEG +ILSVRC2012_val_00027895.JPEG +ILSVRC2012_val_00012609.JPEG +ILSVRC2012_val_00014734.JPEG +ILSVRC2012_val_00033804.JPEG +ILSVRC2012_val_00032590.JPEG +ILSVRC2012_val_00020166.JPEG +ILSVRC2012_val_00031852.JPEG +ILSVRC2012_val_00018176.JPEG +ILSVRC2012_val_00017918.JPEG +ILSVRC2012_val_00046886.JPEG +ILSVRC2012_val_00030076.JPEG +ILSVRC2012_val_00039684.JPEG +ILSVRC2012_val_00008505.JPEG +ILSVRC2012_val_00032687.JPEG +ILSVRC2012_val_00032998.JPEG +ILSVRC2012_val_00028832.JPEG +ILSVRC2012_val_00037663.JPEG +ILSVRC2012_val_00003674.JPEG +ILSVRC2012_val_00024423.JPEG +ILSVRC2012_val_00029852.JPEG +ILSVRC2012_val_00024589.JPEG +ILSVRC2012_val_00021650.JPEG +ILSVRC2012_val_00040408.JPEG +ILSVRC2012_val_00004054.JPEG +ILSVRC2012_val_00000259.JPEG +ILSVRC2012_val_00026889.JPEG +ILSVRC2012_val_00002248.JPEG +ILSVRC2012_val_00023595.JPEG +ILSVRC2012_val_00030579.JPEG +ILSVRC2012_val_00017840.JPEG +ILSVRC2012_val_00006757.JPEG +ILSVRC2012_val_00048202.JPEG +ILSVRC2012_val_00035116.JPEG +ILSVRC2012_val_00047683.JPEG +ILSVRC2012_val_00007888.JPEG +ILSVRC2012_val_00038782.JPEG +ILSVRC2012_val_00048178.JPEG +ILSVRC2012_val_00038673.JPEG +ILSVRC2012_val_00047101.JPEG +ILSVRC2012_val_00034437.JPEG +ILSVRC2012_val_00047473.JPEG +ILSVRC2012_val_00033983.JPEG +ILSVRC2012_val_00034811.JPEG +ILSVRC2012_val_00002139.JPEG +ILSVRC2012_val_00017594.JPEG +ILSVRC2012_val_00025225.JPEG +ILSVRC2012_val_00048015.JPEG +ILSVRC2012_val_00036222.JPEG +ILSVRC2012_val_00035355.JPEG +ILSVRC2012_val_00023472.JPEG +ILSVRC2012_val_00041469.JPEG +ILSVRC2012_val_00013762.JPEG +ILSVRC2012_val_00004439.JPEG +ILSVRC2012_val_00047640.JPEG +ILSVRC2012_val_00046352.JPEG +ILSVRC2012_val_00011545.JPEG +ILSVRC2012_val_00044988.JPEG +ILSVRC2012_val_00016796.JPEG +ILSVRC2012_val_00044494.JPEG +ILSVRC2012_val_00048492.JPEG +ILSVRC2012_val_00040976.JPEG +ILSVRC2012_val_00048458.JPEG +ILSVRC2012_val_00033916.JPEG +ILSVRC2012_val_00047546.JPEG +ILSVRC2012_val_00005267.JPEG +ILSVRC2012_val_00032763.JPEG +ILSVRC2012_val_00012576.JPEG +ILSVRC2012_val_00036552.JPEG +ILSVRC2012_val_00034318.JPEG +ILSVRC2012_val_00009641.JPEG +ILSVRC2012_val_00010201.JPEG +ILSVRC2012_val_00026550.JPEG +ILSVRC2012_val_00042838.JPEG +ILSVRC2012_val_00026053.JPEG +ILSVRC2012_val_00015873.JPEG +ILSVRC2012_val_00045456.JPEG +ILSVRC2012_val_00043757.JPEG +ILSVRC2012_val_00042192.JPEG +ILSVRC2012_val_00015136.JPEG +ILSVRC2012_val_00048198.JPEG +ILSVRC2012_val_00019269.JPEG +ILSVRC2012_val_00004142.JPEG +ILSVRC2012_val_00007870.JPEG +ILSVRC2012_val_00013858.JPEG +ILSVRC2012_val_00017120.JPEG +ILSVRC2012_val_00003466.JPEG +ILSVRC2012_val_00018116.JPEG +ILSVRC2012_val_00022212.JPEG +ILSVRC2012_val_00002142.JPEG +ILSVRC2012_val_00029985.JPEG +ILSVRC2012_val_00046323.JPEG +ILSVRC2012_val_00033142.JPEG +ILSVRC2012_val_00015862.JPEG +ILSVRC2012_val_00048037.JPEG +ILSVRC2012_val_00037707.JPEG +ILSVRC2012_val_00001930.JPEG +ILSVRC2012_val_00012815.JPEG +ILSVRC2012_val_00018449.JPEG +ILSVRC2012_val_00040499.JPEG +ILSVRC2012_val_00039448.JPEG +ILSVRC2012_val_00042696.JPEG +ILSVRC2012_val_00030924.JPEG +ILSVRC2012_val_00032473.JPEG +ILSVRC2012_val_00003563.JPEG +ILSVRC2012_val_00024103.JPEG +ILSVRC2012_val_00025357.JPEG +ILSVRC2012_val_00001883.JPEG +ILSVRC2012_val_00024706.JPEG +ILSVRC2012_val_00033898.JPEG +ILSVRC2012_val_00000426.JPEG +ILSVRC2012_val_00035033.JPEG +ILSVRC2012_val_00031840.JPEG +ILSVRC2012_val_00037055.JPEG +ILSVRC2012_val_00046314.JPEG +ILSVRC2012_val_00036660.JPEG +ILSVRC2012_val_00007128.JPEG +ILSVRC2012_val_00041322.JPEG +ILSVRC2012_val_00036434.JPEG +ILSVRC2012_val_00046436.JPEG +ILSVRC2012_val_00021078.JPEG +ILSVRC2012_val_00026725.JPEG +ILSVRC2012_val_00034260.JPEG +ILSVRC2012_val_00022872.JPEG +ILSVRC2012_val_00022455.JPEG +ILSVRC2012_val_00038219.JPEG +ILSVRC2012_val_00047820.JPEG +ILSVRC2012_val_00025884.JPEG +ILSVRC2012_val_00047904.JPEG +ILSVRC2012_val_00029699.JPEG +ILSVRC2012_val_00003489.JPEG +ILSVRC2012_val_00018218.JPEG +ILSVRC2012_val_00021812.JPEG +ILSVRC2012_val_00047998.JPEG +ILSVRC2012_val_00046805.JPEG +ILSVRC2012_val_00045291.JPEG +ILSVRC2012_val_00025506.JPEG +ILSVRC2012_val_00026291.JPEG +ILSVRC2012_val_00004977.JPEG +ILSVRC2012_val_00027040.JPEG +ILSVRC2012_val_00011645.JPEG +ILSVRC2012_val_00033854.JPEG +ILSVRC2012_val_00004682.JPEG +ILSVRC2012_val_00048943.JPEG +ILSVRC2012_val_00015575.JPEG +ILSVRC2012_val_00032365.JPEG +ILSVRC2012_val_00031558.JPEG +ILSVRC2012_val_00016915.JPEG +ILSVRC2012_val_00036293.JPEG +ILSVRC2012_val_00019687.JPEG +ILSVRC2012_val_00013628.JPEG +ILSVRC2012_val_00004082.JPEG +ILSVRC2012_val_00020191.JPEG +ILSVRC2012_val_00017066.JPEG +ILSVRC2012_val_00043481.JPEG +ILSVRC2012_val_00011674.JPEG +ILSVRC2012_val_00048134.JPEG +ILSVRC2012_val_00009720.JPEG +ILSVRC2012_val_00019022.JPEG +ILSVRC2012_val_00020790.JPEG +ILSVRC2012_val_00015883.JPEG +ILSVRC2012_val_00027410.JPEG +ILSVRC2012_val_00013403.JPEG +ILSVRC2012_val_00031072.JPEG +ILSVRC2012_val_00016055.JPEG +ILSVRC2012_val_00045082.JPEG +ILSVRC2012_val_00014714.JPEG +ILSVRC2012_val_00034577.JPEG +ILSVRC2012_val_00016805.JPEG +ILSVRC2012_val_00020662.JPEG +ILSVRC2012_val_00022123.JPEG +ILSVRC2012_val_00035597.JPEG +ILSVRC2012_val_00038022.JPEG +ILSVRC2012_val_00020019.JPEG +ILSVRC2012_val_00027905.JPEG +ILSVRC2012_val_00002684.JPEG +ILSVRC2012_val_00001230.JPEG +ILSVRC2012_val_00029952.JPEG +ILSVRC2012_val_00038936.JPEG +ILSVRC2012_val_00027844.JPEG +ILSVRC2012_val_00038623.JPEG +ILSVRC2012_val_00048675.JPEG +ILSVRC2012_val_00007826.JPEG +ILSVRC2012_val_00021227.JPEG +ILSVRC2012_val_00012079.JPEG +ILSVRC2012_val_00020828.JPEG +ILSVRC2012_val_00037856.JPEG +ILSVRC2012_val_00009855.JPEG +ILSVRC2012_val_00019378.JPEG +ILSVRC2012_val_00023807.JPEG +ILSVRC2012_val_00002115.JPEG +ILSVRC2012_val_00046465.JPEG +ILSVRC2012_val_00031416.JPEG +ILSVRC2012_val_00046686.JPEG +ILSVRC2012_val_00028407.JPEG +ILSVRC2012_val_00024487.JPEG +ILSVRC2012_val_00014282.JPEG +ILSVRC2012_val_00025447.JPEG +ILSVRC2012_val_00034389.JPEG +ILSVRC2012_val_00017690.JPEG +ILSVRC2012_val_00020458.JPEG +ILSVRC2012_val_00032455.JPEG +ILSVRC2012_val_00032439.JPEG +ILSVRC2012_val_00009193.JPEG +ILSVRC2012_val_00048547.JPEG +ILSVRC2012_val_00003328.JPEG +ILSVRC2012_val_00040446.JPEG +ILSVRC2012_val_00002625.JPEG +ILSVRC2012_val_00024297.JPEG +ILSVRC2012_val_00048174.JPEG +ILSVRC2012_val_00035692.JPEG +ILSVRC2012_val_00018270.JPEG +ILSVRC2012_val_00044151.JPEG +ILSVRC2012_val_00039658.JPEG +ILSVRC2012_val_00026869.JPEG +ILSVRC2012_val_00014627.JPEG +ILSVRC2012_val_00016344.JPEG +ILSVRC2012_val_00035458.JPEG +ILSVRC2012_val_00021425.JPEG +ILSVRC2012_val_00046054.JPEG +ILSVRC2012_val_00008700.JPEG +ILSVRC2012_val_00020024.JPEG +ILSVRC2012_val_00005151.JPEG +ILSVRC2012_val_00030437.JPEG +ILSVRC2012_val_00003167.JPEG +ILSVRC2012_val_00024766.JPEG +ILSVRC2012_val_00026631.JPEG +ILSVRC2012_val_00038692.JPEG +ILSVRC2012_val_00003420.JPEG +ILSVRC2012_val_00018438.JPEG +ILSVRC2012_val_00044146.JPEG +ILSVRC2012_val_00046534.JPEG +ILSVRC2012_val_00013029.JPEG +ILSVRC2012_val_00041792.JPEG +ILSVRC2012_val_00038502.JPEG +ILSVRC2012_val_00020059.JPEG +ILSVRC2012_val_00013855.JPEG +ILSVRC2012_val_00021332.JPEG +ILSVRC2012_val_00015646.JPEG +ILSVRC2012_val_00013818.JPEG +ILSVRC2012_val_00033614.JPEG +ILSVRC2012_val_00031194.JPEG +ILSVRC2012_val_00029013.JPEG +ILSVRC2012_val_00004114.JPEG +ILSVRC2012_val_00039537.JPEG +ILSVRC2012_val_00016153.JPEG +ILSVRC2012_val_00010909.JPEG +ILSVRC2012_val_00030552.JPEG +ILSVRC2012_val_00036408.JPEG +ILSVRC2012_val_00027561.JPEG +ILSVRC2012_val_00038147.JPEG +ILSVRC2012_val_00002969.JPEG +ILSVRC2012_val_00045300.JPEG +ILSVRC2012_val_00045989.JPEG +ILSVRC2012_val_00011348.JPEG +ILSVRC2012_val_00042882.JPEG +ILSVRC2012_val_00047831.JPEG +ILSVRC2012_val_00041648.JPEG +ILSVRC2012_val_00024650.JPEG +ILSVRC2012_val_00016043.JPEG +ILSVRC2012_val_00028605.JPEG +ILSVRC2012_val_00013890.JPEG +ILSVRC2012_val_00011225.JPEG +ILSVRC2012_val_00005269.JPEG +ILSVRC2012_val_00016134.JPEG +ILSVRC2012_val_00029430.JPEG +ILSVRC2012_val_00012727.JPEG +ILSVRC2012_val_00013067.JPEG +ILSVRC2012_val_00032774.JPEG +ILSVRC2012_val_00028446.JPEG +ILSVRC2012_val_00045411.JPEG +ILSVRC2012_val_00049959.JPEG +ILSVRC2012_val_00024844.JPEG +ILSVRC2012_val_00029169.JPEG +ILSVRC2012_val_00001381.JPEG +ILSVRC2012_val_00038473.JPEG +ILSVRC2012_val_00034576.JPEG +ILSVRC2012_val_00009617.JPEG +ILSVRC2012_val_00023040.JPEG +ILSVRC2012_val_00005365.JPEG +ILSVRC2012_val_00019058.JPEG +ILSVRC2012_val_00019414.JPEG +ILSVRC2012_val_00008304.JPEG +ILSVRC2012_val_00022519.JPEG +ILSVRC2012_val_00018170.JPEG +ILSVRC2012_val_00013259.JPEG +ILSVRC2012_val_00044492.JPEG +ILSVRC2012_val_00021188.JPEG +ILSVRC2012_val_00041755.JPEG +ILSVRC2012_val_00001075.JPEG +ILSVRC2012_val_00033396.JPEG +ILSVRC2012_val_00002872.JPEG +ILSVRC2012_val_00009909.JPEG +ILSVRC2012_val_00017778.JPEG +ILSVRC2012_val_00014140.JPEG +ILSVRC2012_val_00010483.JPEG +ILSVRC2012_val_00005750.JPEG +ILSVRC2012_val_00031520.JPEG +ILSVRC2012_val_00044013.JPEG +ILSVRC2012_val_00026579.JPEG +ILSVRC2012_val_00033102.JPEG +ILSVRC2012_val_00047124.JPEG +ILSVRC2012_val_00005826.JPEG +ILSVRC2012_val_00039194.JPEG +ILSVRC2012_val_00049831.JPEG +ILSVRC2012_val_00043567.JPEG +ILSVRC2012_val_00005153.JPEG +ILSVRC2012_val_00040664.JPEG +ILSVRC2012_val_00016488.JPEG +ILSVRC2012_val_00030078.JPEG +ILSVRC2012_val_00017680.JPEG +ILSVRC2012_val_00043108.JPEG +ILSVRC2012_val_00043279.JPEG +ILSVRC2012_val_00026305.JPEG +ILSVRC2012_val_00009704.JPEG +ILSVRC2012_val_00011204.JPEG +ILSVRC2012_val_00019337.JPEG +ILSVRC2012_val_00015812.JPEG +ILSVRC2012_val_00033203.JPEG +ILSVRC2012_val_00012806.JPEG +ILSVRC2012_val_00007014.JPEG +ILSVRC2012_val_00008932.JPEG +ILSVRC2012_val_00048612.JPEG +ILSVRC2012_val_00018996.JPEG +ILSVRC2012_val_00010834.JPEG +ILSVRC2012_val_00014839.JPEG +ILSVRC2012_val_00039904.JPEG +ILSVRC2012_val_00048560.JPEG +ILSVRC2012_val_00017548.JPEG +ILSVRC2012_val_00030903.JPEG +ILSVRC2012_val_00001367.JPEG +ILSVRC2012_val_00041372.JPEG +ILSVRC2012_val_00011738.JPEG +ILSVRC2012_val_00031094.JPEG +ILSVRC2012_val_00005397.JPEG +ILSVRC2012_val_00034006.JPEG +ILSVRC2012_val_00017421.JPEG +ILSVRC2012_val_00024748.JPEG +ILSVRC2012_val_00019234.JPEG +ILSVRC2012_val_00007607.JPEG +ILSVRC2012_val_00003730.JPEG +ILSVRC2012_val_00034797.JPEG +ILSVRC2012_val_00042800.JPEG +ILSVRC2012_val_00009057.JPEG +ILSVRC2012_val_00030639.JPEG +ILSVRC2012_val_00021974.JPEG +ILSVRC2012_val_00044412.JPEG +ILSVRC2012_val_00023829.JPEG +ILSVRC2012_val_00030913.JPEG +ILSVRC2012_val_00000193.JPEG +ILSVRC2012_val_00021960.JPEG +ILSVRC2012_val_00039818.JPEG +ILSVRC2012_val_00012464.JPEG +ILSVRC2012_val_00025039.JPEG +ILSVRC2012_val_00025098.JPEG +ILSVRC2012_val_00007347.JPEG +ILSVRC2012_val_00037463.JPEG +ILSVRC2012_val_00000122.JPEG +ILSVRC2012_val_00022102.JPEG +ILSVRC2012_val_00010106.JPEG +ILSVRC2012_val_00040223.JPEG +ILSVRC2012_val_00025040.JPEG +ILSVRC2012_val_00017352.JPEG +ILSVRC2012_val_00023287.JPEG +ILSVRC2012_val_00045975.JPEG +ILSVRC2012_val_00046355.JPEG +ILSVRC2012_val_00019561.JPEG +ILSVRC2012_val_00008050.JPEG +ILSVRC2012_val_00016514.JPEG +ILSVRC2012_val_00027925.JPEG +ILSVRC2012_val_00041349.JPEG +ILSVRC2012_val_00037249.JPEG +ILSVRC2012_val_00048749.JPEG +ILSVRC2012_val_00033833.JPEG +ILSVRC2012_val_00031325.JPEG +ILSVRC2012_val_00023696.JPEG +ILSVRC2012_val_00004823.JPEG +ILSVRC2012_val_00007848.JPEG +ILSVRC2012_val_00046806.JPEG +ILSVRC2012_val_00028803.JPEG +ILSVRC2012_val_00006890.JPEG +ILSVRC2012_val_00002685.JPEG +ILSVRC2012_val_00034712.JPEG +ILSVRC2012_val_00010991.JPEG +ILSVRC2012_val_00026039.JPEG +ILSVRC2012_val_00029390.JPEG +ILSVRC2012_val_00021339.JPEG +ILSVRC2012_val_00020605.JPEG +ILSVRC2012_val_00020465.JPEG +ILSVRC2012_val_00032471.JPEG +ILSVRC2012_val_00011018.JPEG +ILSVRC2012_val_00015399.JPEG +ILSVRC2012_val_00000354.JPEG +ILSVRC2012_val_00007390.JPEG +ILSVRC2012_val_00005647.JPEG +ILSVRC2012_val_00025696.JPEG +ILSVRC2012_val_00039098.JPEG +ILSVRC2012_val_00048418.JPEG +ILSVRC2012_val_00042773.JPEG +ILSVRC2012_val_00026930.JPEG +ILSVRC2012_val_00012540.JPEG +ILSVRC2012_val_00030740.JPEG +ILSVRC2012_val_00043072.JPEG +ILSVRC2012_val_00040071.JPEG +ILSVRC2012_val_00015547.JPEG +ILSVRC2012_val_00045897.JPEG +ILSVRC2012_val_00022879.JPEG +ILSVRC2012_val_00025956.JPEG +ILSVRC2012_val_00022056.JPEG +ILSVRC2012_val_00011302.JPEG +ILSVRC2012_val_00004829.JPEG +ILSVRC2012_val_00011365.JPEG +ILSVRC2012_val_00032556.JPEG +ILSVRC2012_val_00021427.JPEG +ILSVRC2012_val_00032797.JPEG +ILSVRC2012_val_00048838.JPEG +ILSVRC2012_val_00019673.JPEG +ILSVRC2012_val_00037452.JPEG +ILSVRC2012_val_00036987.JPEG +ILSVRC2012_val_00017293.JPEG +ILSVRC2012_val_00014712.JPEG +ILSVRC2012_val_00042119.JPEG +ILSVRC2012_val_00005962.JPEG +ILSVRC2012_val_00026244.JPEG +ILSVRC2012_val_00046270.JPEG +ILSVRC2012_val_00007251.JPEG +ILSVRC2012_val_00024686.JPEG +ILSVRC2012_val_00020672.JPEG +ILSVRC2012_val_00043504.JPEG +ILSVRC2012_val_00044726.JPEG +ILSVRC2012_val_00030647.JPEG +ILSVRC2012_val_00038282.JPEG +ILSVRC2012_val_00010610.JPEG +ILSVRC2012_val_00018777.JPEG +ILSVRC2012_val_00004946.JPEG +ILSVRC2012_val_00044956.JPEG +ILSVRC2012_val_00015228.JPEG +ILSVRC2012_val_00024378.JPEG +ILSVRC2012_val_00024443.JPEG +ILSVRC2012_val_00025208.JPEG +ILSVRC2012_val_00048100.JPEG +ILSVRC2012_val_00046205.JPEG +ILSVRC2012_val_00041782.JPEG +ILSVRC2012_val_00033610.JPEG +ILSVRC2012_val_00008246.JPEG +ILSVRC2012_val_00003679.JPEG +ILSVRC2012_val_00043123.JPEG +ILSVRC2012_val_00000210.JPEG +ILSVRC2012_val_00025762.JPEG +ILSVRC2012_val_00027488.JPEG +ILSVRC2012_val_00048938.JPEG +ILSVRC2012_val_00025075.JPEG +ILSVRC2012_val_00041074.JPEG +ILSVRC2012_val_00022556.JPEG +ILSVRC2012_val_00018823.JPEG +ILSVRC2012_val_00045753.JPEG +ILSVRC2012_val_00013386.JPEG +ILSVRC2012_val_00026946.JPEG +ILSVRC2012_val_00048235.JPEG +ILSVRC2012_val_00025756.JPEG +ILSVRC2012_val_00041384.JPEG +ILSVRC2012_val_00013678.JPEG +ILSVRC2012_val_00047135.JPEG +ILSVRC2012_val_00026366.JPEG +ILSVRC2012_val_00016900.JPEG +ILSVRC2012_val_00011345.JPEG +ILSVRC2012_val_00009725.JPEG +ILSVRC2012_val_00013007.JPEG +ILSVRC2012_val_00004201.JPEG +ILSVRC2012_val_00009511.JPEG +ILSVRC2012_val_00036527.JPEG +ILSVRC2012_val_00047454.JPEG +ILSVRC2012_val_00015043.JPEG +ILSVRC2012_val_00009853.JPEG +ILSVRC2012_val_00017604.JPEG +ILSVRC2012_val_00032836.JPEG +ILSVRC2012_val_00033595.JPEG +ILSVRC2012_val_00048906.JPEG +ILSVRC2012_val_00011517.JPEG +ILSVRC2012_val_00048678.JPEG +ILSVRC2012_val_00045996.JPEG +ILSVRC2012_val_00040617.JPEG +ILSVRC2012_val_00003978.JPEG +ILSVRC2012_val_00020299.JPEG +ILSVRC2012_val_00024133.JPEG +ILSVRC2012_val_00028842.JPEG +ILSVRC2012_val_00011453.JPEG +ILSVRC2012_val_00021601.JPEG +ILSVRC2012_val_00024227.JPEG +ILSVRC2012_val_00045589.JPEG +ILSVRC2012_val_00014498.JPEG +ILSVRC2012_val_00004416.JPEG +ILSVRC2012_val_00032984.JPEG +ILSVRC2012_val_00026525.JPEG +ILSVRC2012_val_00042430.JPEG +ILSVRC2012_val_00016546.JPEG +ILSVRC2012_val_00043119.JPEG +ILSVRC2012_val_00017948.JPEG +ILSVRC2012_val_00048245.JPEG +ILSVRC2012_val_00009110.JPEG +ILSVRC2012_val_00025007.JPEG +ILSVRC2012_val_00015402.JPEG +ILSVRC2012_val_00029990.JPEG +ILSVRC2012_val_00004363.JPEG +ILSVRC2012_val_00002878.JPEG +ILSVRC2012_val_00029485.JPEG +ILSVRC2012_val_00031510.JPEG +ILSVRC2012_val_00014520.JPEG +ILSVRC2012_val_00041160.JPEG +ILSVRC2012_val_00047942.JPEG +ILSVRC2012_val_00026490.JPEG +ILSVRC2012_val_00005196.JPEG +ILSVRC2012_val_00042645.JPEG +ILSVRC2012_val_00016568.JPEG +ILSVRC2012_val_00011622.JPEG +ILSVRC2012_val_00009490.JPEG +ILSVRC2012_val_00047793.JPEG +ILSVRC2012_val_00031270.JPEG +ILSVRC2012_val_00038955.JPEG +ILSVRC2012_val_00027017.JPEG +ILSVRC2012_val_00039795.JPEG +ILSVRC2012_val_00031125.JPEG +ILSVRC2012_val_00046846.JPEG +ILSVRC2012_val_00033685.JPEG +ILSVRC2012_val_00044674.JPEG +ILSVRC2012_val_00037219.JPEG +ILSVRC2012_val_00041885.JPEG +ILSVRC2012_val_00027034.JPEG +ILSVRC2012_val_00034841.JPEG +ILSVRC2012_val_00045795.JPEG +ILSVRC2012_val_00003336.JPEG +ILSVRC2012_val_00048872.JPEG +ILSVRC2012_val_00018610.JPEG +ILSVRC2012_val_00047937.JPEG +ILSVRC2012_val_00016410.JPEG +ILSVRC2012_val_00006735.JPEG +ILSVRC2012_val_00047970.JPEG +ILSVRC2012_val_00007180.JPEG +ILSVRC2012_val_00043352.JPEG +ILSVRC2012_val_00045598.JPEG +ILSVRC2012_val_00044681.JPEG +ILSVRC2012_val_00016522.JPEG +ILSVRC2012_val_00018734.JPEG +ILSVRC2012_val_00017994.JPEG +ILSVRC2012_val_00011107.JPEG +ILSVRC2012_val_00032339.JPEG +ILSVRC2012_val_00005650.JPEG +ILSVRC2012_val_00046592.JPEG +ILSVRC2012_val_00016999.JPEG +ILSVRC2012_val_00007256.JPEG +ILSVRC2012_val_00031014.JPEG +ILSVRC2012_val_00043930.JPEG +ILSVRC2012_val_00042914.JPEG +ILSVRC2012_val_00015203.JPEG +ILSVRC2012_val_00007062.JPEG +ILSVRC2012_val_00048588.JPEG +ILSVRC2012_val_00041555.JPEG +ILSVRC2012_val_00024219.JPEG +ILSVRC2012_val_00017549.JPEG +ILSVRC2012_val_00012833.JPEG +ILSVRC2012_val_00013018.JPEG +ILSVRC2012_val_00013253.JPEG +ILSVRC2012_val_00023799.JPEG +ILSVRC2012_val_00041210.JPEG +ILSVRC2012_val_00047201.JPEG +ILSVRC2012_val_00023755.JPEG +ILSVRC2012_val_00006761.JPEG +ILSVRC2012_val_00005336.JPEG +ILSVRC2012_val_00002481.JPEG +ILSVRC2012_val_00036505.JPEG +ILSVRC2012_val_00042366.JPEG +ILSVRC2012_val_00015770.JPEG +ILSVRC2012_val_00005154.JPEG +ILSVRC2012_val_00032494.JPEG +ILSVRC2012_val_00013367.JPEG +ILSVRC2012_val_00011415.JPEG +ILSVRC2012_val_00030635.JPEG +ILSVRC2012_val_00010707.JPEG +ILSVRC2012_val_00023534.JPEG +ILSVRC2012_val_00027156.JPEG +ILSVRC2012_val_00038828.JPEG +ILSVRC2012_val_00021369.JPEG +ILSVRC2012_val_00009037.JPEG +ILSVRC2012_val_00025816.JPEG +ILSVRC2012_val_00000528.JPEG +ILSVRC2012_val_00015516.JPEG +ILSVRC2012_val_00013365.JPEG +ILSVRC2012_val_00001375.JPEG +ILSVRC2012_val_00013254.JPEG +ILSVRC2012_val_00024024.JPEG +ILSVRC2012_val_00049743.JPEG +ILSVRC2012_val_00024668.JPEG +ILSVRC2012_val_00019979.JPEG +ILSVRC2012_val_00049403.JPEG +ILSVRC2012_val_00033031.JPEG +ILSVRC2012_val_00026435.JPEG +ILSVRC2012_val_00005014.JPEG +ILSVRC2012_val_00044519.JPEG +ILSVRC2012_val_00041400.JPEG +ILSVRC2012_val_00023153.JPEG +ILSVRC2012_val_00023174.JPEG +ILSVRC2012_val_00036031.JPEG +ILSVRC2012_val_00003064.JPEG +ILSVRC2012_val_00009806.JPEG +ILSVRC2012_val_00001591.JPEG +ILSVRC2012_val_00037113.JPEG +ILSVRC2012_val_00010688.JPEG +ILSVRC2012_val_00017687.JPEG +ILSVRC2012_val_00028076.JPEG +ILSVRC2012_val_00007170.JPEG +ILSVRC2012_val_00000497.JPEG +ILSVRC2012_val_00025752.JPEG +ILSVRC2012_val_00039846.JPEG +ILSVRC2012_val_00047242.JPEG +ILSVRC2012_val_00036323.JPEG +ILSVRC2012_val_00042671.JPEG +ILSVRC2012_val_00028484.JPEG +ILSVRC2012_val_00021998.JPEG +ILSVRC2012_val_00027139.JPEG +ILSVRC2012_val_00039510.JPEG +ILSVRC2012_val_00009327.JPEG +ILSVRC2012_val_00022843.JPEG +ILSVRC2012_val_00031504.JPEG +ILSVRC2012_val_00017809.JPEG +ILSVRC2012_val_00030672.JPEG +ILSVRC2012_val_00000827.JPEG +ILSVRC2012_val_00011571.JPEG +ILSVRC2012_val_00049690.JPEG +ILSVRC2012_val_00045433.JPEG +ILSVRC2012_val_00033826.JPEG +ILSVRC2012_val_00001737.JPEG +ILSVRC2012_val_00027087.JPEG +ILSVRC2012_val_00032310.JPEG +ILSVRC2012_val_00047735.JPEG +ILSVRC2012_val_00036664.JPEG +ILSVRC2012_val_00039458.JPEG +ILSVRC2012_val_00026424.JPEG +ILSVRC2012_val_00012523.JPEG +ILSVRC2012_val_00015160.JPEG +ILSVRC2012_val_00010941.JPEG +ILSVRC2012_val_00039656.JPEG +ILSVRC2012_val_00031896.JPEG +ILSVRC2012_val_00017885.JPEG +ILSVRC2012_val_00038186.JPEG +ILSVRC2012_val_00021767.JPEG +ILSVRC2012_val_00036012.JPEG +ILSVRC2012_val_00048380.JPEG +ILSVRC2012_val_00013820.JPEG +ILSVRC2012_val_00026529.JPEG +ILSVRC2012_val_00006710.JPEG +ILSVRC2012_val_00020398.JPEG +ILSVRC2012_val_00047326.JPEG +ILSVRC2012_val_00012669.JPEG +ILSVRC2012_val_00026976.JPEG +ILSVRC2012_val_00004278.JPEG +ILSVRC2012_val_00016393.JPEG +ILSVRC2012_val_00047266.JPEG +ILSVRC2012_val_00032641.JPEG +ILSVRC2012_val_00006798.JPEG +ILSVRC2012_val_00024817.JPEG +ILSVRC2012_val_00029226.JPEG +ILSVRC2012_val_00034945.JPEG +ILSVRC2012_val_00035375.JPEG +ILSVRC2012_val_00004798.JPEG +ILSVRC2012_val_00013497.JPEG +ILSVRC2012_val_00025542.JPEG +ILSVRC2012_val_00020380.JPEG +ILSVRC2012_val_00002931.JPEG +ILSVRC2012_val_00018754.JPEG +ILSVRC2012_val_00042797.JPEG +ILSVRC2012_val_00030382.JPEG +ILSVRC2012_val_00023236.JPEG +ILSVRC2012_val_00037214.JPEG +ILSVRC2012_val_00000135.JPEG +ILSVRC2012_val_00008748.JPEG +ILSVRC2012_val_00011084.JPEG +ILSVRC2012_val_00037563.JPEG +ILSVRC2012_val_00011226.JPEG +ILSVRC2012_val_00023323.JPEG +ILSVRC2012_val_00001730.JPEG +ILSVRC2012_val_00006939.JPEG +ILSVRC2012_val_00016731.JPEG +ILSVRC2012_val_00033291.JPEG +ILSVRC2012_val_00005491.JPEG +ILSVRC2012_val_00014689.JPEG +ILSVRC2012_val_00005596.JPEG +ILSVRC2012_val_00014184.JPEG +ILSVRC2012_val_00031508.JPEG +ILSVRC2012_val_00002880.JPEG +ILSVRC2012_val_00002616.JPEG +ILSVRC2012_val_00013140.JPEG +ILSVRC2012_val_00029193.JPEG +ILSVRC2012_val_00001271.JPEG +ILSVRC2012_val_00029653.JPEG +ILSVRC2012_val_00036161.JPEG +ILSVRC2012_val_00018733.JPEG +ILSVRC2012_val_00023571.JPEG +ILSVRC2012_val_00037044.JPEG +ILSVRC2012_val_00047272.JPEG +ILSVRC2012_val_00045497.JPEG +ILSVRC2012_val_00021722.JPEG +ILSVRC2012_val_00013570.JPEG +ILSVRC2012_val_00031753.JPEG +ILSVRC2012_val_00034837.JPEG +ILSVRC2012_val_00001774.JPEG +ILSVRC2012_val_00048482.JPEG +ILSVRC2012_val_00003848.JPEG +ILSVRC2012_val_00009515.JPEG +ILSVRC2012_val_00028551.JPEG +ILSVRC2012_val_00013078.JPEG +ILSVRC2012_val_00043860.JPEG +ILSVRC2012_val_00025865.JPEG +ILSVRC2012_val_00027787.JPEG +ILSVRC2012_val_00027684.JPEG +ILSVRC2012_val_00034322.JPEG +ILSVRC2012_val_00026524.JPEG +ILSVRC2012_val_00047833.JPEG +ILSVRC2012_val_00012522.JPEG +ILSVRC2012_val_00033748.JPEG +ILSVRC2012_val_00037257.JPEG +ILSVRC2012_val_00036908.JPEG +ILSVRC2012_val_00015542.JPEG +ILSVRC2012_val_00025141.JPEG +ILSVRC2012_val_00030907.JPEG +ILSVRC2012_val_00048147.JPEG +ILSVRC2012_val_00043921.JPEG +ILSVRC2012_val_00010771.JPEG +ILSVRC2012_val_00019635.JPEG +ILSVRC2012_val_00005907.JPEG +ILSVRC2012_val_00024121.JPEG +ILSVRC2012_val_00002018.JPEG +ILSVRC2012_val_00014408.JPEG +ILSVRC2012_val_00046525.JPEG +ILSVRC2012_val_00032221.JPEG +ILSVRC2012_val_00036459.JPEG +ILSVRC2012_val_00003479.JPEG +ILSVRC2012_val_00005618.JPEG +ILSVRC2012_val_00002493.JPEG +ILSVRC2012_val_00015807.JPEG +ILSVRC2012_val_00011386.JPEG +ILSVRC2012_val_00020733.JPEG +ILSVRC2012_val_00002482.JPEG +ILSVRC2012_val_00034622.JPEG +ILSVRC2012_val_00012771.JPEG +ILSVRC2012_val_00043302.JPEG +ILSVRC2012_val_00022408.JPEG +ILSVRC2012_val_00041327.JPEG +ILSVRC2012_val_00019171.JPEG +ILSVRC2012_val_00043995.JPEG +ILSVRC2012_val_00041223.JPEG +ILSVRC2012_val_00008465.JPEG +ILSVRC2012_val_00038063.JPEG +ILSVRC2012_val_00039038.JPEG +ILSVRC2012_val_00033159.JPEG +ILSVRC2012_val_00011382.JPEG +ILSVRC2012_val_00044823.JPEG +ILSVRC2012_val_00007399.JPEG +ILSVRC2012_val_00047507.JPEG +ILSVRC2012_val_00031973.JPEG +ILSVRC2012_val_00032965.JPEG +ILSVRC2012_val_00004937.JPEG +ILSVRC2012_val_00024038.JPEG +ILSVRC2012_val_00008984.JPEG +ILSVRC2012_val_00005456.JPEG +ILSVRC2012_val_00014141.JPEG +ILSVRC2012_val_00049914.JPEG +ILSVRC2012_val_00033036.JPEG +ILSVRC2012_val_00035721.JPEG +ILSVRC2012_val_00009258.JPEG +ILSVRC2012_val_00009894.JPEG +ILSVRC2012_val_00038122.JPEG +ILSVRC2012_val_00023047.JPEG +ILSVRC2012_val_00005399.JPEG +ILSVRC2012_val_00029479.JPEG +ILSVRC2012_val_00008114.JPEG +ILSVRC2012_val_00037288.JPEG +ILSVRC2012_val_00014414.JPEG +ILSVRC2012_val_00049021.JPEG +ILSVRC2012_val_00032807.JPEG +ILSVRC2012_val_00011872.JPEG +ILSVRC2012_val_00020290.JPEG +ILSVRC2012_val_00025221.JPEG +ILSVRC2012_val_00008645.JPEG +ILSVRC2012_val_00023939.JPEG +ILSVRC2012_val_00030926.JPEG +ILSVRC2012_val_00039414.JPEG +ILSVRC2012_val_00036206.JPEG +ILSVRC2012_val_00010715.JPEG +ILSVRC2012_val_00044384.JPEG +ILSVRC2012_val_00024903.JPEG +ILSVRC2012_val_00010352.JPEG +ILSVRC2012_val_00001360.JPEG +ILSVRC2012_val_00047931.JPEG +ILSVRC2012_val_00032664.JPEG +ILSVRC2012_val_00029760.JPEG +ILSVRC2012_val_00041369.JPEG +ILSVRC2012_val_00044874.JPEG +ILSVRC2012_val_00028223.JPEG +ILSVRC2012_val_00047818.JPEG +ILSVRC2012_val_00016276.JPEG +ILSVRC2012_val_00017543.JPEG +ILSVRC2012_val_00006048.JPEG +ILSVRC2012_val_00037206.JPEG +ILSVRC2012_val_00010147.JPEG +ILSVRC2012_val_00014000.JPEG +ILSVRC2012_val_00010511.JPEG +ILSVRC2012_val_00036604.JPEG +ILSVRC2012_val_00025713.JPEG +ILSVRC2012_val_00037759.JPEG +ILSVRC2012_val_00038048.JPEG +ILSVRC2012_val_00043154.JPEG +ILSVRC2012_val_00035140.JPEG +ILSVRC2012_val_00030110.JPEG +ILSVRC2012_val_00037239.JPEG +ILSVRC2012_val_00046044.JPEG +ILSVRC2012_val_00038143.JPEG +ILSVRC2012_val_00014965.JPEG +ILSVRC2012_val_00016544.JPEG +ILSVRC2012_val_00019020.JPEG +ILSVRC2012_val_00035467.JPEG +ILSVRC2012_val_00027289.JPEG +ILSVRC2012_val_00023677.JPEG +ILSVRC2012_val_00048537.JPEG +ILSVRC2012_val_00041241.JPEG +ILSVRC2012_val_00044502.JPEG +ILSVRC2012_val_00003035.JPEG +ILSVRC2012_val_00047662.JPEG +ILSVRC2012_val_00001398.JPEG +ILSVRC2012_val_00039204.JPEG +ILSVRC2012_val_00022559.JPEG +ILSVRC2012_val_00008368.JPEG +ILSVRC2012_val_00033096.JPEG +ILSVRC2012_val_00047351.JPEG +ILSVRC2012_val_00039635.JPEG +ILSVRC2012_val_00039703.JPEG +ILSVRC2012_val_00029755.JPEG +ILSVRC2012_val_00022114.JPEG +ILSVRC2012_val_00005679.JPEG +ILSVRC2012_val_00047827.JPEG +ILSVRC2012_val_00032430.JPEG +ILSVRC2012_val_00011076.JPEG +ILSVRC2012_val_00047902.JPEG +ILSVRC2012_val_00003982.JPEG +ILSVRC2012_val_00004243.JPEG +ILSVRC2012_val_00043230.JPEG +ILSVRC2012_val_00020292.JPEG +ILSVRC2012_val_00044219.JPEG +ILSVRC2012_val_00019123.JPEG +ILSVRC2012_val_00032522.JPEG +ILSVRC2012_val_00017295.JPEG +ILSVRC2012_val_00035834.JPEG +ILSVRC2012_val_00037647.JPEG +ILSVRC2012_val_00026070.JPEG +ILSVRC2012_val_00041269.JPEG +ILSVRC2012_val_00031611.JPEG +ILSVRC2012_val_00029664.JPEG +ILSVRC2012_val_00013693.JPEG +ILSVRC2012_val_00001840.JPEG +ILSVRC2012_val_00040194.JPEG +ILSVRC2012_val_00040940.JPEG +ILSVRC2012_val_00049385.JPEG +ILSVRC2012_val_00001100.JPEG +ILSVRC2012_val_00003868.JPEG +ILSVRC2012_val_00001894.JPEG +ILSVRC2012_val_00038766.JPEG +ILSVRC2012_val_00017576.JPEG +ILSVRC2012_val_00017462.JPEG +ILSVRC2012_val_00017485.JPEG +ILSVRC2012_val_00045020.JPEG +ILSVRC2012_val_00027846.JPEG +ILSVRC2012_val_00026762.JPEG +ILSVRC2012_val_00022174.JPEG +ILSVRC2012_val_00038702.JPEG +ILSVRC2012_val_00032721.JPEG +ILSVRC2012_val_00038270.JPEG +ILSVRC2012_val_00049299.JPEG +ILSVRC2012_val_00033499.JPEG +ILSVRC2012_val_00046257.JPEG +ILSVRC2012_val_00018381.JPEG +ILSVRC2012_val_00009651.JPEG +ILSVRC2012_val_00032305.JPEG +ILSVRC2012_val_00003718.JPEG +ILSVRC2012_val_00006326.JPEG +ILSVRC2012_val_00001916.JPEG +ILSVRC2012_val_00039118.JPEG +ILSVRC2012_val_00048732.JPEG +ILSVRC2012_val_00011364.JPEG +ILSVRC2012_val_00041867.JPEG +ILSVRC2012_val_00031951.JPEG +ILSVRC2012_val_00010436.JPEG +ILSVRC2012_val_00044302.JPEG +ILSVRC2012_val_00036652.JPEG +ILSVRC2012_val_00017962.JPEG +ILSVRC2012_val_00015922.JPEG +ILSVRC2012_val_00028691.JPEG +ILSVRC2012_val_00023703.JPEG +ILSVRC2012_val_00027118.JPEG +ILSVRC2012_val_00031088.JPEG +ILSVRC2012_val_00048035.JPEG +ILSVRC2012_val_00008881.JPEG +ILSVRC2012_val_00049787.JPEG +ILSVRC2012_val_00000899.JPEG +ILSVRC2012_val_00003070.JPEG +ILSVRC2012_val_00038633.JPEG +ILSVRC2012_val_00035694.JPEG +ILSVRC2012_val_00029887.JPEG +ILSVRC2012_val_00013835.JPEG +ILSVRC2012_val_00031751.JPEG +ILSVRC2012_val_00016772.JPEG +ILSVRC2012_val_00028999.JPEG +ILSVRC2012_val_00018409.JPEG +ILSVRC2012_val_00026861.JPEG +ILSVRC2012_val_00034830.JPEG +ILSVRC2012_val_00034926.JPEG +ILSVRC2012_val_00043571.JPEG +ILSVRC2012_val_00003241.JPEG +ILSVRC2012_val_00028256.JPEG +ILSVRC2012_val_00030584.JPEG +ILSVRC2012_val_00005095.JPEG +ILSVRC2012_val_00018816.JPEG +ILSVRC2012_val_00005278.JPEG +ILSVRC2012_val_00001729.JPEG +ILSVRC2012_val_00016595.JPEG +ILSVRC2012_val_00029643.JPEG +ILSVRC2012_val_00039985.JPEG +ILSVRC2012_val_00043800.JPEG +ILSVRC2012_val_00003554.JPEG +ILSVRC2012_val_00012793.JPEG +ILSVRC2012_val_00009880.JPEG +ILSVRC2012_val_00048828.JPEG +ILSVRC2012_val_00029320.JPEG +ILSVRC2012_val_00044601.JPEG +ILSVRC2012_val_00036231.JPEG +ILSVRC2012_val_00001570.JPEG +ILSVRC2012_val_00008314.JPEG +ILSVRC2012_val_00003759.JPEG +ILSVRC2012_val_00045292.JPEG +ILSVRC2012_val_00001537.JPEG +ILSVRC2012_val_00016062.JPEG +ILSVRC2012_val_00010649.JPEG +ILSVRC2012_val_00005086.JPEG +ILSVRC2012_val_00020472.JPEG +ILSVRC2012_val_00016376.JPEG +ILSVRC2012_val_00006484.JPEG +ILSVRC2012_val_00006818.JPEG +ILSVRC2012_val_00005477.JPEG +ILSVRC2012_val_00000758.JPEG +ILSVRC2012_val_00034580.JPEG +ILSVRC2012_val_00040628.JPEG +ILSVRC2012_val_00025019.JPEG +ILSVRC2012_val_00046997.JPEG +ILSVRC2012_val_00039483.JPEG +ILSVRC2012_val_00012646.JPEG +ILSVRC2012_val_00022453.JPEG +ILSVRC2012_val_00002412.JPEG +ILSVRC2012_val_00006101.JPEG +ILSVRC2012_val_00022385.JPEG +ILSVRC2012_val_00030333.JPEG +ILSVRC2012_val_00036622.JPEG +ILSVRC2012_val_00011425.JPEG +ILSVRC2012_val_00029540.JPEG +ILSVRC2012_val_00003227.JPEG +ILSVRC2012_val_00038888.JPEG +ILSVRC2012_val_00034908.JPEG +ILSVRC2012_val_00042152.JPEG +ILSVRC2012_val_00024803.JPEG +ILSVRC2012_val_00026906.JPEG +ILSVRC2012_val_00041262.JPEG +ILSVRC2012_val_00015979.JPEG +ILSVRC2012_val_00028790.JPEG +ILSVRC2012_val_00040478.JPEG +ILSVRC2012_val_00036017.JPEG +ILSVRC2012_val_00030186.JPEG +ILSVRC2012_val_00035088.JPEG +ILSVRC2012_val_00049286.JPEG +ILSVRC2012_val_00006379.JPEG +ILSVRC2012_val_00042853.JPEG +ILSVRC2012_val_00004196.JPEG +ILSVRC2012_val_00037488.JPEG +ILSVRC2012_val_00001353.JPEG +ILSVRC2012_val_00036304.JPEG +ILSVRC2012_val_00045517.JPEG +ILSVRC2012_val_00003291.JPEG +ILSVRC2012_val_00029734.JPEG +ILSVRC2012_val_00009106.JPEG +ILSVRC2012_val_00020087.JPEG +ILSVRC2012_val_00003786.JPEG +ILSVRC2012_val_00044277.JPEG +ILSVRC2012_val_00043575.JPEG +ILSVRC2012_val_00045955.JPEG +ILSVRC2012_val_00042236.JPEG +ILSVRC2012_val_00007953.JPEG +ILSVRC2012_val_00006787.JPEG +ILSVRC2012_val_00021956.JPEG +ILSVRC2012_val_00000322.JPEG +ILSVRC2012_val_00031844.JPEG +ILSVRC2012_val_00036936.JPEG +ILSVRC2012_val_00034424.JPEG +ILSVRC2012_val_00006994.JPEG +ILSVRC2012_val_00040676.JPEG +ILSVRC2012_val_00011749.JPEG +ILSVRC2012_val_00003317.JPEG +ILSVRC2012_val_00009013.JPEG +ILSVRC2012_val_00023386.JPEG +ILSVRC2012_val_00025628.JPEG +ILSVRC2012_val_00015736.JPEG +ILSVRC2012_val_00030808.JPEG +ILSVRC2012_val_00031380.JPEG +ILSVRC2012_val_00035777.JPEG +ILSVRC2012_val_00024722.JPEG +ILSVRC2012_val_00013158.JPEG +ILSVRC2012_val_00047989.JPEG +ILSVRC2012_val_00043091.JPEG +ILSVRC2012_val_00003066.JPEG +ILSVRC2012_val_00017016.JPEG +ILSVRC2012_val_00030188.JPEG +ILSVRC2012_val_00042585.JPEG +ILSVRC2012_val_00037261.JPEG +ILSVRC2012_val_00013331.JPEG +ILSVRC2012_val_00004888.JPEG +ILSVRC2012_val_00028013.JPEG +ILSVRC2012_val_00017265.JPEG +ILSVRC2012_val_00003048.JPEG +ILSVRC2012_val_00013356.JPEG +ILSVRC2012_val_00024811.JPEG +ILSVRC2012_val_00023836.JPEG +ILSVRC2012_val_00020958.JPEG +ILSVRC2012_val_00007571.JPEG +ILSVRC2012_val_00022137.JPEG +ILSVRC2012_val_00049928.JPEG +ILSVRC2012_val_00000404.JPEG +ILSVRC2012_val_00035404.JPEG +ILSVRC2012_val_00022471.JPEG +ILSVRC2012_val_00020125.JPEG +ILSVRC2012_val_00002021.JPEG +ILSVRC2012_val_00009245.JPEG +ILSVRC2012_val_00022094.JPEG +ILSVRC2012_val_00045984.JPEG +ILSVRC2012_val_00017407.JPEG +ILSVRC2012_val_00006886.JPEG +ILSVRC2012_val_00046123.JPEG +ILSVRC2012_val_00048988.JPEG +ILSVRC2012_val_00047495.JPEG +ILSVRC2012_val_00000996.JPEG +ILSVRC2012_val_00027511.JPEG +ILSVRC2012_val_00037891.JPEG +ILSVRC2012_val_00009687.JPEG +ILSVRC2012_val_00043475.JPEG +ILSVRC2012_val_00034760.JPEG +ILSVRC2012_val_00005557.JPEG +ILSVRC2012_val_00043562.JPEG +ILSVRC2012_val_00010685.JPEG +ILSVRC2012_val_00044440.JPEG +ILSVRC2012_val_00028249.JPEG +ILSVRC2012_val_00020931.JPEG +ILSVRC2012_val_00001117.JPEG +ILSVRC2012_val_00033382.JPEG +ILSVRC2012_val_00049517.JPEG +ILSVRC2012_val_00012415.JPEG +ILSVRC2012_val_00046017.JPEG +ILSVRC2012_val_00048110.JPEG +ILSVRC2012_val_00029294.JPEG +ILSVRC2012_val_00021012.JPEG +ILSVRC2012_val_00002932.JPEG +ILSVRC2012_val_00023370.JPEG +ILSVRC2012_val_00044269.JPEG +ILSVRC2012_val_00041109.JPEG +ILSVRC2012_val_00003521.JPEG +ILSVRC2012_val_00041790.JPEG +ILSVRC2012_val_00026386.JPEG +ILSVRC2012_val_00031970.JPEG +ILSVRC2012_val_00036389.JPEG +ILSVRC2012_val_00016927.JPEG +ILSVRC2012_val_00045838.JPEG +ILSVRC2012_val_00029025.JPEG +ILSVRC2012_val_00029919.JPEG +ILSVRC2012_val_00045995.JPEG +ILSVRC2012_val_00049404.JPEG +ILSVRC2012_val_00028213.JPEG +ILSVRC2012_val_00021552.JPEG +ILSVRC2012_val_00011462.JPEG +ILSVRC2012_val_00012120.JPEG +ILSVRC2012_val_00031518.JPEG +ILSVRC2012_val_00001755.JPEG +ILSVRC2012_val_00043483.JPEG +ILSVRC2012_val_00047619.JPEG +ILSVRC2012_val_00035608.JPEG +ILSVRC2012_val_00045005.JPEG +ILSVRC2012_val_00043222.JPEG +ILSVRC2012_val_00016323.JPEG +ILSVRC2012_val_00043819.JPEG +ILSVRC2012_val_00006955.JPEG +ILSVRC2012_val_00034282.JPEG +ILSVRC2012_val_00039261.JPEG +ILSVRC2012_val_00006523.JPEG +ILSVRC2012_val_00025410.JPEG +ILSVRC2012_val_00001433.JPEG +ILSVRC2012_val_00047901.JPEG +ILSVRC2012_val_00009908.JPEG +ILSVRC2012_val_00007784.JPEG +ILSVRC2012_val_00008784.JPEG +ILSVRC2012_val_00033738.JPEG +ILSVRC2012_val_00004560.JPEG +ILSVRC2012_val_00044617.JPEG +ILSVRC2012_val_00043150.JPEG +ILSVRC2012_val_00049023.JPEG +ILSVRC2012_val_00038347.JPEG +ILSVRC2012_val_00039363.JPEG +ILSVRC2012_val_00037161.JPEG +ILSVRC2012_val_00012640.JPEG +ILSVRC2012_val_00027094.JPEG +ILSVRC2012_val_00029996.JPEG +ILSVRC2012_val_00046781.JPEG +ILSVRC2012_val_00013109.JPEG +ILSVRC2012_val_00035398.JPEG +ILSVRC2012_val_00042465.JPEG +ILSVRC2012_val_00003967.JPEG +ILSVRC2012_val_00022517.JPEG +ILSVRC2012_val_00007171.JPEG +ILSVRC2012_val_00046469.JPEG +ILSVRC2012_val_00042136.JPEG +ILSVRC2012_val_00048739.JPEG +ILSVRC2012_val_00024516.JPEG +ILSVRC2012_val_00030376.JPEG +ILSVRC2012_val_00022400.JPEG +ILSVRC2012_val_00029228.JPEG +ILSVRC2012_val_00002017.JPEG +ILSVRC2012_val_00049261.JPEG +ILSVRC2012_val_00035203.JPEG +ILSVRC2012_val_00016722.JPEG +ILSVRC2012_val_00047226.JPEG +ILSVRC2012_val_00001556.JPEG +ILSVRC2012_val_00039535.JPEG +ILSVRC2012_val_00023588.JPEG +ILSVRC2012_val_00036212.JPEG +ILSVRC2012_val_00026723.JPEG +ILSVRC2012_val_00032167.JPEG +ILSVRC2012_val_00017004.JPEG +ILSVRC2012_val_00045489.JPEG +ILSVRC2012_val_00003631.JPEG +ILSVRC2012_val_00042571.JPEG +ILSVRC2012_val_00041284.JPEG +ILSVRC2012_val_00035772.JPEG +ILSVRC2012_val_00033797.JPEG +ILSVRC2012_val_00011165.JPEG +ILSVRC2012_val_00032591.JPEG +ILSVRC2012_val_00016995.JPEG +ILSVRC2012_val_00010645.JPEG +ILSVRC2012_val_00049371.JPEG +ILSVRC2012_val_00020545.JPEG +ILSVRC2012_val_00039430.JPEG +ILSVRC2012_val_00019767.JPEG +ILSVRC2012_val_00039222.JPEG +ILSVRC2012_val_00016588.JPEG +ILSVRC2012_val_00039019.JPEG +ILSVRC2012_val_00021775.JPEG +ILSVRC2012_val_00045721.JPEG +ILSVRC2012_val_00001666.JPEG +ILSVRC2012_val_00041768.JPEG +ILSVRC2012_val_00014957.JPEG +ILSVRC2012_val_00038019.JPEG +ILSVRC2012_val_00038731.JPEG +ILSVRC2012_val_00049518.JPEG +ILSVRC2012_val_00028903.JPEG +ILSVRC2012_val_00047887.JPEG +ILSVRC2012_val_00049926.JPEG +ILSVRC2012_val_00032111.JPEG +ILSVRC2012_val_00034358.JPEG +ILSVRC2012_val_00016019.JPEG +ILSVRC2012_val_00010221.JPEG +ILSVRC2012_val_00042170.JPEG +ILSVRC2012_val_00000832.JPEG +ILSVRC2012_val_00043534.JPEG +ILSVRC2012_val_00049387.JPEG +ILSVRC2012_val_00043140.JPEG +ILSVRC2012_val_00022151.JPEG +ILSVRC2012_val_00014128.JPEG +ILSVRC2012_val_00002211.JPEG +ILSVRC2012_val_00025487.JPEG +ILSVRC2012_val_00034507.JPEG +ILSVRC2012_val_00038410.JPEG +ILSVRC2012_val_00028000.JPEG +ILSVRC2012_val_00049130.JPEG +ILSVRC2012_val_00038018.JPEG +ILSVRC2012_val_00002262.JPEG +ILSVRC2012_val_00043992.JPEG +ILSVRC2012_val_00007252.JPEG +ILSVRC2012_val_00022061.JPEG +ILSVRC2012_val_00017137.JPEG +ILSVRC2012_val_00042384.JPEG +ILSVRC2012_val_00039246.JPEG +ILSVRC2012_val_00001875.JPEG +ILSVRC2012_val_00032960.JPEG +ILSVRC2012_val_00003123.JPEG +ILSVRC2012_val_00006903.JPEG +ILSVRC2012_val_00023394.JPEG +ILSVRC2012_val_00001497.JPEG +ILSVRC2012_val_00026018.JPEG +ILSVRC2012_val_00018105.JPEG +ILSVRC2012_val_00035215.JPEG +ILSVRC2012_val_00039759.JPEG +ILSVRC2012_val_00046988.JPEG +ILSVRC2012_val_00012314.JPEG +ILSVRC2012_val_00033769.JPEG +ILSVRC2012_val_00045328.JPEG +ILSVRC2012_val_00016416.JPEG +ILSVRC2012_val_00000850.JPEG +ILSVRC2012_val_00004022.JPEG +ILSVRC2012_val_00036497.JPEG +ILSVRC2012_val_00025198.JPEG +ILSVRC2012_val_00005199.JPEG +ILSVRC2012_val_00012549.JPEG +ILSVRC2012_val_00047046.JPEG +ILSVRC2012_val_00045591.JPEG +ILSVRC2012_val_00014145.JPEG +ILSVRC2012_val_00009287.JPEG +ILSVRC2012_val_00016576.JPEG +ILSVRC2012_val_00046868.JPEG +ILSVRC2012_val_00049634.JPEG +ILSVRC2012_val_00039238.JPEG +ILSVRC2012_val_00033605.JPEG +ILSVRC2012_val_00011518.JPEG +ILSVRC2012_val_00023675.JPEG +ILSVRC2012_val_00029282.JPEG +ILSVRC2012_val_00022657.JPEG +ILSVRC2012_val_00013839.JPEG +ILSVRC2012_val_00013074.JPEG +ILSVRC2012_val_00014987.JPEG +ILSVRC2012_val_00044480.JPEG +ILSVRC2012_val_00014632.JPEG +ILSVRC2012_val_00002935.JPEG +ILSVRC2012_val_00014674.JPEG +ILSVRC2012_val_00039811.JPEG +ILSVRC2012_val_00037474.JPEG +ILSVRC2012_val_00033153.JPEG +ILSVRC2012_val_00005537.JPEG +ILSVRC2012_val_00021388.JPEG +ILSVRC2012_val_00035717.JPEG +ILSVRC2012_val_00027501.JPEG +ILSVRC2012_val_00033262.JPEG +ILSVRC2012_val_00028253.JPEG +ILSVRC2012_val_00006535.JPEG +ILSVRC2012_val_00014201.JPEG +ILSVRC2012_val_00011976.JPEG +ILSVRC2012_val_00019377.JPEG +ILSVRC2012_val_00033251.JPEG +ILSVRC2012_val_00011157.JPEG +ILSVRC2012_val_00008469.JPEG +ILSVRC2012_val_00046268.JPEG +ILSVRC2012_val_00039320.JPEG +ILSVRC2012_val_00015869.JPEG +ILSVRC2012_val_00001651.JPEG +ILSVRC2012_val_00046351.JPEG +ILSVRC2012_val_00024579.JPEG +ILSVRC2012_val_00003919.JPEG +ILSVRC2012_val_00002967.JPEG +ILSVRC2012_val_00044037.JPEG +ILSVRC2012_val_00012832.JPEG +ILSVRC2012_val_00048282.JPEG +ILSVRC2012_val_00013389.JPEG +ILSVRC2012_val_00038304.JPEG +ILSVRC2012_val_00033708.JPEG +ILSVRC2012_val_00032684.JPEG +ILSVRC2012_val_00028251.JPEG +ILSVRC2012_val_00006302.JPEG +ILSVRC2012_val_00018415.JPEG +ILSVRC2012_val_00009804.JPEG +ILSVRC2012_val_00018453.JPEG +ILSVRC2012_val_00025255.JPEG +ILSVRC2012_val_00039287.JPEG +ILSVRC2012_val_00007637.JPEG +ILSVRC2012_val_00023800.JPEG +ILSVRC2012_val_00001300.JPEG +ILSVRC2012_val_00016966.JPEG +ILSVRC2012_val_00005064.JPEG +ILSVRC2012_val_00045068.JPEG +ILSVRC2012_val_00005578.JPEG +ILSVRC2012_val_00036839.JPEG +ILSVRC2012_val_00008215.JPEG +ILSVRC2012_val_00016104.JPEG +ILSVRC2012_val_00006892.JPEG +ILSVRC2012_val_00003713.JPEG +ILSVRC2012_val_00037319.JPEG +ILSVRC2012_val_00009054.JPEG +ILSVRC2012_val_00040830.JPEG +ILSVRC2012_val_00007243.JPEG +ILSVRC2012_val_00030086.JPEG +ILSVRC2012_val_00005930.JPEG +ILSVRC2012_val_00002861.JPEG +ILSVRC2012_val_00024411.JPEG +ILSVRC2012_val_00038356.JPEG +ILSVRC2012_val_00034793.JPEG +ILSVRC2012_val_00012932.JPEG +ILSVRC2012_val_00016076.JPEG +ILSVRC2012_val_00015315.JPEG +ILSVRC2012_val_00011659.JPEG +ILSVRC2012_val_00002102.JPEG +ILSVRC2012_val_00024781.JPEG +ILSVRC2012_val_00017704.JPEG +ILSVRC2012_val_00014979.JPEG +ILSVRC2012_val_00018211.JPEG +ILSVRC2012_val_00045469.JPEG +ILSVRC2012_val_00009434.JPEG +ILSVRC2012_val_00012139.JPEG +ILSVRC2012_val_00042462.JPEG +ILSVRC2012_val_00046771.JPEG +ILSVRC2012_val_00032258.JPEG +ILSVRC2012_val_00041429.JPEG +ILSVRC2012_val_00007600.JPEG +ILSVRC2012_val_00048919.JPEG +ILSVRC2012_val_00045803.JPEG +ILSVRC2012_val_00028896.JPEG +ILSVRC2012_val_00037286.JPEG +ILSVRC2012_val_00042990.JPEG +ILSVRC2012_val_00025106.JPEG +ILSVRC2012_val_00049881.JPEG +ILSVRC2012_val_00044645.JPEG +ILSVRC2012_val_00044742.JPEG +ILSVRC2012_val_00021860.JPEG +ILSVRC2012_val_00034001.JPEG +ILSVRC2012_val_00031221.JPEG +ILSVRC2012_val_00023638.JPEG +ILSVRC2012_val_00028505.JPEG +ILSVRC2012_val_00047751.JPEG +ILSVRC2012_val_00008547.JPEG +ILSVRC2012_val_00041146.JPEG +ILSVRC2012_val_00021899.JPEG +ILSVRC2012_val_00034294.JPEG +ILSVRC2012_val_00043531.JPEG +ILSVRC2012_val_00021923.JPEG +ILSVRC2012_val_00024518.JPEG +ILSVRC2012_val_00009886.JPEG +ILSVRC2012_val_00004272.JPEG +ILSVRC2012_val_00029981.JPEG +ILSVRC2012_val_00034581.JPEG +ILSVRC2012_val_00003886.JPEG +ILSVRC2012_val_00037220.JPEG +ILSVRC2012_val_00003288.JPEG +ILSVRC2012_val_00039046.JPEG +ILSVRC2012_val_00043582.JPEG +ILSVRC2012_val_00030708.JPEG +ILSVRC2012_val_00043035.JPEG +ILSVRC2012_val_00049154.JPEG +ILSVRC2012_val_00028218.JPEG +ILSVRC2012_val_00013923.JPEG +ILSVRC2012_val_00013161.JPEG +ILSVRC2012_val_00014367.JPEG +ILSVRC2012_val_00019367.JPEG +ILSVRC2012_val_00035878.JPEG +ILSVRC2012_val_00009679.JPEG +ILSVRC2012_val_00007082.JPEG +ILSVRC2012_val_00012892.JPEG +ILSVRC2012_val_00033925.JPEG +ILSVRC2012_val_00009413.JPEG +ILSVRC2012_val_00046813.JPEG +ILSVRC2012_val_00001661.JPEG +ILSVRC2012_val_00042285.JPEG +ILSVRC2012_val_00040852.JPEG +ILSVRC2012_val_00015359.JPEG +ILSVRC2012_val_00020957.JPEG +ILSVRC2012_val_00025783.JPEG +ILSVRC2012_val_00030109.JPEG +ILSVRC2012_val_00022774.JPEG +ILSVRC2012_val_00034910.JPEG +ILSVRC2012_val_00007648.JPEG +ILSVRC2012_val_00006623.JPEG +ILSVRC2012_val_00020328.JPEG +ILSVRC2012_val_00002259.JPEG +ILSVRC2012_val_00036937.JPEG +ILSVRC2012_val_00035346.JPEG +ILSVRC2012_val_00026328.JPEG +ILSVRC2012_val_00048543.JPEG +ILSVRC2012_val_00040367.JPEG +ILSVRC2012_val_00040393.JPEG +ILSVRC2012_val_00022065.JPEG +ILSVRC2012_val_00049418.JPEG +ILSVRC2012_val_00045692.JPEG +ILSVRC2012_val_00037178.JPEG +ILSVRC2012_val_00025995.JPEG +ILSVRC2012_val_00026322.JPEG +ILSVRC2012_val_00030106.JPEG +ILSVRC2012_val_00013426.JPEG +ILSVRC2012_val_00021421.JPEG +ILSVRC2012_val_00005431.JPEG +ILSVRC2012_val_00008922.JPEG +ILSVRC2012_val_00041198.JPEG +ILSVRC2012_val_00042589.JPEG +ILSVRC2012_val_00043506.JPEG +ILSVRC2012_val_00026811.JPEG +ILSVRC2012_val_00004301.JPEG +ILSVRC2012_val_00032248.JPEG +ILSVRC2012_val_00039104.JPEG +ILSVRC2012_val_00000640.JPEG +ILSVRC2012_val_00033802.JPEG +ILSVRC2012_val_00021697.JPEG +ILSVRC2012_val_00041294.JPEG +ILSVRC2012_val_00000676.JPEG +ILSVRC2012_val_00036799.JPEG +ILSVRC2012_val_00025483.JPEG +ILSVRC2012_val_00036977.JPEG +ILSVRC2012_val_00042477.JPEG +ILSVRC2012_val_00013783.JPEG +ILSVRC2012_val_00034747.JPEG +ILSVRC2012_val_00047655.JPEG +ILSVRC2012_val_00005727.JPEG +ILSVRC2012_val_00025260.JPEG +ILSVRC2012_val_00007438.JPEG +ILSVRC2012_val_00033935.JPEG +ILSVRC2012_val_00013821.JPEG +ILSVRC2012_val_00000584.JPEG +ILSVRC2012_val_00042493.JPEG +ILSVRC2012_val_00002613.JPEG +ILSVRC2012_val_00015992.JPEG +ILSVRC2012_val_00023276.JPEG +ILSVRC2012_val_00018592.JPEG +ILSVRC2012_val_00035863.JPEG +ILSVRC2012_val_00010278.JPEG +ILSVRC2012_val_00035189.JPEG +ILSVRC2012_val_00037078.JPEG +ILSVRC2012_val_00017111.JPEG +ILSVRC2012_val_00034832.JPEG +ILSVRC2012_val_00045695.JPEG +ILSVRC2012_val_00019131.JPEG +ILSVRC2012_val_00036608.JPEG +ILSVRC2012_val_00029725.JPEG +ILSVRC2012_val_00016272.JPEG +ILSVRC2012_val_00046589.JPEG +ILSVRC2012_val_00010939.JPEG +ILSVRC2012_val_00002535.JPEG +ILSVRC2012_val_00005525.JPEG +ILSVRC2012_val_00011581.JPEG +ILSVRC2012_val_00039225.JPEG +ILSVRC2012_val_00000972.JPEG +ILSVRC2012_val_00035677.JPEG +ILSVRC2012_val_00009131.JPEG +ILSVRC2012_val_00003329.JPEG +ILSVRC2012_val_00028833.JPEG +ILSVRC2012_val_00045011.JPEG +ILSVRC2012_val_00012411.JPEG +ILSVRC2012_val_00004896.JPEG +ILSVRC2012_val_00014937.JPEG +ILSVRC2012_val_00009889.JPEG +ILSVRC2012_val_00037013.JPEG +ILSVRC2012_val_00009597.JPEG +ILSVRC2012_val_00039841.JPEG +ILSVRC2012_val_00017326.JPEG +ILSVRC2012_val_00006370.JPEG +ILSVRC2012_val_00043337.JPEG +ILSVRC2012_val_00004321.JPEG +ILSVRC2012_val_00018478.JPEG +ILSVRC2012_val_00013498.JPEG +ILSVRC2012_val_00003867.JPEG +ILSVRC2012_val_00036188.JPEG +ILSVRC2012_val_00039898.JPEG +ILSVRC2012_val_00000741.JPEG +ILSVRC2012_val_00017926.JPEG +ILSVRC2012_val_00017624.JPEG +ILSVRC2012_val_00021359.JPEG +ILSVRC2012_val_00048263.JPEG +ILSVRC2012_val_00021798.JPEG +ILSVRC2012_val_00031455.JPEG +ILSVRC2012_val_00018175.JPEG +ILSVRC2012_val_00016018.JPEG +ILSVRC2012_val_00046434.JPEG +ILSVRC2012_val_00001793.JPEG +ILSVRC2012_val_00034889.JPEG +ILSVRC2012_val_00014832.JPEG +ILSVRC2012_val_00011726.JPEG +ILSVRC2012_val_00044568.JPEG +ILSVRC2012_val_00035397.JPEG +ILSVRC2012_val_00023681.JPEG +ILSVRC2012_val_00037607.JPEG +ILSVRC2012_val_00021851.JPEG +ILSVRC2012_val_00036327.JPEG +ILSVRC2012_val_00031988.JPEG +ILSVRC2012_val_00046097.JPEG +ILSVRC2012_val_00040164.JPEG +ILSVRC2012_val_00036097.JPEG +ILSVRC2012_val_00005138.JPEG +ILSVRC2012_val_00033940.JPEG +ILSVRC2012_val_00018776.JPEG +ILSVRC2012_val_00010752.JPEG +ILSVRC2012_val_00036066.JPEG +ILSVRC2012_val_00019281.JPEG +ILSVRC2012_val_00005198.JPEG +ILSVRC2012_val_00001956.JPEG +ILSVRC2012_val_00037478.JPEG +ILSVRC2012_val_00034183.JPEG +ILSVRC2012_val_00031429.JPEG +ILSVRC2012_val_00015590.JPEG +ILSVRC2012_val_00008487.JPEG +ILSVRC2012_val_00024805.JPEG +ILSVRC2012_val_00020420.JPEG +ILSVRC2012_val_00043277.JPEG +ILSVRC2012_val_00048623.JPEG +ILSVRC2012_val_00037872.JPEG +ILSVRC2012_val_00013310.JPEG +ILSVRC2012_val_00042311.JPEG +ILSVRC2012_val_00044045.JPEG +ILSVRC2012_val_00033149.JPEG +ILSVRC2012_val_00011465.JPEG +ILSVRC2012_val_00043197.JPEG +ILSVRC2012_val_00037228.JPEG +ILSVRC2012_val_00026343.JPEG +ILSVRC2012_val_00001412.JPEG +ILSVRC2012_val_00042377.JPEG +ILSVRC2012_val_00015452.JPEG +ILSVRC2012_val_00002542.JPEG +ILSVRC2012_val_00018732.JPEG +ILSVRC2012_val_00029756.JPEG +ILSVRC2012_val_00006945.JPEG +ILSVRC2012_val_00047433.JPEG +ILSVRC2012_val_00028849.JPEG +ILSVRC2012_val_00016253.JPEG +ILSVRC2012_val_00014536.JPEG +ILSVRC2012_val_00008927.JPEG +ILSVRC2012_val_00010981.JPEG +ILSVRC2012_val_00018867.JPEG +ILSVRC2012_val_00040832.JPEG +ILSVRC2012_val_00045284.JPEG +ILSVRC2012_val_00011227.JPEG +ILSVRC2012_val_00012428.JPEG +ILSVRC2012_val_00000408.JPEG +ILSVRC2012_val_00002069.JPEG +ILSVRC2012_val_00016719.JPEG +ILSVRC2012_val_00032406.JPEG +ILSVRC2012_val_00023290.JPEG +ILSVRC2012_val_00029525.JPEG +ILSVRC2012_val_00007996.JPEG +ILSVRC2012_val_00002695.JPEG +ILSVRC2012_val_00023476.JPEG +ILSVRC2012_val_00016477.JPEG +ILSVRC2012_val_00012919.JPEG +ILSVRC2012_val_00032894.JPEG +ILSVRC2012_val_00029163.JPEG +ILSVRC2012_val_00045817.JPEG +ILSVRC2012_val_00005733.JPEG +ILSVRC2012_val_00004810.JPEG +ILSVRC2012_val_00032082.JPEG +ILSVRC2012_val_00027534.JPEG +ILSVRC2012_val_00033582.JPEG +ILSVRC2012_val_00023845.JPEG +ILSVRC2012_val_00015973.JPEG +ILSVRC2012_val_00027861.JPEG +ILSVRC2012_val_00027197.JPEG +ILSVRC2012_val_00025990.JPEG +ILSVRC2012_val_00038534.JPEG +ILSVRC2012_val_00000538.JPEG +ILSVRC2012_val_00031063.JPEG +ILSVRC2012_val_00012319.JPEG +ILSVRC2012_val_00040523.JPEG +ILSVRC2012_val_00010722.JPEG +ILSVRC2012_val_00023597.JPEG +ILSVRC2012_val_00036265.JPEG +ILSVRC2012_val_00046430.JPEG +ILSVRC2012_val_00023261.JPEG +ILSVRC2012_val_00043244.JPEG +ILSVRC2012_val_00042362.JPEG +ILSVRC2012_val_00042820.JPEG +ILSVRC2012_val_00001087.JPEG +ILSVRC2012_val_00001765.JPEG +ILSVRC2012_val_00017678.JPEG +ILSVRC2012_val_00020300.JPEG +ILSVRC2012_val_00012326.JPEG +ILSVRC2012_val_00043435.JPEG +ILSVRC2012_val_00039411.JPEG +ILSVRC2012_val_00035465.JPEG +ILSVRC2012_val_00020874.JPEG +ILSVRC2012_val_00042278.JPEG +ILSVRC2012_val_00012823.JPEG +ILSVRC2012_val_00042898.JPEG +ILSVRC2012_val_00029555.JPEG +ILSVRC2012_val_00034876.JPEG +ILSVRC2012_val_00041201.JPEG +ILSVRC2012_val_00002305.JPEG +ILSVRC2012_val_00025555.JPEG +ILSVRC2012_val_00038959.JPEG +ILSVRC2012_val_00046159.JPEG +ILSVRC2012_val_00016861.JPEG +ILSVRC2012_val_00034587.JPEG +ILSVRC2012_val_00049641.JPEG +ILSVRC2012_val_00043950.JPEG +ILSVRC2012_val_00042015.JPEG +ILSVRC2012_val_00007814.JPEG +ILSVRC2012_val_00011031.JPEG +ILSVRC2012_val_00014612.JPEG +ILSVRC2012_val_00031151.JPEG +ILSVRC2012_val_00038109.JPEG +ILSVRC2012_val_00044088.JPEG +ILSVRC2012_val_00021407.JPEG +ILSVRC2012_val_00016533.JPEG +ILSVRC2012_val_00048332.JPEG +ILSVRC2012_val_00008000.JPEG +ILSVRC2012_val_00022665.JPEG +ILSVRC2012_val_00048307.JPEG +ILSVRC2012_val_00048891.JPEG +ILSVRC2012_val_00019852.JPEG +ILSVRC2012_val_00046392.JPEG +ILSVRC2012_val_00016310.JPEG +ILSVRC2012_val_00017278.JPEG +ILSVRC2012_val_00020295.JPEG +ILSVRC2012_val_00003997.JPEG +ILSVRC2012_val_00026287.JPEG +ILSVRC2012_val_00015198.JPEG +ILSVRC2012_val_00027570.JPEG +ILSVRC2012_val_00002781.JPEG +ILSVRC2012_val_00044608.JPEG +ILSVRC2012_val_00019060.JPEG +ILSVRC2012_val_00038736.JPEG +ILSVRC2012_val_00040963.JPEG +ILSVRC2012_val_00017830.JPEG +ILSVRC2012_val_00024490.JPEG +ILSVRC2012_val_00047997.JPEG +ILSVRC2012_val_00017848.JPEG +ILSVRC2012_val_00016739.JPEG +ILSVRC2012_val_00000007.JPEG +ILSVRC2012_val_00008158.JPEG +ILSVRC2012_val_00024590.JPEG +ILSVRC2012_val_00007949.JPEG +ILSVRC2012_val_00043265.JPEG +ILSVRC2012_val_00025466.JPEG +ILSVRC2012_val_00005010.JPEG +ILSVRC2012_val_00047905.JPEG +ILSVRC2012_val_00043630.JPEG +ILSVRC2012_val_00016562.JPEG +ILSVRC2012_val_00003398.JPEG +ILSVRC2012_val_00015303.JPEG +ILSVRC2012_val_00015506.JPEG +ILSVRC2012_val_00019185.JPEG +ILSVRC2012_val_00028190.JPEG +ILSVRC2012_val_00004174.JPEG +ILSVRC2012_val_00007706.JPEG +ILSVRC2012_val_00021925.JPEG +ILSVRC2012_val_00040168.JPEG +ILSVRC2012_val_00036239.JPEG +ILSVRC2012_val_00026085.JPEG +ILSVRC2012_val_00013524.JPEG +ILSVRC2012_val_00002824.JPEG +ILSVRC2012_val_00009481.JPEG +ILSVRC2012_val_00016620.JPEG +ILSVRC2012_val_00033835.JPEG +ILSVRC2012_val_00032340.JPEG +ILSVRC2012_val_00034041.JPEG +ILSVRC2012_val_00018314.JPEG +ILSVRC2012_val_00037179.JPEG +ILSVRC2012_val_00034887.JPEG +ILSVRC2012_val_00017774.JPEG +ILSVRC2012_val_00009150.JPEG +ILSVRC2012_val_00015379.JPEG +ILSVRC2012_val_00027718.JPEG +ILSVRC2012_val_00004909.JPEG +ILSVRC2012_val_00046667.JPEG +ILSVRC2012_val_00025375.JPEG +ILSVRC2012_val_00049046.JPEG +ILSVRC2012_val_00040377.JPEG +ILSVRC2012_val_00000813.JPEG +ILSVRC2012_val_00043741.JPEG +ILSVRC2012_val_00030649.JPEG +ILSVRC2012_val_00030041.JPEG +ILSVRC2012_val_00029215.JPEG +ILSVRC2012_val_00026627.JPEG +ILSVRC2012_val_00042799.JPEG +ILSVRC2012_val_00010080.JPEG +ILSVRC2012_val_00011126.JPEG +ILSVRC2012_val_00000701.JPEG +ILSVRC2012_val_00048640.JPEG +ILSVRC2012_val_00039790.JPEG +ILSVRC2012_val_00011526.JPEG +ILSVRC2012_val_00040771.JPEG +ILSVRC2012_val_00030741.JPEG +ILSVRC2012_val_00047941.JPEG +ILSVRC2012_val_00026715.JPEG +ILSVRC2012_val_00006480.JPEG +ILSVRC2012_val_00015423.JPEG +ILSVRC2012_val_00048920.JPEG +ILSVRC2012_val_00021962.JPEG +ILSVRC2012_val_00045123.JPEG +ILSVRC2012_val_00034014.JPEG +ILSVRC2012_val_00039732.JPEG +ILSVRC2012_val_00029396.JPEG +ILSVRC2012_val_00049761.JPEG +ILSVRC2012_val_00008554.JPEG +ILSVRC2012_val_00009544.JPEG +ILSVRC2012_val_00022706.JPEG +ILSVRC2012_val_00015067.JPEG +ILSVRC2012_val_00022111.JPEG +ILSVRC2012_val_00013753.JPEG +ILSVRC2012_val_00023437.JPEG +ILSVRC2012_val_00020230.JPEG +ILSVRC2012_val_00022695.JPEG +ILSVRC2012_val_00023556.JPEG +ILSVRC2012_val_00033260.JPEG +ILSVRC2012_val_00034869.JPEG +ILSVRC2012_val_00048210.JPEG +ILSVRC2012_val_00023271.JPEG +ILSVRC2012_val_00044259.JPEG +ILSVRC2012_val_00028763.JPEG +ILSVRC2012_val_00039042.JPEG +ILSVRC2012_val_00019533.JPEG +ILSVRC2012_val_00011604.JPEG +ILSVRC2012_val_00031606.JPEG +ILSVRC2012_val_00010515.JPEG +ILSVRC2012_val_00018280.JPEG +ILSVRC2012_val_00035390.JPEG +ILSVRC2012_val_00009250.JPEG +ILSVRC2012_val_00024357.JPEG +ILSVRC2012_val_00004945.JPEG +ILSVRC2012_val_00013656.JPEG +ILSVRC2012_val_00006426.JPEG +ILSVRC2012_val_00014107.JPEG +ILSVRC2012_val_00017468.JPEG +ILSVRC2012_val_00010105.JPEG +ILSVRC2012_val_00020783.JPEG +ILSVRC2012_val_00036102.JPEG +ILSVRC2012_val_00027944.JPEG +ILSVRC2012_val_00043232.JPEG +ILSVRC2012_val_00030122.JPEG +ILSVRC2012_val_00011667.JPEG +ILSVRC2012_val_00028176.JPEG +ILSVRC2012_val_00010395.JPEG +ILSVRC2012_val_00034171.JPEG +ILSVRC2012_val_00025805.JPEG +ILSVRC2012_val_00026844.JPEG +ILSVRC2012_val_00018613.JPEG +ILSVRC2012_val_00045652.JPEG +ILSVRC2012_val_00046440.JPEG +ILSVRC2012_val_00045073.JPEG +ILSVRC2012_val_00039977.JPEG +ILSVRC2012_val_00006598.JPEG +ILSVRC2012_val_00007708.JPEG +ILSVRC2012_val_00005548.JPEG +ILSVRC2012_val_00042847.JPEG +ILSVRC2012_val_00022633.JPEG +ILSVRC2012_val_00013301.JPEG +ILSVRC2012_val_00019176.JPEG +ILSVRC2012_val_00028555.JPEG +ILSVRC2012_val_00030312.JPEG +ILSVRC2012_val_00030491.JPEG +ILSVRC2012_val_00040131.JPEG +ILSVRC2012_val_00035885.JPEG +ILSVRC2012_val_00003773.JPEG +ILSVRC2012_val_00018493.JPEG +ILSVRC2012_val_00032322.JPEG +ILSVRC2012_val_00008842.JPEG +ILSVRC2012_val_00000151.JPEG +ILSVRC2012_val_00043934.JPEG +ILSVRC2012_val_00037913.JPEG +ILSVRC2012_val_00003989.JPEG +ILSVRC2012_val_00041099.JPEG +ILSVRC2012_val_00025018.JPEG +ILSVRC2012_val_00042740.JPEG +ILSVRC2012_val_00043037.JPEG +ILSVRC2012_val_00013606.JPEG +ILSVRC2012_val_00021696.JPEG +ILSVRC2012_val_00030148.JPEG +ILSVRC2012_val_00007642.JPEG +ILSVRC2012_val_00016800.JPEG +ILSVRC2012_val_00014917.JPEG +ILSVRC2012_val_00026741.JPEG +ILSVRC2012_val_00036556.JPEG +ILSVRC2012_val_00032220.JPEG +ILSVRC2012_val_00042101.JPEG +ILSVRC2012_val_00036835.JPEG +ILSVRC2012_val_00009642.JPEG +ILSVRC2012_val_00015884.JPEG +ILSVRC2012_val_00032685.JPEG +ILSVRC2012_val_00038136.JPEG +ILSVRC2012_val_00017441.JPEG +ILSVRC2012_val_00021790.JPEG +ILSVRC2012_val_00037527.JPEG +ILSVRC2012_val_00003913.JPEG +ILSVRC2012_val_00030847.JPEG +ILSVRC2012_val_00019957.JPEG +ILSVRC2012_val_00035803.JPEG +ILSVRC2012_val_00010848.JPEG +ILSVRC2012_val_00029081.JPEG +ILSVRC2012_val_00037781.JPEG +ILSVRC2012_val_00018150.JPEG +ILSVRC2012_val_00025549.JPEG +ILSVRC2012_val_00039997.JPEG +ILSVRC2012_val_00033091.JPEG +ILSVRC2012_val_00001197.JPEG +ILSVRC2012_val_00019917.JPEG +ILSVRC2012_val_00019656.JPEG +ILSVRC2012_val_00015943.JPEG +ILSVRC2012_val_00018599.JPEG +ILSVRC2012_val_00007115.JPEG +ILSVRC2012_val_00009978.JPEG +ILSVRC2012_val_00031996.JPEG +ILSVRC2012_val_00019861.JPEG +ILSVRC2012_val_00044035.JPEG +ILSVRC2012_val_00025362.JPEG +ILSVRC2012_val_00006173.JPEG +ILSVRC2012_val_00022823.JPEG +ILSVRC2012_val_00011548.JPEG +ILSVRC2012_val_00048957.JPEG +ILSVRC2012_val_00034696.JPEG +ILSVRC2012_val_00003917.JPEG +ILSVRC2012_val_00021278.JPEG +ILSVRC2012_val_00009724.JPEG +ILSVRC2012_val_00021233.JPEG +ILSVRC2012_val_00020508.JPEG +ILSVRC2012_val_00040063.JPEG +ILSVRC2012_val_00039723.JPEG +ILSVRC2012_val_00040822.JPEG +ILSVRC2012_val_00047665.JPEG +ILSVRC2012_val_00045685.JPEG +ILSVRC2012_val_00026496.JPEG +ILSVRC2012_val_00004171.JPEG +ILSVRC2012_val_00037372.JPEG +ILSVRC2012_val_00011123.JPEG +ILSVRC2012_val_00037934.JPEG +ILSVRC2012_val_00044138.JPEG +ILSVRC2012_val_00005467.JPEG +ILSVRC2012_val_00003455.JPEG +ILSVRC2012_val_00031310.JPEG +ILSVRC2012_val_00004360.JPEG +ILSVRC2012_val_00030006.JPEG +ILSVRC2012_val_00024457.JPEG +ILSVRC2012_val_00046698.JPEG +ILSVRC2012_val_00009702.JPEG +ILSVRC2012_val_00026143.JPEG +ILSVRC2012_val_00016738.JPEG +ILSVRC2012_val_00004521.JPEG +ILSVRC2012_val_00030193.JPEG +ILSVRC2012_val_00017935.JPEG +ILSVRC2012_val_00004971.JPEG +ILSVRC2012_val_00037526.JPEG +ILSVRC2012_val_00043813.JPEG +ILSVRC2012_val_00029214.JPEG +ILSVRC2012_val_00039217.JPEG +ILSVRC2012_val_00000768.JPEG +ILSVRC2012_val_00015691.JPEG +ILSVRC2012_val_00026030.JPEG +ILSVRC2012_val_00013971.JPEG +ILSVRC2012_val_00006877.JPEG +ILSVRC2012_val_00005660.JPEG +ILSVRC2012_val_00013123.JPEG +ILSVRC2012_val_00039773.JPEG +ILSVRC2012_val_00028955.JPEG +ILSVRC2012_val_00025920.JPEG +ILSVRC2012_val_00007405.JPEG +ILSVRC2012_val_00033619.JPEG +ILSVRC2012_val_00008855.JPEG +ILSVRC2012_val_00032726.JPEG +ILSVRC2012_val_00044311.JPEG +ILSVRC2012_val_00018504.JPEG +ILSVRC2012_val_00037893.JPEG +ILSVRC2012_val_00037155.JPEG +ILSVRC2012_val_00035389.JPEG +ILSVRC2012_val_00045952.JPEG +ILSVRC2012_val_00020696.JPEG +ILSVRC2012_val_00009075.JPEG +ILSVRC2012_val_00040739.JPEG +ILSVRC2012_val_00030388.JPEG +ILSVRC2012_val_00002860.JPEG +ILSVRC2012_val_00049495.JPEG +ILSVRC2012_val_00036344.JPEG +ILSVRC2012_val_00003256.JPEG +ILSVRC2012_val_00027451.JPEG +ILSVRC2012_val_00046013.JPEG +ILSVRC2012_val_00046965.JPEG +ILSVRC2012_val_00042759.JPEG +ILSVRC2012_val_00034002.JPEG +ILSVRC2012_val_00023770.JPEG +ILSVRC2012_val_00038224.JPEG +ILSVRC2012_val_00039105.JPEG +ILSVRC2012_val_00013660.JPEG +ILSVRC2012_val_00011443.JPEG +ILSVRC2012_val_00041718.JPEG +ILSVRC2012_val_00033603.JPEG +ILSVRC2012_val_00042073.JPEG +ILSVRC2012_val_00008938.JPEG +ILSVRC2012_val_00045441.JPEG +ILSVRC2012_val_00019583.JPEG +ILSVRC2012_val_00036478.JPEG +ILSVRC2012_val_00013031.JPEG +ILSVRC2012_val_00011509.JPEG +ILSVRC2012_val_00006751.JPEG +ILSVRC2012_val_00008393.JPEG +ILSVRC2012_val_00019392.JPEG +ILSVRC2012_val_00017521.JPEG +ILSVRC2012_val_00002956.JPEG +ILSVRC2012_val_00011917.JPEG +ILSVRC2012_val_00047946.JPEG +ILSVRC2012_val_00005777.JPEG +ILSVRC2012_val_00026429.JPEG +ILSVRC2012_val_00008500.JPEG +ILSVRC2012_val_00032415.JPEG +ILSVRC2012_val_00029953.JPEG +ILSVRC2012_val_00031913.JPEG +ILSVRC2012_val_00035699.JPEG +ILSVRC2012_val_00013979.JPEG +ILSVRC2012_val_00030252.JPEG +ILSVRC2012_val_00000002.JPEG +ILSVRC2012_val_00000249.JPEG +ILSVRC2012_val_00024868.JPEG +ILSVRC2012_val_00000746.JPEG +ILSVRC2012_val_00042299.JPEG +ILSVRC2012_val_00025202.JPEG +ILSVRC2012_val_00011821.JPEG +ILSVRC2012_val_00011823.JPEG +ILSVRC2012_val_00039350.JPEG +ILSVRC2012_val_00033621.JPEG +ILSVRC2012_val_00037042.JPEG +ILSVRC2012_val_00001225.JPEG +ILSVRC2012_val_00031402.JPEG +ILSVRC2012_val_00041030.JPEG +ILSVRC2012_val_00022640.JPEG +ILSVRC2012_val_00009604.JPEG +ILSVRC2012_val_00017887.JPEG +ILSVRC2012_val_00046914.JPEG +ILSVRC2012_val_00033330.JPEG +ILSVRC2012_val_00017722.JPEG +ILSVRC2012_val_00046098.JPEG +ILSVRC2012_val_00040989.JPEG +ILSVRC2012_val_00038606.JPEG +ILSVRC2012_val_00049360.JPEG +ILSVRC2012_val_00040100.JPEG +ILSVRC2012_val_00012515.JPEG +ILSVRC2012_val_00031511.JPEG +ILSVRC2012_val_00000063.JPEG +ILSVRC2012_val_00027789.JPEG +ILSVRC2012_val_00003099.JPEG +ILSVRC2012_val_00023599.JPEG +ILSVRC2012_val_00033530.JPEG +ILSVRC2012_val_00039334.JPEG +ILSVRC2012_val_00034502.JPEG +ILSVRC2012_val_00023765.JPEG +ILSVRC2012_val_00004850.JPEG +ILSVRC2012_val_00013734.JPEG +ILSVRC2012_val_00042960.JPEG +ILSVRC2012_val_00033570.JPEG +ILSVRC2012_val_00036463.JPEG +ILSVRC2012_val_00017703.JPEG +ILSVRC2012_val_00034923.JPEG +ILSVRC2012_val_00019086.JPEG +ILSVRC2012_val_00035992.JPEG +ILSVRC2012_val_00026581.JPEG +ILSVRC2012_val_00019749.JPEG +ILSVRC2012_val_00030202.JPEG +ILSVRC2012_val_00020436.JPEG +ILSVRC2012_val_00007309.JPEG +ILSVRC2012_val_00035494.JPEG +ILSVRC2012_val_00026793.JPEG +ILSVRC2012_val_00014550.JPEG +ILSVRC2012_val_00038161.JPEG +ILSVRC2012_val_00041986.JPEG +ILSVRC2012_val_00019191.JPEG +ILSVRC2012_val_00003257.JPEG +ILSVRC2012_val_00042534.JPEG +ILSVRC2012_val_00012204.JPEG +ILSVRC2012_val_00018832.JPEG +ILSVRC2012_val_00012226.JPEG +ILSVRC2012_val_00041502.JPEG +ILSVRC2012_val_00022322.JPEG +ILSVRC2012_val_00017550.JPEG +ILSVRC2012_val_00048829.JPEG +ILSVRC2012_val_00043492.JPEG +ILSVRC2012_val_00023031.JPEG +ILSVRC2012_val_00049887.JPEG +ILSVRC2012_val_00043151.JPEG +ILSVRC2012_val_00018210.JPEG +ILSVRC2012_val_00011060.JPEG +ILSVRC2012_val_00027583.JPEG +ILSVRC2012_val_00014022.JPEG +ILSVRC2012_val_00048060.JPEG +ILSVRC2012_val_00029797.JPEG +ILSVRC2012_val_00001215.JPEG +ILSVRC2012_val_00031263.JPEG +ILSVRC2012_val_00001408.JPEG +ILSVRC2012_val_00006449.JPEG +ILSVRC2012_val_00027335.JPEG +ILSVRC2012_val_00044155.JPEG +ILSVRC2012_val_00009628.JPEG +ILSVRC2012_val_00049251.JPEG +ILSVRC2012_val_00022372.JPEG +ILSVRC2012_val_00022974.JPEG +ILSVRC2012_val_00021480.JPEG +ILSVRC2012_val_00022084.JPEG +ILSVRC2012_val_00011021.JPEG +ILSVRC2012_val_00021862.JPEG +ILSVRC2012_val_00018916.JPEG +ILSVRC2012_val_00030438.JPEG +ILSVRC2012_val_00003538.JPEG +ILSVRC2012_val_00011813.JPEG +ILSVRC2012_val_00041916.JPEG +ILSVRC2012_val_00048309.JPEG +ILSVRC2012_val_00013402.JPEG +ILSVRC2012_val_00049415.JPEG +ILSVRC2012_val_00010180.JPEG +ILSVRC2012_val_00046481.JPEG +ILSVRC2012_val_00046623.JPEG +ILSVRC2012_val_00019188.JPEG +ILSVRC2012_val_00029558.JPEG +ILSVRC2012_val_00015857.JPEG +ILSVRC2012_val_00042830.JPEG +ILSVRC2012_val_00028580.JPEG +ILSVRC2012_val_00010304.JPEG +ILSVRC2012_val_00048142.JPEG +ILSVRC2012_val_00012781.JPEG +ILSVRC2012_val_00014713.JPEG +ILSVRC2012_val_00020200.JPEG +ILSVRC2012_val_00008176.JPEG +ILSVRC2012_val_00026935.JPEG +ILSVRC2012_val_00008485.JPEG +ILSVRC2012_val_00037193.JPEG +ILSVRC2012_val_00041900.JPEG +ILSVRC2012_val_00007301.JPEG +ILSVRC2012_val_00004249.JPEG +ILSVRC2012_val_00010186.JPEG +ILSVRC2012_val_00002263.JPEG +ILSVRC2012_val_00023103.JPEG +ILSVRC2012_val_00018000.JPEG +ILSVRC2012_val_00037533.JPEG +ILSVRC2012_val_00022510.JPEG +ILSVRC2012_val_00002720.JPEG +ILSVRC2012_val_00026682.JPEG +ILSVRC2012_val_00042967.JPEG +ILSVRC2012_val_00025359.JPEG +ILSVRC2012_val_00049733.JPEG +ILSVRC2012_val_00025565.JPEG +ILSVRC2012_val_00017306.JPEG +ILSVRC2012_val_00021377.JPEG +ILSVRC2012_val_00047861.JPEG +ILSVRC2012_val_00031099.JPEG +ILSVRC2012_val_00042069.JPEG +ILSVRC2012_val_00012595.JPEG +ILSVRC2012_val_00039834.JPEG +ILSVRC2012_val_00015377.JPEG +ILSVRC2012_val_00014046.JPEG +ILSVRC2012_val_00034051.JPEG +ILSVRC2012_val_00006873.JPEG +ILSVRC2012_val_00032656.JPEG +ILSVRC2012_val_00028007.JPEG +ILSVRC2012_val_00035359.JPEG +ILSVRC2012_val_00031873.JPEG +ILSVRC2012_val_00022043.JPEG +ILSVRC2012_val_00037948.JPEG +ILSVRC2012_val_00003247.JPEG +ILSVRC2012_val_00047504.JPEG +ILSVRC2012_val_00040452.JPEG +ILSVRC2012_val_00033075.JPEG +ILSVRC2012_val_00026605.JPEG +ILSVRC2012_val_00033053.JPEG +ILSVRC2012_val_00027979.JPEG +ILSVRC2012_val_00021478.JPEG +ILSVRC2012_val_00023061.JPEG +ILSVRC2012_val_00047144.JPEG +ILSVRC2012_val_00021470.JPEG +ILSVRC2012_val_00007220.JPEG +ILSVRC2012_val_00028592.JPEG +ILSVRC2012_val_00027154.JPEG +ILSVRC2012_val_00047516.JPEG +ILSVRC2012_val_00041814.JPEG +ILSVRC2012_val_00033942.JPEG +ILSVRC2012_val_00045097.JPEG +ILSVRC2012_val_00021309.JPEG +ILSVRC2012_val_00031713.JPEG +ILSVRC2012_val_00028385.JPEG +ILSVRC2012_val_00003924.JPEG +ILSVRC2012_val_00026537.JPEG +ILSVRC2012_val_00035575.JPEG +ILSVRC2012_val_00031255.JPEG +ILSVRC2012_val_00007129.JPEG +ILSVRC2012_val_00008454.JPEG +ILSVRC2012_val_00040310.JPEG +ILSVRC2012_val_00003672.JPEG +ILSVRC2012_val_00024218.JPEG +ILSVRC2012_val_00014890.JPEG +ILSVRC2012_val_00001309.JPEG +ILSVRC2012_val_00016827.JPEG +ILSVRC2012_val_00039597.JPEG +ILSVRC2012_val_00039115.JPEG +ILSVRC2012_val_00001111.JPEG +ILSVRC2012_val_00033182.JPEG +ILSVRC2012_val_00011423.JPEG +ILSVRC2012_val_00049656.JPEG +ILSVRC2012_val_00024267.JPEG +ILSVRC2012_val_00039240.JPEG +ILSVRC2012_val_00031881.JPEG +ILSVRC2012_val_00035369.JPEG +ILSVRC2012_val_00015008.JPEG +ILSVRC2012_val_00030774.JPEG +ILSVRC2012_val_00038595.JPEG +ILSVRC2012_val_00021479.JPEG +ILSVRC2012_val_00040700.JPEG +ILSVRC2012_val_00034549.JPEG +ILSVRC2012_val_00034417.JPEG +ILSVRC2012_val_00006986.JPEG +ILSVRC2012_val_00045019.JPEG +ILSVRC2012_val_00045171.JPEG +ILSVRC2012_val_00006626.JPEG +ILSVRC2012_val_00026932.JPEG +ILSVRC2012_val_00004447.JPEG +ILSVRC2012_val_00003591.JPEG +ILSVRC2012_val_00018806.JPEG +ILSVRC2012_val_00009713.JPEG +ILSVRC2012_val_00012685.JPEG +ILSVRC2012_val_00018192.JPEG +ILSVRC2012_val_00022154.JPEG +ILSVRC2012_val_00026218.JPEG +ILSVRC2012_val_00001202.JPEG +ILSVRC2012_val_00028922.JPEG +ILSVRC2012_val_00018299.JPEG +ILSVRC2012_val_00028400.JPEG +ILSVRC2012_val_00022498.JPEG +ILSVRC2012_val_00002800.JPEG +ILSVRC2012_val_00040443.JPEG +ILSVRC2012_val_00006725.JPEG +ILSVRC2012_val_00049358.JPEG +ILSVRC2012_val_00044275.JPEG +ILSVRC2012_val_00027021.JPEG +ILSVRC2012_val_00033014.JPEG +ILSVRC2012_val_00011543.JPEG +ILSVRC2012_val_00009659.JPEG +ILSVRC2012_val_00024484.JPEG +ILSVRC2012_val_00016832.JPEG +ILSVRC2012_val_00047057.JPEG +ILSVRC2012_val_00013913.JPEG +ILSVRC2012_val_00048594.JPEG +ILSVRC2012_val_00034579.JPEG +ILSVRC2012_val_00017197.JPEG +ILSVRC2012_val_00045680.JPEG +ILSVRC2012_val_00016504.JPEG +ILSVRC2012_val_00040253.JPEG +ILSVRC2012_val_00031865.JPEG +ILSVRC2012_val_00039173.JPEG +ILSVRC2012_val_00046283.JPEG +ILSVRC2012_val_00034635.JPEG +ILSVRC2012_val_00041962.JPEG +ILSVRC2012_val_00046309.JPEG +ILSVRC2012_val_00044748.JPEG +ILSVRC2012_val_00033677.JPEG +ILSVRC2012_val_00040090.JPEG +ILSVRC2012_val_00003964.JPEG +ILSVRC2012_val_00033617.JPEG +ILSVRC2012_val_00046636.JPEG +ILSVRC2012_val_00010208.JPEG +ILSVRC2012_val_00020616.JPEG +ILSVRC2012_val_00019755.JPEG +ILSVRC2012_val_00025190.JPEG +ILSVRC2012_val_00011660.JPEG +ILSVRC2012_val_00010980.JPEG +ILSVRC2012_val_00027738.JPEG +ILSVRC2012_val_00007407.JPEG +ILSVRC2012_val_00027767.JPEG +ILSVRC2012_val_00036709.JPEG +ILSVRC2012_val_00017960.JPEG +ILSVRC2012_val_00044210.JPEG +ILSVRC2012_val_00047592.JPEG +ILSVRC2012_val_00013477.JPEG +ILSVRC2012_val_00046335.JPEG +ILSVRC2012_val_00031119.JPEG +ILSVRC2012_val_00014251.JPEG +ILSVRC2012_val_00030507.JPEG +ILSVRC2012_val_00014294.JPEG +ILSVRC2012_val_00002661.JPEG +ILSVRC2012_val_00013706.JPEG +ILSVRC2012_val_00045757.JPEG +ILSVRC2012_val_00044579.JPEG +ILSVRC2012_val_00014794.JPEG +ILSVRC2012_val_00032113.JPEG +ILSVRC2012_val_00020273.JPEG +ILSVRC2012_val_00019194.JPEG +ILSVRC2012_val_00006226.JPEG +ILSVRC2012_val_00023819.JPEG +ILSVRC2012_val_00009454.JPEG +ILSVRC2012_val_00001694.JPEG +ILSVRC2012_val_00049875.JPEG +ILSVRC2012_val_00037422.JPEG +ILSVRC2012_val_00027200.JPEG +ILSVRC2012_val_00016601.JPEG +ILSVRC2012_val_00018466.JPEG +ILSVRC2012_val_00026461.JPEG +ILSVRC2012_val_00034253.JPEG +ILSVRC2012_val_00039198.JPEG +ILSVRC2012_val_00037684.JPEG +ILSVRC2012_val_00029359.JPEG +ILSVRC2012_val_00039126.JPEG +ILSVRC2012_val_00035121.JPEG +ILSVRC2012_val_00015535.JPEG +ILSVRC2012_val_00043716.JPEG +ILSVRC2012_val_00016745.JPEG +ILSVRC2012_val_00024215.JPEG +ILSVRC2012_val_00041629.JPEG +ILSVRC2012_val_00036155.JPEG +ILSVRC2012_val_00029893.JPEG +ILSVRC2012_val_00042097.JPEG +ILSVRC2012_val_00048932.JPEG +ILSVRC2012_val_00001627.JPEG +ILSVRC2012_val_00014189.JPEG +ILSVRC2012_val_00013213.JPEG +ILSVRC2012_val_00006850.JPEG +ILSVRC2012_val_00048203.JPEG +ILSVRC2012_val_00022452.JPEG +ILSVRC2012_val_00003160.JPEG +ILSVRC2012_val_00039910.JPEG +ILSVRC2012_val_00029912.JPEG +ILSVRC2012_val_00038263.JPEG +ILSVRC2012_val_00013167.JPEG +ILSVRC2012_val_00016072.JPEG +ILSVRC2012_val_00040084.JPEG +ILSVRC2012_val_00020101.JPEG +ILSVRC2012_val_00033436.JPEG +ILSVRC2012_val_00017349.JPEG +ILSVRC2012_val_00000127.JPEG +ILSVRC2012_val_00044962.JPEG +ILSVRC2012_val_00029389.JPEG +ILSVRC2012_val_00028303.JPEG +ILSVRC2012_val_00044342.JPEG +ILSVRC2012_val_00010850.JPEG +ILSVRC2012_val_00048519.JPEG +ILSVRC2012_val_00036144.JPEG +ILSVRC2012_val_00002877.JPEG +ILSVRC2012_val_00019010.JPEG +ILSVRC2012_val_00012936.JPEG +ILSVRC2012_val_00034484.JPEG +ILSVRC2012_val_00017857.JPEG +ILSVRC2012_val_00044344.JPEG +ILSVRC2012_val_00047646.JPEG +ILSVRC2012_val_00018187.JPEG +ILSVRC2012_val_00046922.JPEG +ILSVRC2012_val_00036215.JPEG +ILSVRC2012_val_00036531.JPEG +ILSVRC2012_val_00025011.JPEG +ILSVRC2012_val_00008329.JPEG +ILSVRC2012_val_00008603.JPEG +ILSVRC2012_val_00010589.JPEG +ILSVRC2012_val_00024808.JPEG +ILSVRC2012_val_00027010.JPEG +ILSVRC2012_val_00002155.JPEG +ILSVRC2012_val_00007965.JPEG +ILSVRC2012_val_00043215.JPEG +ILSVRC2012_val_00011850.JPEG +ILSVRC2012_val_00045827.JPEG +ILSVRC2012_val_00006399.JPEG +ILSVRC2012_val_00027151.JPEG +ILSVRC2012_val_00036553.JPEG +ILSVRC2012_val_00031971.JPEG +ILSVRC2012_val_00012419.JPEG +ILSVRC2012_val_00023320.JPEG +ILSVRC2012_val_00039879.JPEG +ILSVRC2012_val_00049440.JPEG +ILSVRC2012_val_00002208.JPEG +ILSVRC2012_val_00020194.JPEG +ILSVRC2012_val_00034750.JPEG +ILSVRC2012_val_00013873.JPEG +ILSVRC2012_val_00030380.JPEG +ILSVRC2012_val_00018324.JPEG +ILSVRC2012_val_00001547.JPEG +ILSVRC2012_val_00048402.JPEG +ILSVRC2012_val_00013462.JPEG +ILSVRC2012_val_00014753.JPEG +ILSVRC2012_val_00008464.JPEG +ILSVRC2012_val_00004005.JPEG +ILSVRC2012_val_00004210.JPEG +ILSVRC2012_val_00023149.JPEG +ILSVRC2012_val_00022880.JPEG +ILSVRC2012_val_00020548.JPEG +ILSVRC2012_val_00015765.JPEG +ILSVRC2012_val_00029325.JPEG +ILSVRC2012_val_00025625.JPEG +ILSVRC2012_val_00010698.JPEG +ILSVRC2012_val_00017142.JPEG +ILSVRC2012_val_00006021.JPEG +ILSVRC2012_val_00043655.JPEG +ILSVRC2012_val_00004718.JPEG +ILSVRC2012_val_00026994.JPEG +ILSVRC2012_val_00015108.JPEG +ILSVRC2012_val_00031857.JPEG +ILSVRC2012_val_00005342.JPEG +ILSVRC2012_val_00022196.JPEG +ILSVRC2012_val_00044817.JPEG +ILSVRC2012_val_00049354.JPEG +ILSVRC2012_val_00023664.JPEG +ILSVRC2012_val_00005434.JPEG +ILSVRC2012_val_00025434.JPEG +ILSVRC2012_val_00049450.JPEG +ILSVRC2012_val_00043589.JPEG +ILSVRC2012_val_00016161.JPEG +ILSVRC2012_val_00032953.JPEG +ILSVRC2012_val_00032295.JPEG +ILSVRC2012_val_00035905.JPEG +ILSVRC2012_val_00000432.JPEG +ILSVRC2012_val_00001400.JPEG +ILSVRC2012_val_00008612.JPEG +ILSVRC2012_val_00020404.JPEG +ILSVRC2012_val_00049372.JPEG +ILSVRC2012_val_00046608.JPEG +ILSVRC2012_val_00030469.JPEG +ILSVRC2012_val_00017257.JPEG +ILSVRC2012_val_00036589.JPEG +ILSVRC2012_val_00039988.JPEG +ILSVRC2012_val_00003127.JPEG +ILSVRC2012_val_00022513.JPEG +ILSVRC2012_val_00026121.JPEG +ILSVRC2012_val_00041435.JPEG +ILSVRC2012_val_00016540.JPEG +ILSVRC2012_val_00039264.JPEG +ILSVRC2012_val_00023257.JPEG +ILSVRC2012_val_00003296.JPEG +ILSVRC2012_val_00039274.JPEG +ILSVRC2012_val_00013522.JPEG +ILSVRC2012_val_00000142.JPEG +ILSVRC2012_val_00042563.JPEG +ILSVRC2012_val_00031699.JPEG +ILSVRC2012_val_00024106.JPEG +ILSVRC2012_val_00043605.JPEG +ILSVRC2012_val_00015044.JPEG +ILSVRC2012_val_00035566.JPEG +ILSVRC2012_val_00010551.JPEG +ILSVRC2012_val_00009509.JPEG +ILSVRC2012_val_00044025.JPEG +ILSVRC2012_val_00041457.JPEG +ILSVRC2012_val_00011337.JPEG +ILSVRC2012_val_00018817.JPEG +ILSVRC2012_val_00001514.JPEG +ILSVRC2012_val_00009207.JPEG +ILSVRC2012_val_00034862.JPEG +ILSVRC2012_val_00024506.JPEG +ILSVRC2012_val_00009623.JPEG +ILSVRC2012_val_00042796.JPEG +ILSVRC2012_val_00008336.JPEG +ILSVRC2012_val_00002768.JPEG +ILSVRC2012_val_00006342.JPEG +ILSVRC2012_val_00037332.JPEG +ILSVRC2012_val_00000953.JPEG +ILSVRC2012_val_00010010.JPEG +ILSVRC2012_val_00027278.JPEG +ILSVRC2012_val_00039665.JPEG +ILSVRC2012_val_00016192.JPEG +ILSVRC2012_val_00016357.JPEG +ILSVRC2012_val_00039631.JPEG +ILSVRC2012_val_00016212.JPEG +ILSVRC2012_val_00022097.JPEG +ILSVRC2012_val_00043217.JPEG +ILSVRC2012_val_00017181.JPEG +ILSVRC2012_val_00042165.JPEG +ILSVRC2012_val_00003311.JPEG +ILSVRC2012_val_00024256.JPEG +ILSVRC2012_val_00012751.JPEG +ILSVRC2012_val_00041663.JPEG +ILSVRC2012_val_00025404.JPEG +ILSVRC2012_val_00004150.JPEG +ILSVRC2012_val_00000264.JPEG +ILSVRC2012_val_00039373.JPEG +ILSVRC2012_val_00041121.JPEG +ILSVRC2012_val_00045936.JPEG +ILSVRC2012_val_00027325.JPEG +ILSVRC2012_val_00029260.JPEG +ILSVRC2012_val_00026313.JPEG +ILSVRC2012_val_00012699.JPEG +ILSVRC2012_val_00034694.JPEG +ILSVRC2012_val_00005143.JPEG +ILSVRC2012_val_00006307.JPEG +ILSVRC2012_val_00046872.JPEG +ILSVRC2012_val_00013641.JPEG +ILSVRC2012_val_00032044.JPEG +ILSVRC2012_val_00048346.JPEG +ILSVRC2012_val_00012222.JPEG +ILSVRC2012_val_00018795.JPEG +ILSVRC2012_val_00032371.JPEG +ILSVRC2012_val_00020208.JPEG +ILSVRC2012_val_00044014.JPEG +ILSVRC2012_val_00008482.JPEG +ILSVRC2012_val_00041116.JPEG +ILSVRC2012_val_00016812.JPEG +ILSVRC2012_val_00048799.JPEG +ILSVRC2012_val_00017315.JPEG +ILSVRC2012_val_00030619.JPEG +ILSVRC2012_val_00002801.JPEG +ILSVRC2012_val_00047117.JPEG +ILSVRC2012_val_00009314.JPEG +ILSVRC2012_val_00024301.JPEG +ILSVRC2012_val_00038550.JPEG +ILSVRC2012_val_00019064.JPEG +ILSVRC2012_val_00026907.JPEG +ILSVRC2012_val_00045064.JPEG +ILSVRC2012_val_00009298.JPEG +ILSVRC2012_val_00015649.JPEG +ILSVRC2012_val_00024736.JPEG +ILSVRC2012_val_00003846.JPEG +ILSVRC2012_val_00023875.JPEG +ILSVRC2012_val_00020888.JPEG +ILSVRC2012_val_00043434.JPEG +ILSVRC2012_val_00031492.JPEG +ILSVRC2012_val_00037451.JPEG +ILSVRC2012_val_00040197.JPEG +ILSVRC2012_val_00039620.JPEG +ILSVRC2012_val_00027406.JPEG +ILSVRC2012_val_00007624.JPEG +ILSVRC2012_val_00017394.JPEG +ILSVRC2012_val_00028734.JPEG +ILSVRC2012_val_00022831.JPEG +ILSVRC2012_val_00037785.JPEG +ILSVRC2012_val_00028788.JPEG +ILSVRC2012_val_00020536.JPEG +ILSVRC2012_val_00020850.JPEG +ILSVRC2012_val_00048599.JPEG +ILSVRC2012_val_00045907.JPEG +ILSVRC2012_val_00044768.JPEG +ILSVRC2012_val_00009949.JPEG +ILSVRC2012_val_00008787.JPEG +ILSVRC2012_val_00018258.JPEG +ILSVRC2012_val_00011663.JPEG +ILSVRC2012_val_00019565.JPEG +ILSVRC2012_val_00047562.JPEG +ILSVRC2012_val_00034209.JPEG +ILSVRC2012_val_00027263.JPEG +ILSVRC2012_val_00033845.JPEG +ILSVRC2012_val_00048850.JPEG +ILSVRC2012_val_00042094.JPEG +ILSVRC2012_val_00011818.JPEG +ILSVRC2012_val_00042615.JPEG +ILSVRC2012_val_00014744.JPEG +ILSVRC2012_val_00003113.JPEG +ILSVRC2012_val_00024137.JPEG +ILSVRC2012_val_00029187.JPEG +ILSVRC2012_val_00015432.JPEG +ILSVRC2012_val_00008661.JPEG +ILSVRC2012_val_00047873.JPEG +ILSVRC2012_val_00027153.JPEG +ILSVRC2012_val_00028475.JPEG +ILSVRC2012_val_00016343.JPEG +ILSVRC2012_val_00043242.JPEG +ILSVRC2012_val_00002889.JPEG +ILSVRC2012_val_00007235.JPEG +ILSVRC2012_val_00028480.JPEG +ILSVRC2012_val_00021361.JPEG +ILSVRC2012_val_00010173.JPEG +ILSVRC2012_val_00036213.JPEG +ILSVRC2012_val_00008468.JPEG +ILSVRC2012_val_00001095.JPEG +ILSVRC2012_val_00029547.JPEG +ILSVRC2012_val_00018840.JPEG +ILSVRC2012_val_00044828.JPEG +ILSVRC2012_val_00019206.JPEG +ILSVRC2012_val_00025868.JPEG +ILSVRC2012_val_00006190.JPEG +ILSVRC2012_val_00031178.JPEG +ILSVRC2012_val_00033301.JPEG +ILSVRC2012_val_00037678.JPEG +ILSVRC2012_val_00015072.JPEG +ILSVRC2012_val_00034668.JPEG +ILSVRC2012_val_00036975.JPEG +ILSVRC2012_val_00019641.JPEG +ILSVRC2012_val_00034269.JPEG +ILSVRC2012_val_00015374.JPEG +ILSVRC2012_val_00006112.JPEG +ILSVRC2012_val_00025769.JPEG +ILSVRC2012_val_00017523.JPEG +ILSVRC2012_val_00010676.JPEG +ILSVRC2012_val_00010986.JPEG +ILSVRC2012_val_00029917.JPEG +ILSVRC2012_val_00010552.JPEG +ILSVRC2012_val_00003502.JPEG +ILSVRC2012_val_00001983.JPEG +ILSVRC2012_val_00047022.JPEG +ILSVRC2012_val_00028227.JPEG +ILSVRC2012_val_00031145.JPEG +ILSVRC2012_val_00002522.JPEG +ILSVRC2012_val_00021449.JPEG +ILSVRC2012_val_00009153.JPEG +ILSVRC2012_val_00004889.JPEG +ILSVRC2012_val_00035894.JPEG +ILSVRC2012_val_00014470.JPEG \ No newline at end of file diff --git a/big_vision/evaluators/proj/uvim/coltran_fid_data/reference_file_names.txt b/big_vision/evaluators/proj/uvim/coltran_fid_data/reference_file_names.txt new file mode 100644 index 0000000000000000000000000000000000000000..8c4faa20f48bfb29e6f8c4e4d70823795c846672 --- /dev/null +++ b/big_vision/evaluators/proj/uvim/coltran_fid_data/reference_file_names.txt @@ -0,0 +1,5000 @@ +ILSVRC2012_val_00013328.JPEG +ILSVRC2012_val_00004694.JPEG +ILSVRC2012_val_00003310.JPEG +ILSVRC2012_val_00013349.JPEG +ILSVRC2012_val_00020452.JPEG +ILSVRC2012_val_00015433.JPEG +ILSVRC2012_val_00039333.JPEG +ILSVRC2012_val_00035263.JPEG +ILSVRC2012_val_00040982.JPEG +ILSVRC2012_val_00035719.JPEG +ILSVRC2012_val_00022596.JPEG +ILSVRC2012_val_00033811.JPEG +ILSVRC2012_val_00012865.JPEG +ILSVRC2012_val_00003372.JPEG +ILSVRC2012_val_00015268.JPEG +ILSVRC2012_val_00030296.JPEG +ILSVRC2012_val_00000872.JPEG +ILSVRC2012_val_00005382.JPEG +ILSVRC2012_val_00027656.JPEG +ILSVRC2012_val_00019275.JPEG +ILSVRC2012_val_00007499.JPEG +ILSVRC2012_val_00000185.JPEG +ILSVRC2012_val_00012704.JPEG +ILSVRC2012_val_00016046.JPEG +ILSVRC2012_val_00014027.JPEG +ILSVRC2012_val_00030748.JPEG +ILSVRC2012_val_00001459.JPEG +ILSVRC2012_val_00021147.JPEG +ILSVRC2012_val_00046948.JPEG +ILSVRC2012_val_00028531.JPEG +ILSVRC2012_val_00004539.JPEG +ILSVRC2012_val_00007052.JPEG +ILSVRC2012_val_00031553.JPEG +ILSVRC2012_val_00000052.JPEG +ILSVRC2012_val_00010415.JPEG +ILSVRC2012_val_00012947.JPEG +ILSVRC2012_val_00037959.JPEG +ILSVRC2012_val_00032520.JPEG +ILSVRC2012_val_00019509.JPEG +ILSVRC2012_val_00008496.JPEG +ILSVRC2012_val_00041804.JPEG +ILSVRC2012_val_00007337.JPEG +ILSVRC2012_val_00022134.JPEG +ILSVRC2012_val_00021473.JPEG +ILSVRC2012_val_00042479.JPEG +ILSVRC2012_val_00016086.JPEG +ILSVRC2012_val_00012076.JPEG +ILSVRC2012_val_00043842.JPEG +ILSVRC2012_val_00022098.JPEG +ILSVRC2012_val_00008187.JPEG +ILSVRC2012_val_00001139.JPEG +ILSVRC2012_val_00044871.JPEG +ILSVRC2012_val_00046803.JPEG +ILSVRC2012_val_00033729.JPEG +ILSVRC2012_val_00049602.JPEG +ILSVRC2012_val_00005731.JPEG +ILSVRC2012_val_00010441.JPEG +ILSVRC2012_val_00044442.JPEG +ILSVRC2012_val_00005255.JPEG +ILSVRC2012_val_00047726.JPEG +ILSVRC2012_val_00016222.JPEG +ILSVRC2012_val_00004195.JPEG +ILSVRC2012_val_00013645.JPEG +ILSVRC2012_val_00006255.JPEG +ILSVRC2012_val_00029684.JPEG +ILSVRC2012_val_00031407.JPEG +ILSVRC2012_val_00012361.JPEG +ILSVRC2012_val_00045108.JPEG +ILSVRC2012_val_00049224.JPEG +ILSVRC2012_val_00018743.JPEG +ILSVRC2012_val_00013493.JPEG +ILSVRC2012_val_00036398.JPEG +ILSVRC2012_val_00006869.JPEG +ILSVRC2012_val_00020791.JPEG +ILSVRC2012_val_00031228.JPEG +ILSVRC2012_val_00033066.JPEG +ILSVRC2012_val_00019237.JPEG +ILSVRC2012_val_00028829.JPEG +ILSVRC2012_val_00020190.JPEG +ILSVRC2012_val_00039176.JPEG +ILSVRC2012_val_00029959.JPEG +ILSVRC2012_val_00011778.JPEG +ILSVRC2012_val_00036200.JPEG +ILSVRC2012_val_00016974.JPEG +ILSVRC2012_val_00036309.JPEG +ILSVRC2012_val_00045377.JPEG +ILSVRC2012_val_00032040.JPEG +ILSVRC2012_val_00014636.JPEG +ILSVRC2012_val_00005688.JPEG +ILSVRC2012_val_00043394.JPEG +ILSVRC2012_val_00045810.JPEG +ILSVRC2012_val_00014678.JPEG +ILSVRC2012_val_00031641.JPEG +ILSVRC2012_val_00023449.JPEG +ILSVRC2012_val_00040348.JPEG +ILSVRC2012_val_00040341.JPEG +ILSVRC2012_val_00033165.JPEG +ILSVRC2012_val_00040844.JPEG +ILSVRC2012_val_00020275.JPEG +ILSVRC2012_val_00036696.JPEG +ILSVRC2012_val_00006550.JPEG +ILSVRC2012_val_00028867.JPEG +ILSVRC2012_val_00012770.JPEG +ILSVRC2012_val_00014127.JPEG +ILSVRC2012_val_00016361.JPEG +ILSVRC2012_val_00009377.JPEG +ILSVRC2012_val_00009420.JPEG +ILSVRC2012_val_00002079.JPEG +ILSVRC2012_val_00048545.JPEG +ILSVRC2012_val_00000254.JPEG +ILSVRC2012_val_00038946.JPEG +ILSVRC2012_val_00048195.JPEG +ILSVRC2012_val_00021193.JPEG +ILSVRC2012_val_00019822.JPEG +ILSVRC2012_val_00010096.JPEG +ILSVRC2012_val_00005640.JPEG +ILSVRC2012_val_00001719.JPEG +ILSVRC2012_val_00001133.JPEG +ILSVRC2012_val_00020074.JPEG +ILSVRC2012_val_00001860.JPEG +ILSVRC2012_val_00024352.JPEG +ILSVRC2012_val_00001885.JPEG +ILSVRC2012_val_00039482.JPEG +ILSVRC2012_val_00012018.JPEG +ILSVRC2012_val_00025839.JPEG +ILSVRC2012_val_00031129.JPEG +ILSVRC2012_val_00012574.JPEG +ILSVRC2012_val_00039938.JPEG +ILSVRC2012_val_00045970.JPEG +ILSVRC2012_val_00047004.JPEG +ILSVRC2012_val_00048687.JPEG +ILSVRC2012_val_00014727.JPEG +ILSVRC2012_val_00032578.JPEG +ILSVRC2012_val_00013640.JPEG +ILSVRC2012_val_00042617.JPEG +ILSVRC2012_val_00036829.JPEG +ILSVRC2012_val_00000810.JPEG +ILSVRC2012_val_00002450.JPEG +ILSVRC2012_val_00028800.JPEG +ILSVRC2012_val_00000146.JPEG +ILSVRC2012_val_00043510.JPEG +ILSVRC2012_val_00037849.JPEG +ILSVRC2012_val_00040470.JPEG +ILSVRC2012_val_00013798.JPEG +ILSVRC2012_val_00013390.JPEG +ILSVRC2012_val_00005308.JPEG +ILSVRC2012_val_00013492.JPEG +ILSVRC2012_val_00031463.JPEG +ILSVRC2012_val_00002149.JPEG +ILSVRC2012_val_00002249.JPEG +ILSVRC2012_val_00036568.JPEG +ILSVRC2012_val_00009829.JPEG +ILSVRC2012_val_00008771.JPEG +ILSVRC2012_val_00049780.JPEG +ILSVRC2012_val_00043443.JPEG +ILSVRC2012_val_00048502.JPEG +ILSVRC2012_val_00048709.JPEG +ILSVRC2012_val_00041308.JPEG +ILSVRC2012_val_00020565.JPEG +ILSVRC2012_val_00005961.JPEG +ILSVRC2012_val_00038221.JPEG +ILSVRC2012_val_00036623.JPEG +ILSVRC2012_val_00004032.JPEG +ILSVRC2012_val_00007345.JPEG +ILSVRC2012_val_00045071.JPEG +ILSVRC2012_val_00015176.JPEG +ILSVRC2012_val_00039111.JPEG +ILSVRC2012_val_00044927.JPEG +ILSVRC2012_val_00046333.JPEG +ILSVRC2012_val_00032433.JPEG +ILSVRC2012_val_00001469.JPEG +ILSVRC2012_val_00048878.JPEG +ILSVRC2012_val_00012356.JPEG +ILSVRC2012_val_00016038.JPEG +ILSVRC2012_val_00013199.JPEG +ILSVRC2012_val_00022162.JPEG +ILSVRC2012_val_00011582.JPEG +ILSVRC2012_val_00031488.JPEG +ILSVRC2012_val_00028854.JPEG +ILSVRC2012_val_00016352.JPEG +ILSVRC2012_val_00038173.JPEG +ILSVRC2012_val_00036319.JPEG +ILSVRC2012_val_00037175.JPEG +ILSVRC2012_val_00037432.JPEG +ILSVRC2012_val_00047920.JPEG +ILSVRC2012_val_00025699.JPEG +ILSVRC2012_val_00016401.JPEG +ILSVRC2012_val_00024466.JPEG +ILSVRC2012_val_00018903.JPEG +ILSVRC2012_val_00020774.JPEG +ILSVRC2012_val_00028132.JPEG +ILSVRC2012_val_00023014.JPEG +ILSVRC2012_val_00018632.JPEG +ILSVRC2012_val_00037601.JPEG +ILSVRC2012_val_00007931.JPEG +ILSVRC2012_val_00049718.JPEG +ILSVRC2012_val_00007455.JPEG +ILSVRC2012_val_00040279.JPEG +ILSVRC2012_val_00046160.JPEG +ILSVRC2012_val_00036153.JPEG +ILSVRC2012_val_00015636.JPEG +ILSVRC2012_val_00019977.JPEG +ILSVRC2012_val_00013131.JPEG +ILSVRC2012_val_00008197.JPEG +ILSVRC2012_val_00020310.JPEG +ILSVRC2012_val_00013001.JPEG +ILSVRC2012_val_00030418.JPEG +ILSVRC2012_val_00039147.JPEG +ILSVRC2012_val_00009474.JPEG +ILSVRC2012_val_00047626.JPEG +ILSVRC2012_val_00018852.JPEG +ILSVRC2012_val_00032244.JPEG +ILSVRC2012_val_00026260.JPEG +ILSVRC2012_val_00026168.JPEG +ILSVRC2012_val_00040284.JPEG +ILSVRC2012_val_00020104.JPEG +ILSVRC2012_val_00010432.JPEG +ILSVRC2012_val_00030982.JPEG +ILSVRC2012_val_00047787.JPEG +ILSVRC2012_val_00027703.JPEG +ILSVRC2012_val_00011851.JPEG +ILSVRC2012_val_00047036.JPEG +ILSVRC2012_val_00034914.JPEG +ILSVRC2012_val_00027009.JPEG +ILSVRC2012_val_00046641.JPEG +ILSVRC2012_val_00049817.JPEG +ILSVRC2012_val_00011935.JPEG +ILSVRC2012_val_00028469.JPEG +ILSVRC2012_val_00009408.JPEG +ILSVRC2012_val_00013004.JPEG +ILSVRC2012_val_00024904.JPEG +ILSVRC2012_val_00049922.JPEG +ILSVRC2012_val_00019697.JPEG +ILSVRC2012_val_00035775.JPEG +ILSVRC2012_val_00031556.JPEG +ILSVRC2012_val_00045696.JPEG +ILSVRC2012_val_00045262.JPEG +ILSVRC2012_val_00031628.JPEG +ILSVRC2012_val_00014104.JPEG +ILSVRC2012_val_00015490.JPEG +ILSVRC2012_val_00033609.JPEG +ILSVRC2012_val_00004806.JPEG +ILSVRC2012_val_00023445.JPEG +ILSVRC2012_val_00023326.JPEG +ILSVRC2012_val_00023901.JPEG +ILSVRC2012_val_00000415.JPEG +ILSVRC2012_val_00031661.JPEG +ILSVRC2012_val_00048406.JPEG +ILSVRC2012_val_00001601.JPEG +ILSVRC2012_val_00038759.JPEG +ILSVRC2012_val_00032195.JPEG +ILSVRC2012_val_00026862.JPEG +ILSVRC2012_val_00032633.JPEG +ILSVRC2012_val_00011208.JPEG +ILSVRC2012_val_00034221.JPEG +ILSVRC2012_val_00017102.JPEG +ILSVRC2012_val_00021565.JPEG +ILSVRC2012_val_00003858.JPEG +ILSVRC2012_val_00015391.JPEG +ILSVRC2012_val_00030691.JPEG +ILSVRC2012_val_00024900.JPEG +ILSVRC2012_val_00023382.JPEG +ILSVRC2012_val_00015981.JPEG +ILSVRC2012_val_00011930.JPEG +ILSVRC2012_val_00013544.JPEG +ILSVRC2012_val_00012859.JPEG +ILSVRC2012_val_00030222.JPEG +ILSVRC2012_val_00018005.JPEG +ILSVRC2012_val_00017577.JPEG +ILSVRC2012_val_00017990.JPEG +ILSVRC2012_val_00017411.JPEG +ILSVRC2012_val_00026116.JPEG +ILSVRC2012_val_00004732.JPEG +ILSVRC2012_val_00034439.JPEG +ILSVRC2012_val_00029943.JPEG +ILSVRC2012_val_00021347.JPEG +ILSVRC2012_val_00036789.JPEG +ILSVRC2012_val_00037660.JPEG +ILSVRC2012_val_00003619.JPEG +ILSVRC2012_val_00006007.JPEG +ILSVRC2012_val_00029155.JPEG +ILSVRC2012_val_00018470.JPEG +ILSVRC2012_val_00019468.JPEG +ILSVRC2012_val_00040620.JPEG +ILSVRC2012_val_00037112.JPEG +ILSVRC2012_val_00035282.JPEG +ILSVRC2012_val_00043815.JPEG +ILSVRC2012_val_00049331.JPEG +ILSVRC2012_val_00004630.JPEG +ILSVRC2012_val_00011301.JPEG +ILSVRC2012_val_00005362.JPEG +ILSVRC2012_val_00027871.JPEG +ILSVRC2012_val_00014763.JPEG +ILSVRC2012_val_00020871.JPEG +ILSVRC2012_val_00006257.JPEG +ILSVRC2012_val_00018205.JPEG +ILSVRC2012_val_00049529.JPEG +ILSVRC2012_val_00046735.JPEG +ILSVRC2012_val_00013541.JPEG +ILSVRC2012_val_00030979.JPEG +ILSVRC2012_val_00011006.JPEG +ILSVRC2012_val_00004503.JPEG +ILSVRC2012_val_00003820.JPEG +ILSVRC2012_val_00018799.JPEG +ILSVRC2012_val_00025967.JPEG +ILSVRC2012_val_00021991.JPEG +ILSVRC2012_val_00016479.JPEG +ILSVRC2012_val_00048801.JPEG +ILSVRC2012_val_00026953.JPEG +ILSVRC2012_val_00043834.JPEG +ILSVRC2012_val_00037570.JPEG +ILSVRC2012_val_00006073.JPEG +ILSVRC2012_val_00043273.JPEG +ILSVRC2012_val_00019660.JPEG +ILSVRC2012_val_00009255.JPEG +ILSVRC2012_val_00038850.JPEG +ILSVRC2012_val_00025518.JPEG +ILSVRC2012_val_00000141.JPEG +ILSVRC2012_val_00008068.JPEG +ILSVRC2012_val_00043958.JPEG +ILSVRC2012_val_00008819.JPEG +ILSVRC2012_val_00049792.JPEG +ILSVRC2012_val_00043262.JPEG +ILSVRC2012_val_00002568.JPEG +ILSVRC2012_val_00009693.JPEG +ILSVRC2012_val_00035164.JPEG +ILSVRC2012_val_00018668.JPEG +ILSVRC2012_val_00008893.JPEG +ILSVRC2012_val_00044853.JPEG +ILSVRC2012_val_00017452.JPEG +ILSVRC2012_val_00026717.JPEG +ILSVRC2012_val_00022723.JPEG +ILSVRC2012_val_00037281.JPEG +ILSVRC2012_val_00001781.JPEG +ILSVRC2012_val_00041738.JPEG +ILSVRC2012_val_00031383.JPEG +ILSVRC2012_val_00021551.JPEG +ILSVRC2012_val_00037482.JPEG +ILSVRC2012_val_00013219.JPEG +ILSVRC2012_val_00026667.JPEG +ILSVRC2012_val_00004125.JPEG +ILSVRC2012_val_00013258.JPEG +ILSVRC2012_val_00017875.JPEG +ILSVRC2012_val_00018075.JPEG +ILSVRC2012_val_00039140.JPEG +ILSVRC2012_val_00038464.JPEG +ILSVRC2012_val_00016215.JPEG +ILSVRC2012_val_00009944.JPEG +ILSVRC2012_val_00000652.JPEG +ILSVRC2012_val_00042080.JPEG +ILSVRC2012_val_00028831.JPEG +ILSVRC2012_val_00016598.JPEG +ILSVRC2012_val_00030277.JPEG +ILSVRC2012_val_00013475.JPEG +ILSVRC2012_val_00048101.JPEG +ILSVRC2012_val_00048907.JPEG +ILSVRC2012_val_00023981.JPEG +ILSVRC2012_val_00042433.JPEG +ILSVRC2012_val_00019444.JPEG +ILSVRC2012_val_00027383.JPEG +ILSVRC2012_val_00031473.JPEG +ILSVRC2012_val_00040874.JPEG +ILSVRC2012_val_00026102.JPEG +ILSVRC2012_val_00007559.JPEG +ILSVRC2012_val_00030454.JPEG +ILSVRC2012_val_00005157.JPEG +ILSVRC2012_val_00006143.JPEG +ILSVRC2012_val_00043168.JPEG +ILSVRC2012_val_00039060.JPEG +ILSVRC2012_val_00010301.JPEG +ILSVRC2012_val_00043584.JPEG +ILSVRC2012_val_00011728.JPEG +ILSVRC2012_val_00034195.JPEG +ILSVRC2012_val_00026980.JPEG +ILSVRC2012_val_00021896.JPEG +ILSVRC2012_val_00038169.JPEG +ILSVRC2012_val_00022481.JPEG +ILSVRC2012_val_00046712.JPEG +ILSVRC2012_val_00017338.JPEG +ILSVRC2012_val_00002958.JPEG +ILSVRC2012_val_00005191.JPEG +ILSVRC2012_val_00012353.JPEG +ILSVRC2012_val_00010460.JPEG +ILSVRC2012_val_00010603.JPEG +ILSVRC2012_val_00010746.JPEG +ILSVRC2012_val_00025094.JPEG +ILSVRC2012_val_00036942.JPEG +ILSVRC2012_val_00022762.JPEG +ILSVRC2012_val_00000197.JPEG +ILSVRC2012_val_00043579.JPEG +ILSVRC2012_val_00042051.JPEG +ILSVRC2012_val_00029050.JPEG +ILSVRC2012_val_00047557.JPEG +ILSVRC2012_val_00025645.JPEG +ILSVRC2012_val_00040607.JPEG +ILSVRC2012_val_00046645.JPEG +ILSVRC2012_val_00012109.JPEG +ILSVRC2012_val_00000096.JPEG +ILSVRC2012_val_00049252.JPEG +ILSVRC2012_val_00030869.JPEG +ILSVRC2012_val_00004865.JPEG +ILSVRC2012_val_00031418.JPEG +ILSVRC2012_val_00005550.JPEG +ILSVRC2012_val_00038057.JPEG +ILSVRC2012_val_00029723.JPEG +ILSVRC2012_val_00042153.JPEG +ILSVRC2012_val_00020339.JPEG +ILSVRC2012_val_00024141.JPEG +ILSVRC2012_val_00036085.JPEG +ILSVRC2012_val_00043160.JPEG +ILSVRC2012_val_00012720.JPEG +ILSVRC2012_val_00008933.JPEG +ILSVRC2012_val_00006335.JPEG +ILSVRC2012_val_00021204.JPEG +ILSVRC2012_val_00046353.JPEG +ILSVRC2012_val_00003776.JPEG +ILSVRC2012_val_00014170.JPEG +ILSVRC2012_val_00044746.JPEG +ILSVRC2012_val_00003216.JPEG +ILSVRC2012_val_00049777.JPEG +ILSVRC2012_val_00001540.JPEG +ILSVRC2012_val_00022168.JPEG +ILSVRC2012_val_00048843.JPEG +ILSVRC2012_val_00033131.JPEG +ILSVRC2012_val_00011687.JPEG +ILSVRC2012_val_00019213.JPEG +ILSVRC2012_val_00024975.JPEG +ILSVRC2012_val_00044339.JPEG +ILSVRC2012_val_00005806.JPEG +ILSVRC2012_val_00023026.JPEG +ILSVRC2012_val_00030853.JPEG +ILSVRC2012_val_00023861.JPEG +ILSVRC2012_val_00046455.JPEG +ILSVRC2012_val_00044393.JPEG +ILSVRC2012_val_00017040.JPEG +ILSVRC2012_val_00048292.JPEG +ILSVRC2012_val_00043459.JPEG +ILSVRC2012_val_00026858.JPEG +ILSVRC2012_val_00007285.JPEG +ILSVRC2012_val_00001862.JPEG +ILSVRC2012_val_00042415.JPEG +ILSVRC2012_val_00003452.JPEG +ILSVRC2012_val_00029466.JPEG +ILSVRC2012_val_00003005.JPEG +ILSVRC2012_val_00009494.JPEG +ILSVRC2012_val_00044722.JPEG +ILSVRC2012_val_00042329.JPEG +ILSVRC2012_val_00030807.JPEG +ILSVRC2012_val_00033536.JPEG +ILSVRC2012_val_00044872.JPEG +ILSVRC2012_val_00014964.JPEG +ILSVRC2012_val_00014256.JPEG +ILSVRC2012_val_00004170.JPEG +ILSVRC2012_val_00025334.JPEG +ILSVRC2012_val_00003406.JPEG +ILSVRC2012_val_00034174.JPEG +ILSVRC2012_val_00008848.JPEG +ILSVRC2012_val_00016537.JPEG +ILSVRC2012_val_00000033.JPEG +ILSVRC2012_val_00023443.JPEG +ILSVRC2012_val_00034067.JPEG +ILSVRC2012_val_00037479.JPEG +ILSVRC2012_val_00032622.JPEG +ILSVRC2012_val_00021389.JPEG +ILSVRC2012_val_00015747.JPEG +ILSVRC2012_val_00022116.JPEG +ILSVRC2012_val_00009714.JPEG +ILSVRC2012_val_00032229.JPEG +ILSVRC2012_val_00015642.JPEG +ILSVRC2012_val_00037000.JPEG +ILSVRC2012_val_00007695.JPEG +ILSVRC2012_val_00015392.JPEG +ILSVRC2012_val_00020355.JPEG +ILSVRC2012_val_00027357.JPEG +ILSVRC2012_val_00009705.JPEG +ILSVRC2012_val_00023495.JPEG +ILSVRC2012_val_00033003.JPEG +ILSVRC2012_val_00010103.JPEG +ILSVRC2012_val_00029847.JPEG +ILSVRC2012_val_00037718.JPEG +ILSVRC2012_val_00029644.JPEG +ILSVRC2012_val_00046509.JPEG +ILSVRC2012_val_00017656.JPEG +ILSVRC2012_val_00045212.JPEG +ILSVRC2012_val_00032062.JPEG +ILSVRC2012_val_00010095.JPEG +ILSVRC2012_val_00002946.JPEG +ILSVRC2012_val_00008392.JPEG +ILSVRC2012_val_00035667.JPEG +ILSVRC2012_val_00022626.JPEG +ILSVRC2012_val_00026119.JPEG +ILSVRC2012_val_00027545.JPEG +ILSVRC2012_val_00035934.JPEG +ILSVRC2012_val_00018136.JPEG +ILSVRC2012_val_00042711.JPEG +ILSVRC2012_val_00007536.JPEG +ILSVRC2012_val_00038740.JPEG +ILSVRC2012_val_00031926.JPEG +ILSVRC2012_val_00029345.JPEG +ILSVRC2012_val_00027520.JPEG +ILSVRC2012_val_00040858.JPEG +ILSVRC2012_val_00023646.JPEG +ILSVRC2012_val_00032650.JPEG +ILSVRC2012_val_00009026.JPEG +ILSVRC2012_val_00003345.JPEG +ILSVRC2012_val_00008413.JPEG +ILSVRC2012_val_00024318.JPEG +ILSVRC2012_val_00001210.JPEG +ILSVRC2012_val_00040288.JPEG +ILSVRC2012_val_00014168.JPEG +ILSVRC2012_val_00047428.JPEG +ILSVRC2012_val_00004307.JPEG +ILSVRC2012_val_00046472.JPEG +ILSVRC2012_val_00049379.JPEG +ILSVRC2012_val_00032817.JPEG +ILSVRC2012_val_00012418.JPEG +ILSVRC2012_val_00024498.JPEG +ILSVRC2012_val_00014487.JPEG +ILSVRC2012_val_00043557.JPEG +ILSVRC2012_val_00028422.JPEG +ILSVRC2012_val_00011171.JPEG +ILSVRC2012_val_00031391.JPEG +ILSVRC2012_val_00022619.JPEG +ILSVRC2012_val_00021014.JPEG +ILSVRC2012_val_00037745.JPEG +ILSVRC2012_val_00025754.JPEG +ILSVRC2012_val_00043945.JPEG +ILSVRC2012_val_00031022.JPEG +ILSVRC2012_val_00044597.JPEG +ILSVRC2012_val_00003095.JPEG +ILSVRC2012_val_00003261.JPEG +ILSVRC2012_val_00049784.JPEG +ILSVRC2012_val_00004474.JPEG +ILSVRC2012_val_00042535.JPEG +ILSVRC2012_val_00004488.JPEG +ILSVRC2012_val_00008735.JPEG +ILSVRC2012_val_00048223.JPEG +ILSVRC2012_val_00000061.JPEG +ILSVRC2012_val_00016239.JPEG +ILSVRC2012_val_00012542.JPEG +ILSVRC2012_val_00021794.JPEG +ILSVRC2012_val_00023532.JPEG +ILSVRC2012_val_00028411.JPEG +ILSVRC2012_val_00046427.JPEG +ILSVRC2012_val_00010531.JPEG +ILSVRC2012_val_00029585.JPEG +ILSVRC2012_val_00003187.JPEG +ILSVRC2012_val_00027146.JPEG +ILSVRC2012_val_00043587.JPEG +ILSVRC2012_val_00023413.JPEG +ILSVRC2012_val_00006372.JPEG +ILSVRC2012_val_00028036.JPEG +ILSVRC2012_val_00002455.JPEG +ILSVRC2012_val_00018932.JPEG +ILSVRC2012_val_00015674.JPEG +ILSVRC2012_val_00040049.JPEG +ILSVRC2012_val_00048880.JPEG +ILSVRC2012_val_00026134.JPEG +ILSVRC2012_val_00042736.JPEG +ILSVRC2012_val_00021531.JPEG +ILSVRC2012_val_00038649.JPEG +ILSVRC2012_val_00027518.JPEG +ILSVRC2012_val_00040016.JPEG +ILSVRC2012_val_00000964.JPEG +ILSVRC2012_val_00001245.JPEG +ILSVRC2012_val_00047893.JPEG +ILSVRC2012_val_00008989.JPEG +ILSVRC2012_val_00012004.JPEG +ILSVRC2012_val_00001246.JPEG +ILSVRC2012_val_00034390.JPEG +ILSVRC2012_val_00023032.JPEG +ILSVRC2012_val_00021748.JPEG +ILSVRC2012_val_00004393.JPEG +ILSVRC2012_val_00002282.JPEG +ILSVRC2012_val_00047645.JPEG +ILSVRC2012_val_00037661.JPEG +ILSVRC2012_val_00024855.JPEG +ILSVRC2012_val_00013482.JPEG +ILSVRC2012_val_00010475.JPEG +ILSVRC2012_val_00006027.JPEG +ILSVRC2012_val_00046760.JPEG +ILSVRC2012_val_00013471.JPEG +ILSVRC2012_val_00019314.JPEG +ILSVRC2012_val_00032652.JPEG +ILSVRC2012_val_00003065.JPEG +ILSVRC2012_val_00027455.JPEG +ILSVRC2012_val_00013957.JPEG +ILSVRC2012_val_00041348.JPEG +ILSVRC2012_val_00043270.JPEG +ILSVRC2012_val_00024909.JPEG +ILSVRC2012_val_00011724.JPEG +ILSVRC2012_val_00018445.JPEG +ILSVRC2012_val_00035671.JPEG +ILSVRC2012_val_00020578.JPEG +ILSVRC2012_val_00021418.JPEG +ILSVRC2012_val_00017242.JPEG +ILSVRC2012_val_00039311.JPEG +ILSVRC2012_val_00035391.JPEG +ILSVRC2012_val_00038461.JPEG +ILSVRC2012_val_00021274.JPEG +ILSVRC2012_val_00040670.JPEG +ILSVRC2012_val_00032153.JPEG +ILSVRC2012_val_00007142.JPEG +ILSVRC2012_val_00017765.JPEG +ILSVRC2012_val_00040587.JPEG +ILSVRC2012_val_00025493.JPEG +ILSVRC2012_val_00049322.JPEG +ILSVRC2012_val_00012376.JPEG +ILSVRC2012_val_00008728.JPEG +ILSVRC2012_val_00046573.JPEG +ILSVRC2012_val_00013374.JPEG +ILSVRC2012_val_00036914.JPEG +ILSVRC2012_val_00019963.JPEG +ILSVRC2012_val_00004382.JPEG +ILSVRC2012_val_00046751.JPEG +ILSVRC2012_val_00011422.JPEG +ILSVRC2012_val_00041235.JPEG +ILSVRC2012_val_00034671.JPEG +ILSVRC2012_val_00008471.JPEG +ILSVRC2012_val_00004615.JPEG +ILSVRC2012_val_00043965.JPEG +ILSVRC2012_val_00031171.JPEG +ILSVRC2012_val_00011906.JPEG +ILSVRC2012_val_00003002.JPEG +ILSVRC2012_val_00032735.JPEG +ILSVRC2012_val_00038157.JPEG +ILSVRC2012_val_00035637.JPEG +ILSVRC2012_val_00013439.JPEG +ILSVRC2012_val_00023431.JPEG +ILSVRC2012_val_00013782.JPEG +ILSVRC2012_val_00028347.JPEG +ILSVRC2012_val_00000106.JPEG +ILSVRC2012_val_00012958.JPEG +ILSVRC2012_val_00048730.JPEG +ILSVRC2012_val_00021293.JPEG +ILSVRC2012_val_00048357.JPEG +ILSVRC2012_val_00019361.JPEG +ILSVRC2012_val_00033830.JPEG +ILSVRC2012_val_00021712.JPEG +ILSVRC2012_val_00031799.JPEG +ILSVRC2012_val_00025750.JPEG +ILSVRC2012_val_00011113.JPEG +ILSVRC2012_val_00032075.JPEG +ILSVRC2012_val_00025937.JPEG +ILSVRC2012_val_00028029.JPEG +ILSVRC2012_val_00019636.JPEG +ILSVRC2012_val_00024501.JPEG +ILSVRC2012_val_00048757.JPEG +ILSVRC2012_val_00034592.JPEG +ILSVRC2012_val_00039260.JPEG +ILSVRC2012_val_00007394.JPEG +ILSVRC2012_val_00033093.JPEG +ILSVRC2012_val_00045565.JPEG +ILSVRC2012_val_00030855.JPEG +ILSVRC2012_val_00021626.JPEG +ILSVRC2012_val_00014051.JPEG +ILSVRC2012_val_00009183.JPEG +ILSVRC2012_val_00025021.JPEG +ILSVRC2012_val_00023096.JPEG +ILSVRC2012_val_00017681.JPEG +ILSVRC2012_val_00014345.JPEG +ILSVRC2012_val_00015115.JPEG +ILSVRC2012_val_00008333.JPEG +ILSVRC2012_val_00020503.JPEG +ILSVRC2012_val_00029377.JPEG +ILSVRC2012_val_00007577.JPEG +ILSVRC2012_val_00034982.JPEG +ILSVRC2012_val_00018824.JPEG +ILSVRC2012_val_00019263.JPEG +ILSVRC2012_val_00024669.JPEG +ILSVRC2012_val_00010241.JPEG +ILSVRC2012_val_00022155.JPEG +ILSVRC2012_val_00039279.JPEG +ILSVRC2012_val_00049327.JPEG +ILSVRC2012_val_00017593.JPEG +ILSVRC2012_val_00020975.JPEG +ILSVRC2012_val_00020843.JPEG +ILSVRC2012_val_00049234.JPEG +ILSVRC2012_val_00045672.JPEG +ILSVRC2012_val_00028180.JPEG +ILSVRC2012_val_00014089.JPEG +ILSVRC2012_val_00028845.JPEG +ILSVRC2012_val_00014178.JPEG +ILSVRC2012_val_00004291.JPEG +ILSVRC2012_val_00049144.JPEG +ILSVRC2012_val_00029182.JPEG +ILSVRC2012_val_00005314.JPEG +ILSVRC2012_val_00024361.JPEG +ILSVRC2012_val_00004773.JPEG +ILSVRC2012_val_00022108.JPEG +ILSVRC2012_val_00001718.JPEG +ILSVRC2012_val_00045977.JPEG +ILSVRC2012_val_00024617.JPEG +ILSVRC2012_val_00045051.JPEG +ILSVRC2012_val_00000957.JPEG +ILSVRC2012_val_00035873.JPEG +ILSVRC2012_val_00017502.JPEG +ILSVRC2012_val_00047733.JPEG +ILSVRC2012_val_00045112.JPEG +ILSVRC2012_val_00012019.JPEG +ILSVRC2012_val_00022905.JPEG +ILSVRC2012_val_00035993.JPEG +ILSVRC2012_val_00001808.JPEG +ILSVRC2012_val_00029765.JPEG +ILSVRC2012_val_00034236.JPEG +ILSVRC2012_val_00013272.JPEG +ILSVRC2012_val_00040912.JPEG +ILSVRC2012_val_00016753.JPEG +ILSVRC2012_val_00023155.JPEG +ILSVRC2012_val_00014580.JPEG +ILSVRC2012_val_00019964.JPEG +ILSVRC2012_val_00036636.JPEG +ILSVRC2012_val_00043122.JPEG +ILSVRC2012_val_00048027.JPEG +ILSVRC2012_val_00017355.JPEG +ILSVRC2012_val_00014113.JPEG +ILSVRC2012_val_00017609.JPEG +ILSVRC2012_val_00049040.JPEG +ILSVRC2012_val_00026775.JPEG +ILSVRC2012_val_00043421.JPEG +ILSVRC2012_val_00032876.JPEG +ILSVRC2012_val_00022636.JPEG +ILSVRC2012_val_00020020.JPEG +ILSVRC2012_val_00000335.JPEG +ILSVRC2012_val_00018120.JPEG +ILSVRC2012_val_00046791.JPEG +ILSVRC2012_val_00036034.JPEG +ILSVRC2012_val_00011776.JPEG +ILSVRC2012_val_00014485.JPEG +ILSVRC2012_val_00024108.JPEG +ILSVRC2012_val_00001662.JPEG +ILSVRC2012_val_00035450.JPEG +ILSVRC2012_val_00015730.JPEG +ILSVRC2012_val_00031574.JPEG +ILSVRC2012_val_00033714.JPEG +ILSVRC2012_val_00024711.JPEG +ILSVRC2012_val_00046949.JPEG +ILSVRC2012_val_00034448.JPEG +ILSVRC2012_val_00047984.JPEG +ILSVRC2012_val_00029957.JPEG +ILSVRC2012_val_00039937.JPEG +ILSVRC2012_val_00043380.JPEG +ILSVRC2012_val_00030600.JPEG +ILSVRC2012_val_00004588.JPEG +ILSVRC2012_val_00023248.JPEG +ILSVRC2012_val_00012479.JPEG +ILSVRC2012_val_00049933.JPEG +ILSVRC2012_val_00001443.JPEG +ILSVRC2012_val_00000110.JPEG +ILSVRC2012_val_00049937.JPEG +ILSVRC2012_val_00042268.JPEG +ILSVRC2012_val_00049565.JPEG +ILSVRC2012_val_00042029.JPEG +ILSVRC2012_val_00035629.JPEG +ILSVRC2012_val_00022964.JPEG +ILSVRC2012_val_00012830.JPEG +ILSVRC2012_val_00025724.JPEG +ILSVRC2012_val_00032899.JPEG +ILSVRC2012_val_00010183.JPEG +ILSVRC2012_val_00015458.JPEG +ILSVRC2012_val_00000034.JPEG +ILSVRC2012_val_00018697.JPEG +ILSVRC2012_val_00007565.JPEG +ILSVRC2012_val_00020730.JPEG +ILSVRC2012_val_00016443.JPEG +ILSVRC2012_val_00033391.JPEG +ILSVRC2012_val_00015386.JPEG +ILSVRC2012_val_00029529.JPEG +ILSVRC2012_val_00040022.JPEG +ILSVRC2012_val_00009184.JPEG +ILSVRC2012_val_00019396.JPEG +ILSVRC2012_val_00008580.JPEG +ILSVRC2012_val_00049789.JPEG +ILSVRC2012_val_00049460.JPEG +ILSVRC2012_val_00011552.JPEG +ILSVRC2012_val_00011049.JPEG +ILSVRC2012_val_00037289.JPEG +ILSVRC2012_val_00016035.JPEG +ILSVRC2012_val_00015091.JPEG +ILSVRC2012_val_00049413.JPEG +ILSVRC2012_val_00001110.JPEG +ILSVRC2012_val_00029186.JPEG +ILSVRC2012_val_00035556.JPEG +ILSVRC2012_val_00043502.JPEG +ILSVRC2012_val_00011212.JPEG +ILSVRC2012_val_00048885.JPEG +ILSVRC2012_val_00044047.JPEG +ILSVRC2012_val_00014318.JPEG +ILSVRC2012_val_00014572.JPEG +ILSVRC2012_val_00047917.JPEG +ILSVRC2012_val_00045678.JPEG +ILSVRC2012_val_00043381.JPEG +ILSVRC2012_val_00005031.JPEG +ILSVRC2012_val_00043843.JPEG +ILSVRC2012_val_00047304.JPEG +ILSVRC2012_val_00003823.JPEG +ILSVRC2012_val_00049192.JPEG +ILSVRC2012_val_00035205.JPEG +ILSVRC2012_val_00035857.JPEG +ILSVRC2012_val_00030657.JPEG +ILSVRC2012_val_00048081.JPEG +ILSVRC2012_val_00004305.JPEG +ILSVRC2012_val_00013827.JPEG +ILSVRC2012_val_00024881.JPEG +ILSVRC2012_val_00027233.JPEG +ILSVRC2012_val_00015903.JPEG +ILSVRC2012_val_00033345.JPEG +ILSVRC2012_val_00014879.JPEG +ILSVRC2012_val_00011437.JPEG +ILSVRC2012_val_00007687.JPEG +ILSVRC2012_val_00031954.JPEG +ILSVRC2012_val_00037649.JPEG +ILSVRC2012_val_00014304.JPEG +ILSVRC2012_val_00035790.JPEG +ILSVRC2012_val_00042613.JPEG +ILSVRC2012_val_00047355.JPEG +ILSVRC2012_val_00039347.JPEG +ILSVRC2012_val_00016003.JPEG +ILSVRC2012_val_00039802.JPEG +ILSVRC2012_val_00015499.JPEG +ILSVRC2012_val_00003242.JPEG +ILSVRC2012_val_00029116.JPEG +ILSVRC2012_val_00020677.JPEG +ILSVRC2012_val_00013024.JPEG +ILSVRC2012_val_00039386.JPEG +ILSVRC2012_val_00025253.JPEG +ILSVRC2012_val_00025753.JPEG +ILSVRC2012_val_00010280.JPEG +ILSVRC2012_val_00014221.JPEG +ILSVRC2012_val_00024867.JPEG +ILSVRC2012_val_00009997.JPEG +ILSVRC2012_val_00011203.JPEG +ILSVRC2012_val_00044864.JPEG +ILSVRC2012_val_00020250.JPEG +ILSVRC2012_val_00018772.JPEG +ILSVRC2012_val_00021073.JPEG +ILSVRC2012_val_00047096.JPEG +ILSVRC2012_val_00040422.JPEG +ILSVRC2012_val_00049727.JPEG +ILSVRC2012_val_00037138.JPEG +ILSVRC2012_val_00025918.JPEG +ILSVRC2012_val_00037445.JPEG +ILSVRC2012_val_00041897.JPEG +ILSVRC2012_val_00008712.JPEG +ILSVRC2012_val_00009020.JPEG +ILSVRC2012_val_00012321.JPEG +ILSVRC2012_val_00023863.JPEG +ILSVRC2012_val_00029978.JPEG +ILSVRC2012_val_00042137.JPEG +ILSVRC2012_val_00009681.JPEG +ILSVRC2012_val_00020386.JPEG +ILSVRC2012_val_00000880.JPEG +ILSVRC2012_val_00047324.JPEG +ILSVRC2012_val_00005240.JPEG +ILSVRC2012_val_00039332.JPEG +ILSVRC2012_val_00022237.JPEG +ILSVRC2012_val_00026674.JPEG +ILSVRC2012_val_00039785.JPEG +ILSVRC2012_val_00044521.JPEG +ILSVRC2012_val_00039655.JPEG +ILSVRC2012_val_00014741.JPEG +ILSVRC2012_val_00003441.JPEG +ILSVRC2012_val_00023021.JPEG +ILSVRC2012_val_00032374.JPEG +ILSVRC2012_val_00048606.JPEG +ILSVRC2012_val_00009274.JPEG +ILSVRC2012_val_00030216.JPEG +ILSVRC2012_val_00006135.JPEG +ILSVRC2012_val_00004852.JPEG +ILSVRC2012_val_00024458.JPEG +ILSVRC2012_val_00015780.JPEG +ILSVRC2012_val_00001270.JPEG +ILSVRC2012_val_00028533.JPEG +ILSVRC2012_val_00027821.JPEG +ILSVRC2012_val_00009427.JPEG +ILSVRC2012_val_00049047.JPEG +ILSVRC2012_val_00004800.JPEG +ILSVRC2012_val_00012167.JPEG +ILSVRC2012_val_00030358.JPEG +ILSVRC2012_val_00022473.JPEG +ILSVRC2012_val_00003700.JPEG +ILSVRC2012_val_00005868.JPEG +ILSVRC2012_val_00030033.JPEG +ILSVRC2012_val_00015706.JPEG +ILSVRC2012_val_00024073.JPEG +ILSVRC2012_val_00044884.JPEG +ILSVRC2012_val_00048099.JPEG +ILSVRC2012_val_00026694.JPEG +ILSVRC2012_val_00044648.JPEG +ILSVRC2012_val_00020837.JPEG +ILSVRC2012_val_00013410.JPEG +ILSVRC2012_val_00037172.JPEG +ILSVRC2012_val_00048735.JPEG +ILSVRC2012_val_00037466.JPEG +ILSVRC2012_val_00041907.JPEG +ILSVRC2012_val_00026299.JPEG +ILSVRC2012_val_00029499.JPEG +ILSVRC2012_val_00023702.JPEG +ILSVRC2012_val_00023477.JPEG +ILSVRC2012_val_00014210.JPEG +ILSVRC2012_val_00035416.JPEG +ILSVRC2012_val_00022253.JPEG +ILSVRC2012_val_00009231.JPEG +ILSVRC2012_val_00027673.JPEG +ILSVRC2012_val_00046445.JPEG +ILSVRC2012_val_00000463.JPEG +ILSVRC2012_val_00044907.JPEG +ILSVRC2012_val_00041806.JPEG +ILSVRC2012_val_00035911.JPEG +ILSVRC2012_val_00048053.JPEG +ILSVRC2012_val_00040285.JPEG +ILSVRC2012_val_00021884.JPEG +ILSVRC2012_val_00011632.JPEG +ILSVRC2012_val_00029929.JPEG +ILSVRC2012_val_00003594.JPEG +ILSVRC2012_val_00008442.JPEG +ILSVRC2012_val_00000603.JPEG +ILSVRC2012_val_00038656.JPEG +ILSVRC2012_val_00047360.JPEG +ILSVRC2012_val_00045255.JPEG +ILSVRC2012_val_00014562.JPEG +ILSVRC2012_val_00019235.JPEG +ILSVRC2012_val_00026514.JPEG +ILSVRC2012_val_00006431.JPEG +ILSVRC2012_val_00039560.JPEG +ILSVRC2012_val_00031476.JPEG +ILSVRC2012_val_00014263.JPEG +ILSVRC2012_val_00048386.JPEG +ILSVRC2012_val_00015585.JPEG +ILSVRC2012_val_00049025.JPEG +ILSVRC2012_val_00017243.JPEG +ILSVRC2012_val_00028661.JPEG +ILSVRC2012_val_00041593.JPEG +ILSVRC2012_val_00032537.JPEG +ILSVRC2012_val_00005215.JPEG +ILSVRC2012_val_00046532.JPEG +ILSVRC2012_val_00005108.JPEG +ILSVRC2012_val_00041666.JPEG +ILSVRC2012_val_00043962.JPEG +ILSVRC2012_val_00045564.JPEG +ILSVRC2012_val_00015731.JPEG +ILSVRC2012_val_00004915.JPEG +ILSVRC2012_val_00038977.JPEG +ILSVRC2012_val_00013169.JPEG +ILSVRC2012_val_00049621.JPEG +ILSVRC2012_val_00008696.JPEG +ILSVRC2012_val_00020036.JPEG +ILSVRC2012_val_00043610.JPEG +ILSVRC2012_val_00016032.JPEG +ILSVRC2012_val_00030250.JPEG +ILSVRC2012_val_00005182.JPEG +ILSVRC2012_val_00046048.JPEG +ILSVRC2012_val_00039951.JPEG +ILSVRC2012_val_00029911.JPEG +ILSVRC2012_val_00046045.JPEG +ILSVRC2012_val_00012248.JPEG +ILSVRC2012_val_00000891.JPEG +ILSVRC2012_val_00043299.JPEG +ILSVRC2012_val_00034833.JPEG +ILSVRC2012_val_00025658.JPEG +ILSVRC2012_val_00013799.JPEG +ILSVRC2012_val_00033302.JPEG +ILSVRC2012_val_00023000.JPEG +ILSVRC2012_val_00047792.JPEG +ILSVRC2012_val_00002337.JPEG +ILSVRC2012_val_00031595.JPEG +ILSVRC2012_val_00006951.JPEG +ILSVRC2012_val_00021820.JPEG +ILSVRC2012_val_00028514.JPEG +ILSVRC2012_val_00004194.JPEG +ILSVRC2012_val_00001479.JPEG +ILSVRC2012_val_00024952.JPEG +ILSVRC2012_val_00034246.JPEG +ILSVRC2012_val_00016254.JPEG +ILSVRC2012_val_00024387.JPEG +ILSVRC2012_val_00011783.JPEG +ILSVRC2012_val_00010738.JPEG +ILSVRC2012_val_00023267.JPEG +ILSVRC2012_val_00003158.JPEG +ILSVRC2012_val_00048290.JPEG +ILSVRC2012_val_00016447.JPEG +ILSVRC2012_val_00001045.JPEG +ILSVRC2012_val_00032891.JPEG +ILSVRC2012_val_00003038.JPEG +ILSVRC2012_val_00026778.JPEG +ILSVRC2012_val_00033241.JPEG +ILSVRC2012_val_00000046.JPEG +ILSVRC2012_val_00020219.JPEG +ILSVRC2012_val_00047696.JPEG +ILSVRC2012_val_00001374.JPEG +ILSVRC2012_val_00006124.JPEG +ILSVRC2012_val_00001167.JPEG +ILSVRC2012_val_00002128.JPEG +ILSVRC2012_val_00007132.JPEG +ILSVRC2012_val_00032760.JPEG +ILSVRC2012_val_00019465.JPEG +ILSVRC2012_val_00010205.JPEG +ILSVRC2012_val_00019246.JPEG +ILSVRC2012_val_00018740.JPEG +ILSVRC2012_val_00043027.JPEG +ILSVRC2012_val_00001453.JPEG +ILSVRC2012_val_00009958.JPEG +ILSVRC2012_val_00004473.JPEG +ILSVRC2012_val_00015569.JPEG +ILSVRC2012_val_00031001.JPEG +ILSVRC2012_val_00002122.JPEG +ILSVRC2012_val_00044792.JPEG +ILSVRC2012_val_00028301.JPEG +ILSVRC2012_val_00008301.JPEG +ILSVRC2012_val_00007855.JPEG +ILSVRC2012_val_00004490.JPEG +ILSVRC2012_val_00004411.JPEG +ILSVRC2012_val_00040039.JPEG +ILSVRC2012_val_00049706.JPEG +ILSVRC2012_val_00003533.JPEG +ILSVRC2012_val_00025795.JPEG +ILSVRC2012_val_00031809.JPEG +ILSVRC2012_val_00006164.JPEG +ILSVRC2012_val_00007424.JPEG +ILSVRC2012_val_00013284.JPEG +ILSVRC2012_val_00008497.JPEG +ILSVRC2012_val_00030123.JPEG +ILSVRC2012_val_00019691.JPEG +ILSVRC2012_val_00028116.JPEG +ILSVRC2012_val_00032683.JPEG +ILSVRC2012_val_00004316.JPEG +ILSVRC2012_val_00003574.JPEG +ILSVRC2012_val_00027155.JPEG +ILSVRC2012_val_00043707.JPEG +ILSVRC2012_val_00000068.JPEG +ILSVRC2012_val_00039327.JPEG +ILSVRC2012_val_00011424.JPEG +ILSVRC2012_val_00048057.JPEG +ILSVRC2012_val_00002978.JPEG +ILSVRC2012_val_00001669.JPEG +ILSVRC2012_val_00048109.JPEG +ILSVRC2012_val_00009540.JPEG +ILSVRC2012_val_00048666.JPEG +ILSVRC2012_val_00015456.JPEG +ILSVRC2012_val_00035305.JPEG +ILSVRC2012_val_00011890.JPEG +ILSVRC2012_val_00002748.JPEG +ILSVRC2012_val_00020898.JPEG +ILSVRC2012_val_00020896.JPEG +ILSVRC2012_val_00012327.JPEG +ILSVRC2012_val_00013363.JPEG +ILSVRC2012_val_00019826.JPEG +ILSVRC2012_val_00000831.JPEG +ILSVRC2012_val_00013281.JPEG +ILSVRC2012_val_00003636.JPEG +ILSVRC2012_val_00030405.JPEG +ILSVRC2012_val_00036384.JPEG +ILSVRC2012_val_00002342.JPEG +ILSVRC2012_val_00017539.JPEG +ILSVRC2012_val_00044980.JPEG +ILSVRC2012_val_00024601.JPEG +ILSVRC2012_val_00049249.JPEG +ILSVRC2012_val_00020280.JPEG +ILSVRC2012_val_00019626.JPEG +ILSVRC2012_val_00017597.JPEG +ILSVRC2012_val_00004240.JPEG +ILSVRC2012_val_00019376.JPEG +ILSVRC2012_val_00008694.JPEG +ILSVRC2012_val_00027175.JPEG +ILSVRC2012_val_00048991.JPEG +ILSVRC2012_val_00012558.JPEG +ILSVRC2012_val_00027245.JPEG +ILSVRC2012_val_00021184.JPEG +ILSVRC2012_val_00016824.JPEG +ILSVRC2012_val_00046863.JPEG +ILSVRC2012_val_00020920.JPEG +ILSVRC2012_val_00020880.JPEG +ILSVRC2012_val_00015476.JPEG +ILSVRC2012_val_00021953.JPEG +ILSVRC2012_val_00003480.JPEG +ILSVRC2012_val_00037790.JPEG +ILSVRC2012_val_00027480.JPEG +ILSVRC2012_val_00027368.JPEG +ILSVRC2012_val_00049195.JPEG +ILSVRC2012_val_00025414.JPEG +ILSVRC2012_val_00022392.JPEG +ILSVRC2012_val_00040525.JPEG +ILSVRC2012_val_00041917.JPEG +ILSVRC2012_val_00041134.JPEG +ILSVRC2012_val_00046297.JPEG +ILSVRC2012_val_00028008.JPEG +ILSVRC2012_val_00031084.JPEG +ILSVRC2012_val_00018097.JPEG +ILSVRC2012_val_00021356.JPEG +ILSVRC2012_val_00049174.JPEG +ILSVRC2012_val_00022656.JPEG +ILSVRC2012_val_00022742.JPEG +ILSVRC2012_val_00013947.JPEG +ILSVRC2012_val_00043190.JPEG +ILSVRC2012_val_00000476.JPEG +ILSVRC2012_val_00019329.JPEG +ILSVRC2012_val_00016924.JPEG +ILSVRC2012_val_00033474.JPEG +ILSVRC2012_val_00037051.JPEG +ILSVRC2012_val_00049000.JPEG +ILSVRC2012_val_00000858.JPEG +ILSVRC2012_val_00030824.JPEG +ILSVRC2012_val_00049422.JPEG +ILSVRC2012_val_00026945.JPEG +ILSVRC2012_val_00031627.JPEG +ILSVRC2012_val_00040169.JPEG +ILSVRC2012_val_00039567.JPEG +ILSVRC2012_val_00013496.JPEG +ILSVRC2012_val_00027670.JPEG +ILSVRC2012_val_00033211.JPEG +ILSVRC2012_val_00019725.JPEG +ILSVRC2012_val_00005459.JPEG +ILSVRC2012_val_00026872.JPEG +ILSVRC2012_val_00006385.JPEG +ILSVRC2012_val_00030199.JPEG +ILSVRC2012_val_00019031.JPEG +ILSVRC2012_val_00043078.JPEG +ILSVRC2012_val_00037941.JPEG +ILSVRC2012_val_00027229.JPEG +ILSVRC2012_val_00001372.JPEG +ILSVRC2012_val_00013758.JPEG +ILSVRC2012_val_00020873.JPEG +ILSVRC2012_val_00048847.JPEG +ILSVRC2012_val_00037750.JPEG +ILSVRC2012_val_00023629.JPEG +ILSVRC2012_val_00031537.JPEG +ILSVRC2012_val_00037485.JPEG +ILSVRC2012_val_00027601.JPEG +ILSVRC2012_val_00044159.JPEG +ILSVRC2012_val_00029496.JPEG +ILSVRC2012_val_00038135.JPEG +ILSVRC2012_val_00014488.JPEG +ILSVRC2012_val_00023364.JPEG +ILSVRC2012_val_00008582.JPEG +ILSVRC2012_val_00031338.JPEG +ILSVRC2012_val_00024431.JPEG +ILSVRC2012_val_00020827.JPEG +ILSVRC2012_val_00037575.JPEG +ILSVRC2012_val_00034848.JPEG +ILSVRC2012_val_00022847.JPEG +ILSVRC2012_val_00048288.JPEG +ILSVRC2012_val_00019199.JPEG +ILSVRC2012_val_00009918.JPEG +ILSVRC2012_val_00015293.JPEG +ILSVRC2012_val_00021764.JPEG +ILSVRC2012_val_00001932.JPEG +ILSVRC2012_val_00042085.JPEG +ILSVRC2012_val_00005144.JPEG +ILSVRC2012_val_00007186.JPEG +ILSVRC2012_val_00025462.JPEG +ILSVRC2012_val_00049597.JPEG +ILSVRC2012_val_00020803.JPEG +ILSVRC2012_val_00003540.JPEG +ILSVRC2012_val_00029109.JPEG +ILSVRC2012_val_00022049.JPEG +ILSVRC2012_val_00038406.JPEG +ILSVRC2012_val_00029105.JPEG +ILSVRC2012_val_00027294.JPEG +ILSVRC2012_val_00004508.JPEG +ILSVRC2012_val_00015262.JPEG +ILSVRC2012_val_00024010.JPEG +ILSVRC2012_val_00015860.JPEG +ILSVRC2012_val_00023410.JPEG +ILSVRC2012_val_00035749.JPEG +ILSVRC2012_val_00006547.JPEG +ILSVRC2012_val_00027555.JPEG +ILSVRC2012_val_00023558.JPEG +ILSVRC2012_val_00014261.JPEG +ILSVRC2012_val_00007033.JPEG +ILSVRC2012_val_00037196.JPEG +ILSVRC2012_val_00007358.JPEG +ILSVRC2012_val_00004898.JPEG +ILSVRC2012_val_00022131.JPEG +ILSVRC2012_val_00017461.JPEG +ILSVRC2012_val_00027207.JPEG +ILSVRC2012_val_00012592.JPEG +ILSVRC2012_val_00027412.JPEG +ILSVRC2012_val_00047350.JPEG +ILSVRC2012_val_00011540.JPEG +ILSVRC2012_val_00044294.JPEG +ILSVRC2012_val_00038681.JPEG +ILSVRC2012_val_00020984.JPEG +ILSVRC2012_val_00011143.JPEG +ILSVRC2012_val_00037983.JPEG +ILSVRC2012_val_00027672.JPEG +ILSVRC2012_val_00010295.JPEG +ILSVRC2012_val_00019585.JPEG +ILSVRC2012_val_00030201.JPEG +ILSVRC2012_val_00032166.JPEG +ILSVRC2012_val_00037838.JPEG +ILSVRC2012_val_00033023.JPEG +ILSVRC2012_val_00023609.JPEG +ILSVRC2012_val_00041888.JPEG +ILSVRC2012_val_00024407.JPEG +ILSVRC2012_val_00033868.JPEG +ILSVRC2012_val_00008077.JPEG +ILSVRC2012_val_00006123.JPEG +ILSVRC2012_val_00021422.JPEG +ILSVRC2012_val_00009333.JPEG +ILSVRC2012_val_00006441.JPEG +ILSVRC2012_val_00006915.JPEG +ILSVRC2012_val_00036236.JPEG +ILSVRC2012_val_00010766.JPEG +ILSVRC2012_val_00009605.JPEG +ILSVRC2012_val_00025017.JPEG +ILSVRC2012_val_00039541.JPEG +ILSVRC2012_val_00016971.JPEG +ILSVRC2012_val_00011704.JPEG +ILSVRC2012_val_00004905.JPEG +ILSVRC2012_val_00009423.JPEG +ILSVRC2012_val_00002514.JPEG +ILSVRC2012_val_00016353.JPEG +ILSVRC2012_val_00049638.JPEG +ILSVRC2012_val_00031006.JPEG +ILSVRC2012_val_00032495.JPEG +ILSVRC2012_val_00044850.JPEG +ILSVRC2012_val_00024969.JPEG +ILSVRC2012_val_00022435.JPEG +ILSVRC2012_val_00012531.JPEG +ILSVRC2012_val_00030042.JPEG +ILSVRC2012_val_00004051.JPEG +ILSVRC2012_val_00009862.JPEG +ILSVRC2012_val_00019659.JPEG +ILSVRC2012_val_00039381.JPEG +ILSVRC2012_val_00019863.JPEG +ILSVRC2012_val_00040835.JPEG +ILSVRC2012_val_00014557.JPEG +ILSVRC2012_val_00015502.JPEG +ILSVRC2012_val_00005669.JPEG +ILSVRC2012_val_00030985.JPEG +ILSVRC2012_val_00032979.JPEG +ILSVRC2012_val_00027059.JPEG +ILSVRC2012_val_00025393.JPEG +ILSVRC2012_val_00033253.JPEG +ILSVRC2012_val_00032781.JPEG +ILSVRC2012_val_00021311.JPEG +ILSVRC2012_val_00027044.JPEG +ILSVRC2012_val_00011606.JPEG +ILSVRC2012_val_00037412.JPEG +ILSVRC2012_val_00033129.JPEG +ILSVRC2012_val_00007665.JPEG +ILSVRC2012_val_00021959.JPEG +ILSVRC2012_val_00046146.JPEG +ILSVRC2012_val_00000544.JPEG +ILSVRC2012_val_00004846.JPEG +ILSVRC2012_val_00040116.JPEG +ILSVRC2012_val_00025351.JPEG +ILSVRC2012_val_00006217.JPEG +ILSVRC2012_val_00021113.JPEG +ILSVRC2012_val_00017837.JPEG +ILSVRC2012_val_00018889.JPEG +ILSVRC2012_val_00041968.JPEG +ILSVRC2012_val_00027792.JPEG +ILSVRC2012_val_00016871.JPEG +ILSVRC2012_val_00009309.JPEG +ILSVRC2012_val_00000760.JPEG +ILSVRC2012_val_00024761.JPEG +ILSVRC2012_val_00001825.JPEG +ILSVRC2012_val_00031410.JPEG +ILSVRC2012_val_00017157.JPEG +ILSVRC2012_val_00022739.JPEG +ILSVRC2012_val_00035986.JPEG +ILSVRC2012_val_00033572.JPEG +ILSVRC2012_val_00035129.JPEG +ILSVRC2012_val_00035392.JPEG +ILSVRC2012_val_00033647.JPEG +ILSVRC2012_val_00007375.JPEG +ILSVRC2012_val_00010651.JPEG +ILSVRC2012_val_00023065.JPEG +ILSVRC2012_val_00041864.JPEG +ILSVRC2012_val_00026613.JPEG +ILSVRC2012_val_00025104.JPEG +ILSVRC2012_val_00001190.JPEG +ILSVRC2012_val_00019568.JPEG +ILSVRC2012_val_00023751.JPEG +ILSVRC2012_val_00012502.JPEG +ILSVRC2012_val_00010917.JPEG +ILSVRC2012_val_00016395.JPEG +ILSVRC2012_val_00040208.JPEG +ILSVRC2012_val_00008481.JPEG +ILSVRC2012_val_00033625.JPEG +ILSVRC2012_val_00014993.JPEG +ILSVRC2012_val_00023145.JPEG +ILSVRC2012_val_00010136.JPEG +ILSVRC2012_val_00048387.JPEG +ILSVRC2012_val_00023683.JPEG +ILSVRC2012_val_00013677.JPEG +ILSVRC2012_val_00008788.JPEG +ILSVRC2012_val_00013581.JPEG +ILSVRC2012_val_00004118.JPEG +ILSVRC2012_val_00048236.JPEG +ILSVRC2012_val_00016914.JPEG +ILSVRC2012_val_00035300.JPEG +ILSVRC2012_val_00041040.JPEG +ILSVRC2012_val_00006987.JPEG +ILSVRC2012_val_00047603.JPEG +ILSVRC2012_val_00017860.JPEG +ILSVRC2012_val_00033990.JPEG +ILSVRC2012_val_00044708.JPEG +ILSVRC2012_val_00024770.JPEG +ILSVRC2012_val_00001391.JPEG +ILSVRC2012_val_00011119.JPEG +ILSVRC2012_val_00000657.JPEG +ILSVRC2012_val_00003660.JPEG +ILSVRC2012_val_00036353.JPEG +ILSVRC2012_val_00019544.JPEG +ILSVRC2012_val_00049562.JPEG +ILSVRC2012_val_00022560.JPEG +ILSVRC2012_val_00025905.JPEG +ILSVRC2012_val_00009180.JPEG +ILSVRC2012_val_00044414.JPEG +ILSVRC2012_val_00049084.JPEG +ILSVRC2012_val_00027078.JPEG +ILSVRC2012_val_00049375.JPEG +ILSVRC2012_val_00019184.JPEG +ILSVRC2012_val_00028972.JPEG +ILSVRC2012_val_00008515.JPEG +ILSVRC2012_val_00016329.JPEG +ILSVRC2012_val_00005092.JPEG +ILSVRC2012_val_00043021.JPEG +ILSVRC2012_val_00039953.JPEG +ILSVRC2012_val_00038742.JPEG +ILSVRC2012_val_00038640.JPEG +ILSVRC2012_val_00038196.JPEG +ILSVRC2012_val_00047247.JPEG +ILSVRC2012_val_00010830.JPEG +ILSVRC2012_val_00046855.JPEG +ILSVRC2012_val_00028233.JPEG +ILSVRC2012_val_00001358.JPEG +ILSVRC2012_val_00012754.JPEG +ILSVRC2012_val_00005453.JPEG +ILSVRC2012_val_00002951.JPEG +ILSVRC2012_val_00008466.JPEG +ILSVRC2012_val_00012437.JPEG +ILSVRC2012_val_00037242.JPEG +ILSVRC2012_val_00035684.JPEG +ILSVRC2012_val_00049739.JPEG +ILSVRC2012_val_00013811.JPEG +ILSVRC2012_val_00015414.JPEG +ILSVRC2012_val_00019614.JPEG +ILSVRC2012_val_00044192.JPEG +ILSVRC2012_val_00006600.JPEG +ILSVRC2012_val_00007809.JPEG +ILSVRC2012_val_00006923.JPEG +ILSVRC2012_val_00021265.JPEG +ILSVRC2012_val_00020835.JPEG +ILSVRC2012_val_00008453.JPEG +ILSVRC2012_val_00004341.JPEG +ILSVRC2012_val_00003252.JPEG +ILSVRC2012_val_00029289.JPEG +ILSVRC2012_val_00038792.JPEG +ILSVRC2012_val_00009847.JPEG +ILSVRC2012_val_00042081.JPEG +ILSVRC2012_val_00048779.JPEG +ILSVRC2012_val_00030397.JPEG +ILSVRC2012_val_00044164.JPEG +ILSVRC2012_val_00026657.JPEG +ILSVRC2012_val_00024522.JPEG +ILSVRC2012_val_00007374.JPEG +ILSVRC2012_val_00012784.JPEG +ILSVRC2012_val_00004007.JPEG +ILSVRC2012_val_00025950.JPEG +ILSVRC2012_val_00038231.JPEG +ILSVRC2012_val_00033997.JPEG +ILSVRC2012_val_00004693.JPEG +ILSVRC2012_val_00040140.JPEG +ILSVRC2012_val_00012355.JPEG +ILSVRC2012_val_00039524.JPEG +ILSVRC2012_val_00009627.JPEG +ILSVRC2012_val_00049546.JPEG +ILSVRC2012_val_00011791.JPEG +ILSVRC2012_val_00032643.JPEG +ILSVRC2012_val_00033111.JPEG +ILSVRC2012_val_00035975.JPEG +ILSVRC2012_val_00040178.JPEG +ILSVRC2012_val_00025820.JPEG +ILSVRC2012_val_00028959.JPEG +ILSVRC2012_val_00008470.JPEG +ILSVRC2012_val_00012351.JPEG +ILSVRC2012_val_00018332.JPEG +ILSVRC2012_val_00003639.JPEG +ILSVRC2012_val_00047310.JPEG +ILSVRC2012_val_00046409.JPEG +ILSVRC2012_val_00003090.JPEG +ILSVRC2012_val_00006742.JPEG +ILSVRC2012_val_00000379.JPEG +ILSVRC2012_val_00041333.JPEG +ILSVRC2012_val_00042387.JPEG +ILSVRC2012_val_00011042.JPEG +ILSVRC2012_val_00020778.JPEG +ILSVRC2012_val_00043248.JPEG +ILSVRC2012_val_00027585.JPEG +ILSVRC2012_val_00030925.JPEG +ILSVRC2012_val_00024703.JPEG +ILSVRC2012_val_00027295.JPEG +ILSVRC2012_val_00035019.JPEG +ILSVRC2012_val_00014590.JPEG +ILSVRC2012_val_00040475.JPEG +ILSVRC2012_val_00029417.JPEG +ILSVRC2012_val_00011610.JPEG +ILSVRC2012_val_00020955.JPEG +ILSVRC2012_val_00003567.JPEG +ILSVRC2012_val_00041793.JPEG +ILSVRC2012_val_00016089.JPEG +ILSVRC2012_val_00044772.JPEG +ILSVRC2012_val_00041473.JPEG +ILSVRC2012_val_00005309.JPEG +ILSVRC2012_val_00040785.JPEG +ILSVRC2012_val_00024764.JPEG +ILSVRC2012_val_00023879.JPEG +ILSVRC2012_val_00038680.JPEG +ILSVRC2012_val_00037820.JPEG +ILSVRC2012_val_00007513.JPEG +ILSVRC2012_val_00015525.JPEG +ILSVRC2012_val_00011181.JPEG +ILSVRC2012_val_00017180.JPEG +ILSVRC2012_val_00030045.JPEG +ILSVRC2012_val_00028672.JPEG +ILSVRC2012_val_00022604.JPEG +ILSVRC2012_val_00030840.JPEG +ILSVRC2012_val_00025700.JPEG +ILSVRC2012_val_00009433.JPEG +ILSVRC2012_val_00042953.JPEG +ILSVRC2012_val_00012491.JPEG +ILSVRC2012_val_00039638.JPEG +ILSVRC2012_val_00039893.JPEG +ILSVRC2012_val_00017903.JPEG +ILSVRC2012_val_00018539.JPEG +ILSVRC2012_val_00026638.JPEG +ILSVRC2012_val_00046456.JPEG +ILSVRC2012_val_00001033.JPEG +ILSVRC2012_val_00035545.JPEG +ILSVRC2012_val_00030731.JPEG +ILSVRC2012_val_00022647.JPEG +ILSVRC2012_val_00035785.JPEG +ILSVRC2012_val_00004794.JPEG +ILSVRC2012_val_00013608.JPEG +ILSVRC2012_val_00049724.JPEG +ILSVRC2012_val_00031262.JPEG +ILSVRC2012_val_00012488.JPEG +ILSVRC2012_val_00023969.JPEG +ILSVRC2012_val_00021538.JPEG +ILSVRC2012_val_00034563.JPEG +ILSVRC2012_val_00045101.JPEG +ILSVRC2012_val_00032731.JPEG +ILSVRC2012_val_00012648.JPEG +ILSVRC2012_val_00034120.JPEG +ILSVRC2012_val_00044447.JPEG +ILSVRC2012_val_00025913.JPEG +ILSVRC2012_val_00018927.JPEG +ILSVRC2012_val_00011128.JPEG +ILSVRC2012_val_00007963.JPEG +ILSVRC2012_val_00026408.JPEG +ILSVRC2012_val_00040742.JPEG +ILSVRC2012_val_00049705.JPEG +ILSVRC2012_val_00047396.JPEG +ILSVRC2012_val_00024005.JPEG +ILSVRC2012_val_00044795.JPEG +ILSVRC2012_val_00029603.JPEG +ILSVRC2012_val_00034734.JPEG +ILSVRC2012_val_00043458.JPEG +ILSVRC2012_val_00045295.JPEG +ILSVRC2012_val_00016834.JPEG +ILSVRC2012_val_00048866.JPEG +ILSVRC2012_val_00041484.JPEG +ILSVRC2012_val_00014190.JPEG +ILSVRC2012_val_00006984.JPEG +ILSVRC2012_val_00049770.JPEG +ILSVRC2012_val_00008920.JPEG +ILSVRC2012_val_00025115.JPEG +ILSVRC2012_val_00046169.JPEG +ILSVRC2012_val_00034856.JPEG +ILSVRC2012_val_00014984.JPEG +ILSVRC2012_val_00000160.JPEG +ILSVRC2012_val_00007010.JPEG +ILSVRC2012_val_00048995.JPEG +ILSVRC2012_val_00047020.JPEG +ILSVRC2012_val_00023124.JPEG +ILSVRC2012_val_00012600.JPEG +ILSVRC2012_val_00048526.JPEG +ILSVRC2012_val_00019264.JPEG +ILSVRC2012_val_00042419.JPEG +ILSVRC2012_val_00038849.JPEG +ILSVRC2012_val_00000912.JPEG +ILSVRC2012_val_00016837.JPEG +ILSVRC2012_val_00021367.JPEG +ILSVRC2012_val_00015327.JPEG +ILSVRC2012_val_00042886.JPEG +ILSVRC2012_val_00028530.JPEG +ILSVRC2012_val_00007758.JPEG +ILSVRC2012_val_00020557.JPEG +ILSVRC2012_val_00024393.JPEG +ILSVRC2012_val_00017058.JPEG +ILSVRC2012_val_00041259.JPEG +ILSVRC2012_val_00040007.JPEG +ILSVRC2012_val_00002236.JPEG +ILSVRC2012_val_00023033.JPEG +ILSVRC2012_val_00005068.JPEG +ILSVRC2012_val_00047074.JPEG +ILSVRC2012_val_00036330.JPEG +ILSVRC2012_val_00016801.JPEG +ILSVRC2012_val_00014770.JPEG +ILSVRC2012_val_00039189.JPEG +ILSVRC2012_val_00033788.JPEG +ILSVRC2012_val_00011621.JPEG +ILSVRC2012_val_00043847.JPEG +ILSVRC2012_val_00030609.JPEG +ILSVRC2012_val_00037916.JPEG +ILSVRC2012_val_00000200.JPEG +ILSVRC2012_val_00027119.JPEG +ILSVRC2012_val_00017924.JPEG +ILSVRC2012_val_00026920.JPEG +ILSVRC2012_val_00006128.JPEG +ILSVRC2012_val_00018993.JPEG +ILSVRC2012_val_00027998.JPEG +ILSVRC2012_val_00009235.JPEG +ILSVRC2012_val_00022470.JPEG +ILSVRC2012_val_00031456.JPEG +ILSVRC2012_val_00007651.JPEG +ILSVRC2012_val_00049215.JPEG +ILSVRC2012_val_00024268.JPEG +ILSVRC2012_val_00020388.JPEG +ILSVRC2012_val_00027322.JPEG +ILSVRC2012_val_00018653.JPEG +ILSVRC2012_val_00017224.JPEG +ILSVRC2012_val_00031812.JPEG +ILSVRC2012_val_00009253.JPEG +ILSVRC2012_val_00038614.JPEG +ILSVRC2012_val_00025177.JPEG +ILSVRC2012_val_00013805.JPEG +ILSVRC2012_val_00019282.JPEG +ILSVRC2012_val_00029954.JPEG +ILSVRC2012_val_00003651.JPEG +ILSVRC2012_val_00039336.JPEG +ILSVRC2012_val_00045992.JPEG +ILSVRC2012_val_00043590.JPEG +ILSVRC2012_val_00042129.JPEG +ILSVRC2012_val_00007510.JPEG +ILSVRC2012_val_00030648.JPEG +ILSVRC2012_val_00020456.JPEG +ILSVRC2012_val_00045164.JPEG +ILSVRC2012_val_00010959.JPEG +ILSVRC2012_val_00021593.JPEG +ILSVRC2012_val_00015892.JPEG +ILSVRC2012_val_00047703.JPEG +ILSVRC2012_val_00033439.JPEG +ILSVRC2012_val_00036671.JPEG +ILSVRC2012_val_00035619.JPEG +ILSVRC2012_val_00020626.JPEG +ILSVRC2012_val_00023871.JPEG +ILSVRC2012_val_00017213.JPEG +ILSVRC2012_val_00041516.JPEG +ILSVRC2012_val_00018612.JPEG +ILSVRC2012_val_00024587.JPEG +ILSVRC2012_val_00020043.JPEG +ILSVRC2012_val_00024567.JPEG +ILSVRC2012_val_00016465.JPEG +ILSVRC2012_val_00020606.JPEG +ILSVRC2012_val_00049841.JPEG +ILSVRC2012_val_00040394.JPEG +ILSVRC2012_val_00046121.JPEG +ILSVRC2012_val_00023685.JPEG +ILSVRC2012_val_00002727.JPEG +ILSVRC2012_val_00032208.JPEG +ILSVRC2012_val_00008069.JPEG +ILSVRC2012_val_00025738.JPEG +ILSVRC2012_val_00019809.JPEG +ILSVRC2012_val_00032393.JPEG +ILSVRC2012_val_00039466.JPEG +ILSVRC2012_val_00011883.JPEG +ILSVRC2012_val_00007157.JPEG +ILSVRC2012_val_00003200.JPEG +ILSVRC2012_val_00043471.JPEG +ILSVRC2012_val_00001770.JPEG +ILSVRC2012_val_00047525.JPEG +ILSVRC2012_val_00007162.JPEG +ILSVRC2012_val_00040076.JPEG +ILSVRC2012_val_00010565.JPEG +ILSVRC2012_val_00001132.JPEG +ILSVRC2012_val_00026242.JPEG +ILSVRC2012_val_00025191.JPEG +ILSVRC2012_val_00000718.JPEG +ILSVRC2012_val_00012762.JPEG +ILSVRC2012_val_00023529.JPEG +ILSVRC2012_val_00016960.JPEG +ILSVRC2012_val_00036817.JPEG +ILSVRC2012_val_00043528.JPEG +ILSVRC2012_val_00034911.JPEG +ILSVRC2012_val_00012783.JPEG +ILSVRC2012_val_00044915.JPEG +ILSVRC2012_val_00006744.JPEG +ILSVRC2012_val_00048440.JPEG +ILSVRC2012_val_00008135.JPEG +ILSVRC2012_val_00005905.JPEG +ILSVRC2012_val_00025916.JPEG +ILSVRC2012_val_00035927.JPEG +ILSVRC2012_val_00049010.JPEG +ILSVRC2012_val_00011652.JPEG +ILSVRC2012_val_00010121.JPEG +ILSVRC2012_val_00024606.JPEG +ILSVRC2012_val_00010345.JPEG +ILSVRC2012_val_00049138.JPEG +ILSVRC2012_val_00002873.JPEG +ILSVRC2012_val_00023016.JPEG +ILSVRC2012_val_00020173.JPEG +ILSVRC2012_val_00043130.JPEG +ILSVRC2012_val_00023503.JPEG +ILSVRC2012_val_00000014.JPEG +ILSVRC2012_val_00006696.JPEG +ILSVRC2012_val_00015286.JPEG +ILSVRC2012_val_00023275.JPEG +ILSVRC2012_val_00017709.JPEG +ILSVRC2012_val_00007655.JPEG +ILSVRC2012_val_00030317.JPEG +ILSVRC2012_val_00008948.JPEG +ILSVRC2012_val_00008886.JPEG +ILSVRC2012_val_00011664.JPEG +ILSVRC2012_val_00037828.JPEG +ILSVRC2012_val_00039835.JPEG +ILSVRC2012_val_00034094.JPEG +ILSVRC2012_val_00011436.JPEG +ILSVRC2012_val_00047486.JPEG +ILSVRC2012_val_00022825.JPEG +ILSVRC2012_val_00038413.JPEG +ILSVRC2012_val_00017027.JPEG +ILSVRC2012_val_00047128.JPEG +ILSVRC2012_val_00034625.JPEG +ILSVRC2012_val_00036417.JPEG +ILSVRC2012_val_00047505.JPEG +ILSVRC2012_val_00042264.JPEG +ILSVRC2012_val_00036710.JPEG +ILSVRC2012_val_00039203.JPEG +ILSVRC2012_val_00030386.JPEG +ILSVRC2012_val_00000591.JPEG +ILSVRC2012_val_00002275.JPEG +ILSVRC2012_val_00042574.JPEG +ILSVRC2012_val_00039503.JPEG +ILSVRC2012_val_00037725.JPEG +ILSVRC2012_val_00037749.JPEG +ILSVRC2012_val_00021411.JPEG +ILSVRC2012_val_00016421.JPEG +ILSVRC2012_val_00028254.JPEG +ILSVRC2012_val_00013898.JPEG +ILSVRC2012_val_00006844.JPEG +ILSVRC2012_val_00046517.JPEG +ILSVRC2012_val_00010396.JPEG +ILSVRC2012_val_00015518.JPEG +ILSVRC2012_val_00029209.JPEG +ILSVRC2012_val_00001852.JPEG +ILSVRC2012_val_00009975.JPEG +ILSVRC2012_val_00001263.JPEG +ILSVRC2012_val_00032101.JPEG +ILSVRC2012_val_00037708.JPEG +ILSVRC2012_val_00037695.JPEG +ILSVRC2012_val_00021911.JPEG +ILSVRC2012_val_00047659.JPEG +ILSVRC2012_val_00006518.JPEG +ILSVRC2012_val_00036617.JPEG +ILSVRC2012_val_00023452.JPEG +ILSVRC2012_val_00002985.JPEG +ILSVRC2012_val_00010644.JPEG +ILSVRC2012_val_00046139.JPEG +ILSVRC2012_val_00040246.JPEG +ILSVRC2012_val_00009395.JPEG +ILSVRC2012_val_00036168.JPEG +ILSVRC2012_val_00028730.JPEG +ILSVRC2012_val_00006525.JPEG +ILSVRC2012_val_00032985.JPEG +ILSVRC2012_val_00024908.JPEG +ILSVRC2012_val_00011745.JPEG +ILSVRC2012_val_00008640.JPEG +ILSVRC2012_val_00005011.JPEG +ILSVRC2012_val_00031723.JPEG +ILSVRC2012_val_00006041.JPEG +ILSVRC2012_val_00007315.JPEG +ILSVRC2012_val_00032030.JPEG +ILSVRC2012_val_00049704.JPEG +ILSVRC2012_val_00002392.JPEG +ILSVRC2012_val_00035173.JPEG +ILSVRC2012_val_00036986.JPEG +ILSVRC2012_val_00010591.JPEG +ILSVRC2012_val_00007005.JPEG +ILSVRC2012_val_00002796.JPEG +ILSVRC2012_val_00004133.JPEG +ILSVRC2012_val_00027888.JPEG +ILSVRC2012_val_00019845.JPEG +ILSVRC2012_val_00037014.JPEG +ILSVRC2012_val_00022161.JPEG +ILSVRC2012_val_00023092.JPEG +ILSVRC2012_val_00014990.JPEG +ILSVRC2012_val_00044650.JPEG +ILSVRC2012_val_00030321.JPEG +ILSVRC2012_val_00021371.JPEG +ILSVRC2012_val_00049003.JPEG +ILSVRC2012_val_00023783.JPEG +ILSVRC2012_val_00038968.JPEG +ILSVRC2012_val_00046700.JPEG +ILSVRC2012_val_00013819.JPEG +ILSVRC2012_val_00000876.JPEG +ILSVRC2012_val_00048756.JPEG +ILSVRC2012_val_00036119.JPEG +ILSVRC2012_val_00025627.JPEG +ILSVRC2012_val_00044511.JPEG +ILSVRC2012_val_00045106.JPEG +ILSVRC2012_val_00014766.JPEG +ILSVRC2012_val_00042745.JPEG +ILSVRC2012_val_00015278.JPEG +ILSVRC2012_val_00009760.JPEG +ILSVRC2012_val_00021871.JPEG +ILSVRC2012_val_00003273.JPEG +ILSVRC2012_val_00030424.JPEG +ILSVRC2012_val_00044453.JPEG +ILSVRC2012_val_00007746.JPEG +ILSVRC2012_val_00021539.JPEG +ILSVRC2012_val_00001693.JPEG +ILSVRC2012_val_00034332.JPEG +ILSVRC2012_val_00037973.JPEG +ILSVRC2012_val_00011034.JPEG +ILSVRC2012_val_00030434.JPEG +ILSVRC2012_val_00032585.JPEG +ILSVRC2012_val_00049644.JPEG +ILSVRC2012_val_00038043.JPEG +ILSVRC2012_val_00018525.JPEG +ILSVRC2012_val_00013699.JPEG +ILSVRC2012_val_00044388.JPEG +ILSVRC2012_val_00047727.JPEG +ILSVRC2012_val_00045396.JPEG +ILSVRC2012_val_00031725.JPEG +ILSVRC2012_val_00019019.JPEG +ILSVRC2012_val_00045825.JPEG +ILSVRC2012_val_00029786.JPEG +ILSVRC2012_val_00018128.JPEG +ILSVRC2012_val_00014218.JPEG +ILSVRC2012_val_00017202.JPEG +ILSVRC2012_val_00025765.JPEG +ILSVRC2012_val_00044167.JPEG +ILSVRC2012_val_00005322.JPEG +ILSVRC2012_val_00026673.JPEG +ILSVRC2012_val_00034666.JPEG +ILSVRC2012_val_00007928.JPEG +ILSVRC2012_val_00005582.JPEG +ILSVRC2012_val_00018837.JPEG +ILSVRC2012_val_00020260.JPEG +ILSVRC2012_val_00017259.JPEG +ILSVRC2012_val_00013300.JPEG +ILSVRC2012_val_00048894.JPEG +ILSVRC2012_val_00015560.JPEG +ILSVRC2012_val_00040716.JPEG +ILSVRC2012_val_00044493.JPEG +ILSVRC2012_val_00007487.JPEG +ILSVRC2012_val_00035705.JPEG +ILSVRC2012_val_00044935.JPEG +ILSVRC2012_val_00031634.JPEG +ILSVRC2012_val_00047489.JPEG +ILSVRC2012_val_00040200.JPEG +ILSVRC2012_val_00000483.JPEG +ILSVRC2012_val_00017477.JPEG +ILSVRC2012_val_00029642.JPEG +ILSVRC2012_val_00026046.JPEG +ILSVRC2012_val_00005654.JPEG +ILSVRC2012_val_00035091.JPEG +ILSVRC2012_val_00022966.JPEG +ILSVRC2012_val_00018549.JPEG +ILSVRC2012_val_00010059.JPEG +ILSVRC2012_val_00041385.JPEG +ILSVRC2012_val_00021942.JPEG +ILSVRC2012_val_00013582.JPEG +ILSVRC2012_val_00024795.JPEG +ILSVRC2012_val_00044685.JPEG +ILSVRC2012_val_00040056.JPEG +ILSVRC2012_val_00010513.JPEG +ILSVRC2012_val_00044296.JPEG +ILSVRC2012_val_00043634.JPEG +ILSVRC2012_val_00033934.JPEG +ILSVRC2012_val_00031648.JPEG +ILSVRC2012_val_00046067.JPEG +ILSVRC2012_val_00026197.JPEG +ILSVRC2012_val_00014372.JPEG +ILSVRC2012_val_00000984.JPEG +ILSVRC2012_val_00035826.JPEG +ILSVRC2012_val_00044385.JPEG +ILSVRC2012_val_00039760.JPEG +ILSVRC2012_val_00009603.JPEG +ILSVRC2012_val_00024001.JPEG +ILSVRC2012_val_00000042.JPEG +ILSVRC2012_val_00032729.JPEG +ILSVRC2012_val_00000931.JPEG +ILSVRC2012_val_00042902.JPEG +ILSVRC2012_val_00018763.JPEG +ILSVRC2012_val_00016384.JPEG +ILSVRC2012_val_00041128.JPEG +ILSVRC2012_val_00048671.JPEG +ILSVRC2012_val_00024369.JPEG +ILSVRC2012_val_00024350.JPEG +ILSVRC2012_val_00003938.JPEG +ILSVRC2012_val_00045903.JPEG +ILSVRC2012_val_00008657.JPEG +ILSVRC2012_val_00044281.JPEG +ILSVRC2012_val_00017897.JPEG +ILSVRC2012_val_00009848.JPEG +ILSVRC2012_val_00016422.JPEG +ILSVRC2012_val_00045727.JPEG +ILSVRC2012_val_00046587.JPEG +ILSVRC2012_val_00018257.JPEG +ILSVRC2012_val_00037766.JPEG +ILSVRC2012_val_00041067.JPEG +ILSVRC2012_val_00000078.JPEG +ILSVRC2012_val_00040557.JPEG +ILSVRC2012_val_00011863.JPEG +ILSVRC2012_val_00011541.JPEG +ILSVRC2012_val_00036109.JPEG +ILSVRC2012_val_00013250.JPEG +ILSVRC2012_val_00021852.JPEG +ILSVRC2012_val_00000401.JPEG +ILSVRC2012_val_00003319.JPEG +ILSVRC2012_val_00040612.JPEG +ILSVRC2012_val_00026783.JPEG +ILSVRC2012_val_00020653.JPEG +ILSVRC2012_val_00027324.JPEG +ILSVRC2012_val_00002692.JPEG +ILSVRC2012_val_00024716.JPEG +ILSVRC2012_val_00047650.JPEG +ILSVRC2012_val_00013731.JPEG +ILSVRC2012_val_00033638.JPEG +ILSVRC2012_val_00041408.JPEG +ILSVRC2012_val_00020671.JPEG +ILSVRC2012_val_00047693.JPEG +ILSVRC2012_val_00001018.JPEG +ILSVRC2012_val_00005560.JPEG +ILSVRC2012_val_00034151.JPEG +ILSVRC2012_val_00011407.JPEG +ILSVRC2012_val_00036935.JPEG +ILSVRC2012_val_00017379.JPEG +ILSVRC2012_val_00023053.JPEG +ILSVRC2012_val_00033496.JPEG +ILSVRC2012_val_00024370.JPEG +ILSVRC2012_val_00046935.JPEG +ILSVRC2012_val_00012047.JPEG +ILSVRC2012_val_00019290.JPEG +ILSVRC2012_val_00016836.JPEG +ILSVRC2012_val_00007373.JPEG +ILSVRC2012_val_00048216.JPEG +ILSVRC2012_val_00035872.JPEG +ILSVRC2012_val_00005079.JPEG +ILSVRC2012_val_00034765.JPEG +ILSVRC2012_val_00019198.JPEG +ILSVRC2012_val_00017320.JPEG +ILSVRC2012_val_00015370.JPEG +ILSVRC2012_val_00030958.JPEG +ILSVRC2012_val_00016932.JPEG +ILSVRC2012_val_00047111.JPEG +ILSVRC2012_val_00001692.JPEG +ILSVRC2012_val_00023973.JPEG +ILSVRC2012_val_00042837.JPEG +ILSVRC2012_val_00012045.JPEG +ILSVRC2012_val_00028399.JPEG +ILSVRC2012_val_00002582.JPEG +ILSVRC2012_val_00014302.JPEG +ILSVRC2012_val_00021562.JPEG +ILSVRC2012_val_00043360.JPEG +ILSVRC2012_val_00021891.JPEG +ILSVRC2012_val_00023858.JPEG +ILSVRC2012_val_00035066.JPEG +ILSVRC2012_val_00044367.JPEG +ILSVRC2012_val_00040868.JPEG +ILSVRC2012_val_00046854.JPEG +ILSVRC2012_val_00023803.JPEG +ILSVRC2012_val_00049755.JPEG +ILSVRC2012_val_00048462.JPEG +ILSVRC2012_val_00035645.JPEG +ILSVRC2012_val_00004298.JPEG +ILSVRC2012_val_00019052.JPEG +ILSVRC2012_val_00025974.JPEG +ILSVRC2012_val_00018785.JPEG +ILSVRC2012_val_00010249.JPEG +ILSVRC2012_val_00048073.JPEG +ILSVRC2012_val_00019937.JPEG +ILSVRC2012_val_00025837.JPEG +ILSVRC2012_val_00021805.JPEG +ILSVRC2012_val_00024095.JPEG +ILSVRC2012_val_00006125.JPEG +ILSVRC2012_val_00045635.JPEG +ILSVRC2012_val_00018224.JPEG +ILSVRC2012_val_00012662.JPEG +ILSVRC2012_val_00033730.JPEG +ILSVRC2012_val_00027798.JPEG +ILSVRC2012_val_00049845.JPEG +ILSVRC2012_val_00024728.JPEG +ILSVRC2012_val_00049072.JPEG +ILSVRC2012_val_00041076.JPEG +ILSVRC2012_val_00039952.JPEG +ILSVRC2012_val_00017612.JPEG +ILSVRC2012_val_00008459.JPEG +ILSVRC2012_val_00045637.JPEG +ILSVRC2012_val_00045166.JPEG +ILSVRC2012_val_00010016.JPEG +ILSVRC2012_val_00021005.JPEG +ILSVRC2012_val_00005639.JPEG +ILSVRC2012_val_00035976.JPEG +ILSVRC2012_val_00016236.JPEG +ILSVRC2012_val_00021360.JPEG +ILSVRC2012_val_00049646.JPEG +ILSVRC2012_val_00046485.JPEG +ILSVRC2012_val_00004644.JPEG +ILSVRC2012_val_00022674.JPEG +ILSVRC2012_val_00011846.JPEG +ILSVRC2012_val_00014614.JPEG +ILSVRC2012_val_00016866.JPEG +ILSVRC2012_val_00002350.JPEG +ILSVRC2012_val_00018243.JPEG +ILSVRC2012_val_00002693.JPEG +ILSVRC2012_val_00010446.JPEG +ILSVRC2012_val_00016388.JPEG +ILSVRC2012_val_00027369.JPEG +ILSVRC2012_val_00017340.JPEG +ILSVRC2012_val_00045786.JPEG +ILSVRC2012_val_00019077.JPEG +ILSVRC2012_val_00044123.JPEG +ILSVRC2012_val_00018880.JPEG +ILSVRC2012_val_00011442.JPEG +ILSVRC2012_val_00037100.JPEG +ILSVRC2012_val_00016942.JPEG +ILSVRC2012_val_00042839.JPEG +ILSVRC2012_val_00031729.JPEG +ILSVRC2012_val_00010934.JPEG +ILSVRC2012_val_00001509.JPEG +ILSVRC2012_val_00006153.JPEG +ILSVRC2012_val_00033347.JPEG +ILSVRC2012_val_00010816.JPEG +ILSVRC2012_val_00034299.JPEG +ILSVRC2012_val_00008552.JPEG +ILSVRC2012_val_00040402.JPEG +ILSVRC2012_val_00049456.JPEG +ILSVRC2012_val_00013751.JPEG +ILSVRC2012_val_00047416.JPEG +ILSVRC2012_val_00029788.JPEG +ILSVRC2012_val_00028413.JPEG +ILSVRC2012_val_00036004.JPEG +ILSVRC2012_val_00007776.JPEG +ILSVRC2012_val_00031108.JPEG +ILSVRC2012_val_00035106.JPEG +ILSVRC2012_val_00002343.JPEG +ILSVRC2012_val_00012451.JPEG +ILSVRC2012_val_00039523.JPEG +ILSVRC2012_val_00019944.JPEG +ILSVRC2012_val_00044326.JPEG +ILSVRC2012_val_00018930.JPEG +ILSVRC2012_val_00043737.JPEG +ILSVRC2012_val_00002448.JPEG +ILSVRC2012_val_00014788.JPEG +ILSVRC2012_val_00010281.JPEG +ILSVRC2012_val_00000751.JPEG +ILSVRC2012_val_00010517.JPEG +ILSVRC2012_val_00019637.JPEG +ILSVRC2012_val_00015971.JPEG +ILSVRC2012_val_00042766.JPEG +ILSVRC2012_val_00024249.JPEG +ILSVRC2012_val_00038935.JPEG +ILSVRC2012_val_00032219.JPEG +ILSVRC2012_val_00036395.JPEG +ILSVRC2012_val_00003658.JPEG +ILSVRC2012_val_00029942.JPEG +ILSVRC2012_val_00008190.JPEG +ILSVRC2012_val_00024775.JPEG +ILSVRC2012_val_00030703.JPEG +ILSVRC2012_val_00005099.JPEG +ILSVRC2012_val_00020857.JPEG +ILSVRC2012_val_00023536.JPEG +ILSVRC2012_val_00006993.JPEG +ILSVRC2012_val_00020711.JPEG +ILSVRC2012_val_00026389.JPEG +ILSVRC2012_val_00027339.JPEG +ILSVRC2012_val_00020559.JPEG +ILSVRC2012_val_00031420.JPEG +ILSVRC2012_val_00005091.JPEG +ILSVRC2012_val_00007940.JPEG +ILSVRC2012_val_00006140.JPEG +ILSVRC2012_val_00030766.JPEG +ILSVRC2012_val_00034501.JPEG +ILSVRC2012_val_00048705.JPEG +ILSVRC2012_val_00034242.JPEG +ILSVRC2012_val_00019363.JPEG +ILSVRC2012_val_00024730.JPEG +ILSVRC2012_val_00024449.JPEG +ILSVRC2012_val_00003640.JPEG +ILSVRC2012_val_00016680.JPEG +ILSVRC2012_val_00013874.JPEG +ILSVRC2012_val_00039519.JPEG +ILSVRC2012_val_00000334.JPEG +ILSVRC2012_val_00001712.JPEG +ILSVRC2012_val_00016798.JPEG +ILSVRC2012_val_00013181.JPEG +ILSVRC2012_val_00012945.JPEG +ILSVRC2012_val_00040486.JPEG +ILSVRC2012_val_00023747.JPEG +ILSVRC2012_val_00045894.JPEG +ILSVRC2012_val_00029329.JPEG +ILSVRC2012_val_00037666.JPEG +ILSVRC2012_val_00025894.JPEG +ILSVRC2012_val_00036845.JPEG +ILSVRC2012_val_00049278.JPEG +ILSVRC2012_val_00044929.JPEG +ILSVRC2012_val_00035223.JPEG +ILSVRC2012_val_00042224.JPEG +ILSVRC2012_val_00049303.JPEG +ILSVRC2012_val_00006542.JPEG +ILSVRC2012_val_00023611.JPEG +ILSVRC2012_val_00020086.JPEG +ILSVRC2012_val_00023838.JPEG +ILSVRC2012_val_00034690.JPEG +ILSVRC2012_val_00019501.JPEG +ILSVRC2012_val_00000223.JPEG +ILSVRC2012_val_00029224.JPEG +ILSVRC2012_val_00033257.JPEG +ILSVRC2012_val_00019668.JPEG +ILSVRC2012_val_00038060.JPEG +ILSVRC2012_val_00047758.JPEG +ILSVRC2012_val_00043018.JPEG +ILSVRC2012_val_00039892.JPEG +ILSVRC2012_val_00015450.JPEG +ILSVRC2012_val_00019142.JPEG +ILSVRC2012_val_00032777.JPEG +ILSVRC2012_val_00037343.JPEG +ILSVRC2012_val_00023684.JPEG +ILSVRC2012_val_00047478.JPEG +ILSVRC2012_val_00014506.JPEG +ILSVRC2012_val_00045845.JPEG +ILSVRC2012_val_00044119.JPEG +ILSVRC2012_val_00034303.JPEG +ILSVRC2012_val_00006151.JPEG +ILSVRC2012_val_00028968.JPEG +ILSVRC2012_val_00043814.JPEG +ILSVRC2012_val_00046041.JPEG +ILSVRC2012_val_00041083.JPEG +ILSVRC2012_val_00031286.JPEG +ILSVRC2012_val_00035820.JPEG +ILSVRC2012_val_00031008.JPEG +ILSVRC2012_val_00012847.JPEG +ILSVRC2012_val_00038212.JPEG +ILSVRC2012_val_00026055.JPEG +ILSVRC2012_val_00035938.JPEG +ILSVRC2012_val_00008178.JPEG +ILSVRC2012_val_00005575.JPEG +ILSVRC2012_val_00013464.JPEG +ILSVRC2012_val_00005283.JPEG +ILSVRC2012_val_00006829.JPEG +ILSVRC2012_val_00011650.JPEG +ILSVRC2012_val_00032021.JPEG +ILSVRC2012_val_00016760.JPEG +ILSVRC2012_val_00008031.JPEG +ILSVRC2012_val_00011608.JPEG +ILSVRC2012_val_00004519.JPEG +ILSVRC2012_val_00046367.JPEG +ILSVRC2012_val_00026067.JPEG +ILSVRC2012_val_00023130.JPEG +ILSVRC2012_val_00023223.JPEG +ILSVRC2012_val_00023784.JPEG +ILSVRC2012_val_00036833.JPEG +ILSVRC2012_val_00029053.JPEG +ILSVRC2012_val_00006793.JPEG +ILSVRC2012_val_00042254.JPEG +ILSVRC2012_val_00009356.JPEG +ILSVRC2012_val_00002473.JPEG +ILSVRC2012_val_00027879.JPEG +ILSVRC2012_val_00048718.JPEG +ILSVRC2012_val_00046091.JPEG +ILSVRC2012_val_00015715.JPEG +ILSVRC2012_val_00018794.JPEG +ILSVRC2012_val_00028229.JPEG +ILSVRC2012_val_00019795.JPEG +ILSVRC2012_val_00038865.JPEG +ILSVRC2012_val_00044381.JPEG +ILSVRC2012_val_00016168.JPEG +ILSVRC2012_val_00013124.JPEG +ILSVRC2012_val_00026174.JPEG +ILSVRC2012_val_00037277.JPEG +ILSVRC2012_val_00046141.JPEG +ILSVRC2012_val_00009567.JPEG +ILSVRC2012_val_00034937.JPEG +ILSVRC2012_val_00037458.JPEG +ILSVRC2012_val_00014525.JPEG +ILSVRC2012_val_00040774.JPEG +ILSVRC2012_val_00036673.JPEG +ILSVRC2012_val_00018987.JPEG +ILSVRC2012_val_00026225.JPEG +ILSVRC2012_val_00011020.JPEG +ILSVRC2012_val_00010100.JPEG +ILSVRC2012_val_00023262.JPEG +ILSVRC2012_val_00026315.JPEG +ILSVRC2012_val_00001039.JPEG +ILSVRC2012_val_00037045.JPEG +ILSVRC2012_val_00015111.JPEG +ILSVRC2012_val_00008865.JPEG +ILSVRC2012_val_00036727.JPEG +ILSVRC2012_val_00036268.JPEG +ILSVRC2012_val_00026560.JPEG +ILSVRC2012_val_00017152.JPEG +ILSVRC2012_val_00031172.JPEG +ILSVRC2012_val_00025045.JPEG +ILSVRC2012_val_00031796.JPEG +ILSVRC2012_val_00045860.JPEG +ILSVRC2012_val_00028417.JPEG +ILSVRC2012_val_00002328.JPEG +ILSVRC2012_val_00018363.JPEG +ILSVRC2012_val_00047010.JPEG +ILSVRC2012_val_00014914.JPEG +ILSVRC2012_val_00030772.JPEG +ILSVRC2012_val_00028078.JPEG +ILSVRC2012_val_00028582.JPEG +ILSVRC2012_val_00019814.JPEG +ILSVRC2012_val_00037321.JPEG +ILSVRC2012_val_00036551.JPEG +ILSVRC2012_val_00047182.JPEG +ILSVRC2012_val_00014861.JPEG +ILSVRC2012_val_00017749.JPEG +ILSVRC2012_val_00011444.JPEG +ILSVRC2012_val_00010877.JPEG +ILSVRC2012_val_00021048.JPEG +ILSVRC2012_val_00004349.JPEG +ILSVRC2012_val_00004562.JPEG +ILSVRC2012_val_00041682.JPEG +ILSVRC2012_val_00009858.JPEG +ILSVRC2012_val_00040891.JPEG +ILSVRC2012_val_00009410.JPEG +ILSVRC2012_val_00019149.JPEG +ILSVRC2012_val_00018914.JPEG +ILSVRC2012_val_00016638.JPEG +ILSVRC2012_val_00023027.JPEG +ILSVRC2012_val_00016078.JPEG +ILSVRC2012_val_00041613.JPEG +ILSVRC2012_val_00011525.JPEG +ILSVRC2012_val_00005947.JPEG +ILSVRC2012_val_00035823.JPEG +ILSVRC2012_val_00048564.JPEG +ILSVRC2012_val_00032145.JPEG +ILSVRC2012_val_00042045.JPEG +ILSVRC2012_val_00041464.JPEG +ILSVRC2012_val_00021754.JPEG +ILSVRC2012_val_00020084.JPEG +ILSVRC2012_val_00013609.JPEG +ILSVRC2012_val_00028043.JPEG +ILSVRC2012_val_00002434.JPEG +ILSVRC2012_val_00048124.JPEG +ILSVRC2012_val_00035879.JPEG +ILSVRC2012_val_00039557.JPEG +ILSVRC2012_val_00032990.JPEG +ILSVRC2012_val_00038669.JPEG +ILSVRC2012_val_00006209.JPEG +ILSVRC2012_val_00032469.JPEG +ILSVRC2012_val_00028767.JPEG +ILSVRC2012_val_00013266.JPEG +ILSVRC2012_val_00009934.JPEG +ILSVRC2012_val_00010660.JPEG +ILSVRC2012_val_00009626.JPEG +ILSVRC2012_val_00022236.JPEG +ILSVRC2012_val_00011070.JPEG +ILSVRC2012_val_00038151.JPEG +ILSVRC2012_val_00007671.JPEG +ILSVRC2012_val_00017380.JPEG +ILSVRC2012_val_00012628.JPEG +ILSVRC2012_val_00034536.JPEG +ILSVRC2012_val_00004713.JPEG +ILSVRC2012_val_00021248.JPEG +ILSVRC2012_val_00006031.JPEG +ILSVRC2012_val_00021438.JPEG +ILSVRC2012_val_00034986.JPEG +ILSVRC2012_val_00019087.JPEG +ILSVRC2012_val_00003229.JPEG +ILSVRC2012_val_00020956.JPEG +ILSVRC2012_val_00040308.JPEG +ILSVRC2012_val_00011527.JPEG +ILSVRC2012_val_00026993.JPEG +ILSVRC2012_val_00027622.JPEG +ILSVRC2012_val_00032538.JPEG +ILSVRC2012_val_00047716.JPEG +ILSVRC2012_val_00010392.JPEG +ILSVRC2012_val_00000675.JPEG +ILSVRC2012_val_00025774.JPEG +ILSVRC2012_val_00017628.JPEG +ILSVRC2012_val_00000847.JPEG +ILSVRC2012_val_00048279.JPEG +ILSVRC2012_val_00015269.JPEG +ILSVRC2012_val_00000600.JPEG +ILSVRC2012_val_00010539.JPEG +ILSVRC2012_val_00016607.JPEG +ILSVRC2012_val_00034486.JPEG +ILSVRC2012_val_00033866.JPEG +ILSVRC2012_val_00005448.JPEG +ILSVRC2012_val_00001035.JPEG +ILSVRC2012_val_00048695.JPEG +ILSVRC2012_val_00027965.JPEG +ILSVRC2012_val_00047558.JPEG +ILSVRC2012_val_00007631.JPEG +ILSVRC2012_val_00031215.JPEG +ILSVRC2012_val_00003562.JPEG +ILSVRC2012_val_00033526.JPEG +ILSVRC2012_val_00036195.JPEG +ILSVRC2012_val_00003901.JPEG +ILSVRC2012_val_00043042.JPEG +ILSVRC2012_val_00046995.JPEG +ILSVRC2012_val_00005584.JPEG +ILSVRC2012_val_00030673.JPEG +ILSVRC2012_val_00041244.JPEG +ILSVRC2012_val_00049573.JPEG +ILSVRC2012_val_00010319.JPEG +ILSVRC2012_val_00027271.JPEG +ILSVRC2012_val_00018801.JPEG +ILSVRC2012_val_00044171.JPEG +ILSVRC2012_val_00037763.JPEG +ILSVRC2012_val_00035903.JPEG +ILSVRC2012_val_00004092.JPEG +ILSVRC2012_val_00001179.JPEG +ILSVRC2012_val_00026821.JPEG +ILSVRC2012_val_00012524.JPEG +ILSVRC2012_val_00037323.JPEG +ILSVRC2012_val_00004400.JPEG +ILSVRC2012_val_00045368.JPEG +ILSVRC2012_val_00018779.JPEG +ILSVRC2012_val_00003785.JPEG +ILSVRC2012_val_00027863.JPEG +ILSVRC2012_val_00046646.JPEG +ILSVRC2012_val_00035531.JPEG +ILSVRC2012_val_00009256.JPEG +ILSVRC2012_val_00003749.JPEG +ILSVRC2012_val_00026433.JPEG +ILSVRC2012_val_00029443.JPEG +ILSVRC2012_val_00013335.JPEG +ILSVRC2012_val_00047582.JPEG +ILSVRC2012_val_00049184.JPEG +ILSVRC2012_val_00009762.JPEG +ILSVRC2012_val_00024750.JPEG +ILSVRC2012_val_00015941.JPEG +ILSVRC2012_val_00036039.JPEG +ILSVRC2012_val_00019174.JPEG +ILSVRC2012_val_00045340.JPEG +ILSVRC2012_val_00006763.JPEG +ILSVRC2012_val_00023467.JPEG +ILSVRC2012_val_00031949.JPEG +ILSVRC2012_val_00045609.JPEG +ILSVRC2012_val_00018347.JPEG +ILSVRC2012_val_00021814.JPEG +ILSVRC2012_val_00021683.JPEG +ILSVRC2012_val_00042074.JPEG +ILSVRC2012_val_00003410.JPEG +ILSVRC2012_val_00025424.JPEG +ILSVRC2012_val_00046273.JPEG +ILSVRC2012_val_00035630.JPEG +ILSVRC2012_val_00035027.JPEG +ILSVRC2012_val_00011279.JPEG +ILSVRC2012_val_00000500.JPEG +ILSVRC2012_val_00001458.JPEG +ILSVRC2012_val_00030641.JPEG +ILSVRC2012_val_00038449.JPEG +ILSVRC2012_val_00016563.JPEG +ILSVRC2012_val_00007299.JPEG +ILSVRC2012_val_00001163.JPEG +ILSVRC2012_val_00034337.JPEG +ILSVRC2012_val_00004855.JPEG +ILSVRC2012_val_00044486.JPEG +ILSVRC2012_val_00026008.JPEG +ILSVRC2012_val_00025662.JPEG +ILSVRC2012_val_00033107.JPEG +ILSVRC2012_val_00036493.JPEG +ILSVRC2012_val_00044932.JPEG +ILSVRC2012_val_00040527.JPEG +ILSVRC2012_val_00047982.JPEG +ILSVRC2012_val_00031109.JPEG +ILSVRC2012_val_00018482.JPEG +ILSVRC2012_val_00036646.JPEG +ILSVRC2012_val_00027693.JPEG +ILSVRC2012_val_00043979.JPEG +ILSVRC2012_val_00045173.JPEG +ILSVRC2012_val_00035387.JPEG +ILSVRC2012_val_00011409.JPEG +ILSVRC2012_val_00022215.JPEG +ILSVRC2012_val_00001539.JPEG +ILSVRC2012_val_00030937.JPEG +ILSVRC2012_val_00026213.JPEG +ILSVRC2012_val_00038214.JPEG +ILSVRC2012_val_00013273.JPEG +ILSVRC2012_val_00003377.JPEG +ILSVRC2012_val_00004543.JPEG +ILSVRC2012_val_00023561.JPEG +ILSVRC2012_val_00044028.JPEG +ILSVRC2012_val_00041735.JPEG +ILSVRC2012_val_00032670.JPEG +ILSVRC2012_val_00036429.JPEG +ILSVRC2012_val_00040814.JPEG +ILSVRC2012_val_00039316.JPEG +ILSVRC2012_val_00015768.JPEG +ILSVRC2012_val_00025363.JPEG +ILSVRC2012_val_00041085.JPEG +ILSVRC2012_val_00047255.JPEG +ILSVRC2012_val_00000351.JPEG +ILSVRC2012_val_00029602.JPEG +ILSVRC2012_val_00032837.JPEG +ILSVRC2012_val_00042925.JPEG +ILSVRC2012_val_00028758.JPEG +ILSVRC2012_val_00036342.JPEG +ILSVRC2012_val_00016269.JPEG +ILSVRC2012_val_00033975.JPEG +ILSVRC2012_val_00019988.JPEG +ILSVRC2012_val_00037086.JPEG +ILSVRC2012_val_00022973.JPEG +ILSVRC2012_val_00039615.JPEG +ILSVRC2012_val_00016398.JPEG +ILSVRC2012_val_00041008.JPEG +ILSVRC2012_val_00032932.JPEG +ILSVRC2012_val_00023332.JPEG +ILSVRC2012_val_00041730.JPEG +ILSVRC2012_val_00024871.JPEG +ILSVRC2012_val_00010468.JPEG +ILSVRC2012_val_00030892.JPEG +ILSVRC2012_val_00036276.JPEG +ILSVRC2012_val_00035396.JPEG +ILSVRC2012_val_00032130.JPEG +ILSVRC2012_val_00026577.JPEG +ILSVRC2012_val_00024694.JPEG +ILSVRC2012_val_00036816.JPEG +ILSVRC2012_val_00002837.JPEG +ILSVRC2012_val_00001904.JPEG +ILSVRC2012_val_00027628.JPEG +ILSVRC2012_val_00000924.JPEG +ILSVRC2012_val_00001419.JPEG +ILSVRC2012_val_00009305.JPEG +ILSVRC2012_val_00026254.JPEG +ILSVRC2012_val_00022314.JPEG +ILSVRC2012_val_00017475.JPEG +ILSVRC2012_val_00037598.JPEG +ILSVRC2012_val_00047049.JPEG +ILSVRC2012_val_00042656.JPEG +ILSVRC2012_val_00042869.JPEG +ILSVRC2012_val_00000935.JPEG +ILSVRC2012_val_00010682.JPEG +ILSVRC2012_val_00039528.JPEG +ILSVRC2012_val_00028668.JPEG +ILSVRC2012_val_00046110.JPEG +ILSVRC2012_val_00031907.JPEG +ILSVRC2012_val_00016108.JPEG +ILSVRC2012_val_00011887.JPEG +ILSVRC2012_val_00023563.JPEG +ILSVRC2012_val_00032793.JPEG +ILSVRC2012_val_00038253.JPEG +ILSVRC2012_val_00035815.JPEG +ILSVRC2012_val_00021843.JPEG +ILSVRC2012_val_00006199.JPEG +ILSVRC2012_val_00048341.JPEG +ILSVRC2012_val_00033478.JPEG +ILSVRC2012_val_00010965.JPEG +ILSVRC2012_val_00005142.JPEG +ILSVRC2012_val_00039507.JPEG +ILSVRC2012_val_00013694.JPEG +ILSVRC2012_val_00009299.JPEG +ILSVRC2012_val_00014336.JPEG +ILSVRC2012_val_00028277.JPEG +ILSVRC2012_val_00039872.JPEG +ILSVRC2012_val_00022328.JPEG +ILSVRC2012_val_00045682.JPEG +ILSVRC2012_val_00019740.JPEG +ILSVRC2012_val_00021287.JPEG +ILSVRC2012_val_00012303.JPEG +ILSVRC2012_val_00025794.JPEG +ILSVRC2012_val_00048074.JPEG +ILSVRC2012_val_00001604.JPEG +ILSVRC2012_val_00032053.JPEG +ILSVRC2012_val_00028745.JPEG +ILSVRC2012_val_00012804.JPEG +ILSVRC2012_val_00029036.JPEG +ILSVRC2012_val_00043931.JPEG +ILSVRC2012_val_00024114.JPEG +ILSVRC2012_val_00031573.JPEG +ILSVRC2012_val_00020872.JPEG +ILSVRC2012_val_00047379.JPEG +ILSVRC2012_val_00023841.JPEG +ILSVRC2012_val_00040693.JPEG +ILSVRC2012_val_00002704.JPEG +ILSVRC2012_val_00003079.JPEG +ILSVRC2012_val_00019481.JPEG +ILSVRC2012_val_00030953.JPEG +ILSVRC2012_val_00009530.JPEG +ILSVRC2012_val_00039955.JPEG +ILSVRC2012_val_00043148.JPEG +ILSVRC2012_val_00044733.JPEG +ILSVRC2012_val_00042102.JPEG +ILSVRC2012_val_00049589.JPEG +ILSVRC2012_val_00001706.JPEG +ILSVRC2012_val_00049355.JPEG +ILSVRC2012_val_00035915.JPEG +ILSVRC2012_val_00037883.JPEG +ILSVRC2012_val_00011328.JPEG +ILSVRC2012_val_00001571.JPEG +ILSVRC2012_val_00029964.JPEG +ILSVRC2012_val_00024329.JPEG +ILSVRC2012_val_00040651.JPEG +ILSVRC2012_val_00009469.JPEG +ILSVRC2012_val_00013113.JPEG +ILSVRC2012_val_00020385.JPEG +ILSVRC2012_val_00020780.JPEG +ILSVRC2012_val_00003322.JPEG +ILSVRC2012_val_00008605.JPEG +ILSVRC2012_val_00049741.JPEG +ILSVRC2012_val_00048017.JPEG +ILSVRC2012_val_00014616.JPEG +ILSVRC2012_val_00009455.JPEG +ILSVRC2012_val_00022418.JPEG +ILSVRC2012_val_00033156.JPEG +ILSVRC2012_val_00025460.JPEG +ILSVRC2012_val_00046219.JPEG +ILSVRC2012_val_00012035.JPEG +ILSVRC2012_val_00036371.JPEG +ILSVRC2012_val_00039433.JPEG +ILSVRC2012_val_00000622.JPEG +ILSVRC2012_val_00008420.JPEG +ILSVRC2012_val_00009002.JPEG +ILSVRC2012_val_00002618.JPEG +ILSVRC2012_val_00040966.JPEG +ILSVRC2012_val_00030207.JPEG +ILSVRC2012_val_00019653.JPEG +ILSVRC2012_val_00005188.JPEG +ILSVRC2012_val_00017463.JPEG +ILSVRC2012_val_00032088.JPEG +ILSVRC2012_val_00001262.JPEG +ILSVRC2012_val_00004712.JPEG +ILSVRC2012_val_00049897.JPEG +ILSVRC2012_val_00038794.JPEG +ILSVRC2012_val_00002607.JPEG +ILSVRC2012_val_00020651.JPEG +ILSVRC2012_val_00022499.JPEG +ILSVRC2012_val_00022042.JPEG +ILSVRC2012_val_00031694.JPEG +ILSVRC2012_val_00011077.JPEG +ILSVRC2012_val_00049712.JPEG +ILSVRC2012_val_00016813.JPEG +ILSVRC2012_val_00029212.JPEG +ILSVRC2012_val_00041773.JPEG +ILSVRC2012_val_00016125.JPEG +ILSVRC2012_val_00046274.JPEG +ILSVRC2012_val_00012379.JPEG +ILSVRC2012_val_00031388.JPEG +ILSVRC2012_val_00022261.JPEG +ILSVRC2012_val_00028260.JPEG +ILSVRC2012_val_00047585.JPEG +ILSVRC2012_val_00002991.JPEG +ILSVRC2012_val_00021364.JPEG +ILSVRC2012_val_00012807.JPEG +ILSVRC2012_val_00030373.JPEG +ILSVRC2012_val_00029210.JPEG +ILSVRC2012_val_00031100.JPEG +ILSVRC2012_val_00039568.JPEG +ILSVRC2012_val_00045568.JPEG +ILSVRC2012_val_00013382.JPEG +ILSVRC2012_val_00042321.JPEG +ILSVRC2012_val_00044596.JPEG +ILSVRC2012_val_00010608.JPEG +ILSVRC2012_val_00026368.JPEG +ILSVRC2012_val_00006470.JPEG +ILSVRC2012_val_00042660.JPEG +ILSVRC2012_val_00044767.JPEG +ILSVRC2012_val_00002664.JPEG +ILSVRC2012_val_00021490.JPEG +ILSVRC2012_val_00046857.JPEG +ILSVRC2012_val_00012062.JPEG +ILSVRC2012_val_00020654.JPEG +ILSVRC2012_val_00021989.JPEG +ILSVRC2012_val_00043918.JPEG +ILSVRC2012_val_00042353.JPEG +ILSVRC2012_val_00024088.JPEG +ILSVRC2012_val_00035875.JPEG +ILSVRC2012_val_00022087.JPEG +ILSVRC2012_val_00003072.JPEG +ILSVRC2012_val_00045061.JPEG +ILSVRC2012_val_00000834.JPEG +ILSVRC2012_val_00014571.JPEG +ILSVRC2012_val_00027456.JPEG +ILSVRC2012_val_00011553.JPEG +ILSVRC2012_val_00045384.JPEG +ILSVRC2012_val_00028217.JPEG +ILSVRC2012_val_00023812.JPEG +ILSVRC2012_val_00018841.JPEG +ILSVRC2012_val_00019079.JPEG +ILSVRC2012_val_00048455.JPEG +ILSVRC2012_val_00038239.JPEG +ILSVRC2012_val_00037001.JPEG +ILSVRC2012_val_00035746.JPEG +ILSVRC2012_val_00030834.JPEG +ILSVRC2012_val_00014804.JPEG +ILSVRC2012_val_00036891.JPEG +ILSVRC2012_val_00037200.JPEG +ILSVRC2012_val_00008132.JPEG +ILSVRC2012_val_00042991.JPEG +ILSVRC2012_val_00014875.JPEG +ILSVRC2012_val_00007633.JPEG +ILSVRC2012_val_00020547.JPEG +ILSVRC2012_val_00007445.JPEG +ILSVRC2012_val_00031415.JPEG +ILSVRC2012_val_00044098.JPEG +ILSVRC2012_val_00018082.JPEG +ILSVRC2012_val_00049463.JPEG +ILSVRC2012_val_00020149.JPEG +ILSVRC2012_val_00036513.JPEG +ILSVRC2012_val_00023493.JPEG +ILSVRC2012_val_00030823.JPEG +ILSVRC2012_val_00038001.JPEG +ILSVRC2012_val_00048528.JPEG +ILSVRC2012_val_00002725.JPEG +ILSVRC2012_val_00027642.JPEG +ILSVRC2012_val_00014085.JPEG +ILSVRC2012_val_00019604.JPEG +ILSVRC2012_val_00007141.JPEG +ILSVRC2012_val_00019907.JPEG +ILSVRC2012_val_00034719.JPEG +ILSVRC2012_val_00048579.JPEG +ILSVRC2012_val_00017533.JPEG +ILSVRC2012_val_00015367.JPEG +ILSVRC2012_val_00038739.JPEG +ILSVRC2012_val_00036110.JPEG +ILSVRC2012_val_00034088.JPEG +ILSVRC2012_val_00000383.JPEG +ILSVRC2012_val_00015615.JPEG +ILSVRC2012_val_00004131.JPEG +ILSVRC2012_val_00012197.JPEG +ILSVRC2012_val_00023067.JPEG +ILSVRC2012_val_00006905.JPEG +ILSVRC2012_val_00038342.JPEG +ILSVRC2012_val_00046025.JPEG +ILSVRC2012_val_00015782.JPEG +ILSVRC2012_val_00028348.JPEG +ILSVRC2012_val_00019674.JPEG +ILSVRC2012_val_00049874.JPEG +ILSVRC2012_val_00048064.JPEG +ILSVRC2012_val_00041566.JPEG +ILSVRC2012_val_00007810.JPEG +ILSVRC2012_val_00018829.JPEG +ILSVRC2012_val_00025170.JPEG +ILSVRC2012_val_00020285.JPEG +ILSVRC2012_val_00030678.JPEG +ILSVRC2012_val_00028030.JPEG +ILSVRC2012_val_00021448.JPEG +ILSVRC2012_val_00031754.JPEG +ILSVRC2012_val_00043480.JPEG +ILSVRC2012_val_00035728.JPEG +ILSVRC2012_val_00014610.JPEG +ILSVRC2012_val_00019669.JPEG +ILSVRC2012_val_00009550.JPEG +ILSVRC2012_val_00049092.JPEG +ILSVRC2012_val_00029199.JPEG +ILSVRC2012_val_00015099.JPEG +ILSVRC2012_val_00007211.JPEG +ILSVRC2012_val_00039867.JPEG +ILSVRC2012_val_00038112.JPEG +ILSVRC2012_val_00032266.JPEG +ILSVRC2012_val_00032186.JPEG +ILSVRC2012_val_00035049.JPEG +ILSVRC2012_val_00021236.JPEG +ILSVRC2012_val_00049483.JPEG +ILSVRC2012_val_00011410.JPEG +ILSVRC2012_val_00042436.JPEG +ILSVRC2012_val_00049329.JPEG +ILSVRC2012_val_00027024.JPEG +ILSVRC2012_val_00036713.JPEG +ILSVRC2012_val_00035245.JPEG +ILSVRC2012_val_00041316.JPEG +ILSVRC2012_val_00045718.JPEG +ILSVRC2012_val_00003469.JPEG +ILSVRC2012_val_00040459.JPEG +ILSVRC2012_val_00000071.JPEG +ILSVRC2012_val_00036701.JPEG +ILSVRC2012_val_00000867.JPEG +ILSVRC2012_val_00021760.JPEG +ILSVRC2012_val_00001055.JPEG +ILSVRC2012_val_00029126.JPEG +ILSVRC2012_val_00015773.JPEG +ILSVRC2012_val_00002963.JPEG +ILSVRC2012_val_00019915.JPEG +ILSVRC2012_val_00047617.JPEG +ILSVRC2012_val_00031451.JPEG +ILSVRC2012_val_00012707.JPEG +ILSVRC2012_val_00016024.JPEG +ILSVRC2012_val_00005678.JPEG +ILSVRC2012_val_00025388.JPEG +ILSVRC2012_val_00039697.JPEG +ILSVRC2012_val_00016332.JPEG +ILSVRC2012_val_00028142.JPEG +ILSVRC2012_val_00040030.JPEG +ILSVRC2012_val_00019580.JPEG +ILSVRC2012_val_00027454.JPEG +ILSVRC2012_val_00034066.JPEG +ILSVRC2012_val_00027981.JPEG +ILSVRC2012_val_00022567.JPEG +ILSVRC2012_val_00036680.JPEG +ILSVRC2012_val_00035922.JPEG +ILSVRC2012_val_00027053.JPEG +ILSVRC2012_val_00017893.JPEG +ILSVRC2012_val_00033029.JPEG +ILSVRC2012_val_00046555.JPEG +ILSVRC2012_val_00003154.JPEG +ILSVRC2012_val_00017131.JPEG +ILSVRC2012_val_00030425.JPEG +ILSVRC2012_val_00025025.JPEG +ILSVRC2012_val_00032058.JPEG +ILSVRC2012_val_00037019.JPEG +ILSVRC2012_val_00004399.JPEG +ILSVRC2012_val_00025996.JPEG +ILSVRC2012_val_00005057.JPEG +ILSVRC2012_val_00037511.JPEG +ILSVRC2012_val_00045065.JPEG +ILSVRC2012_val_00011848.JPEG +ILSVRC2012_val_00026292.JPEG +ILSVRC2012_val_00011777.JPEG +ILSVRC2012_val_00033121.JPEG +ILSVRC2012_val_00045263.JPEG +ILSVRC2012_val_00044859.JPEG +ILSVRC2012_val_00006451.JPEG +ILSVRC2012_val_00016791.JPEG +ILSVRC2012_val_00044133.JPEG +ILSVRC2012_val_00043900.JPEG +ILSVRC2012_val_00008607.JPEG +ILSVRC2012_val_00011304.JPEG +ILSVRC2012_val_00046998.JPEG +ILSVRC2012_val_00001313.JPEG +ILSVRC2012_val_00042215.JPEG +ILSVRC2012_val_00049176.JPEG +ILSVRC2012_val_00029742.JPEG +ILSVRC2012_val_00026306.JPEG +ILSVRC2012_val_00045261.JPEG +ILSVRC2012_val_00002681.JPEG +ILSVRC2012_val_00027620.JPEG +ILSVRC2012_val_00014169.JPEG +ILSVRC2012_val_00020432.JPEG +ILSVRC2012_val_00011857.JPEG +ILSVRC2012_val_00009342.JPEG +ILSVRC2012_val_00033411.JPEG +ILSVRC2012_val_00035107.JPEG +ILSVRC2012_val_00039378.JPEG +ILSVRC2012_val_00048565.JPEG +ILSVRC2012_val_00048366.JPEG +ILSVRC2012_val_00044658.JPEG +ILSVRC2012_val_00003432.JPEG +ILSVRC2012_val_00033668.JPEG +ILSVRC2012_val_00047533.JPEG +ILSVRC2012_val_00041405.JPEG +ILSVRC2012_val_00032704.JPEG +ILSVRC2012_val_00028306.JPEG +ILSVRC2012_val_00002067.JPEG +ILSVRC2012_val_00001732.JPEG +ILSVRC2012_val_00014504.JPEG +ILSVRC2012_val_00006460.JPEG +ILSVRC2012_val_00012165.JPEG +ILSVRC2012_val_00042325.JPEG +ILSVRC2012_val_00015914.JPEG +ILSVRC2012_val_00021330.JPEG +ILSVRC2012_val_00035838.JPEG +ILSVRC2012_val_00015016.JPEG +ILSVRC2012_val_00002200.JPEG +ILSVRC2012_val_00033169.JPEG +ILSVRC2012_val_00020349.JPEG +ILSVRC2012_val_00034176.JPEG +ILSVRC2012_val_00001208.JPEG +ILSVRC2012_val_00032447.JPEG +ILSVRC2012_val_00001435.JPEG +ILSVRC2012_val_00031013.JPEG +ILSVRC2012_val_00004663.JPEG +ILSVRC2012_val_00018298.JPEG +ILSVRC2012_val_00003457.JPEG +ILSVRC2012_val_00016873.JPEG +ILSVRC2012_val_00035284.JPEG +ILSVRC2012_val_00021406.JPEG +ILSVRC2012_val_00027268.JPEG +ILSVRC2012_val_00007326.JPEG +ILSVRC2012_val_00006127.JPEG +ILSVRC2012_val_00002627.JPEG +ILSVRC2012_val_00042204.JPEG +ILSVRC2012_val_00044683.JPEG +ILSVRC2012_val_00009126.JPEG +ILSVRC2012_val_00009097.JPEG +ILSVRC2012_val_00017564.JPEG +ILSVRC2012_val_00017874.JPEG +ILSVRC2012_val_00022546.JPEG +ILSVRC2012_val_00048002.JPEG +ILSVRC2012_val_00000430.JPEG +ILSVRC2012_val_00038064.JPEG +ILSVRC2012_val_00031153.JPEG +ILSVRC2012_val_00023714.JPEG +ILSVRC2012_val_00015713.JPEG +ILSVRC2012_val_00009700.JPEG +ILSVRC2012_val_00009531.JPEG +ILSVRC2012_val_00034469.JPEG +ILSVRC2012_val_00026486.JPEG +ILSVRC2012_val_00028276.JPEG +ILSVRC2012_val_00006740.JPEG +ILSVRC2012_val_00023673.JPEG +ILSVRC2012_val_00026878.JPEG +ILSVRC2012_val_00003103.JPEG +ILSVRC2012_val_00015208.JPEG +ILSVRC2012_val_00038754.JPEG +ILSVRC2012_val_00030087.JPEG +ILSVRC2012_val_00047068.JPEG +ILSVRC2012_val_00013548.JPEG +ILSVRC2012_val_00025080.JPEG +ILSVRC2012_val_00029470.JPEG +ILSVRC2012_val_00023402.JPEG +ILSVRC2012_val_00030180.JPEG +ILSVRC2012_val_00034568.JPEG +ILSVRC2012_val_00014888.JPEG +ILSVRC2012_val_00022289.JPEG +ILSVRC2012_val_00005251.JPEG +ILSVRC2012_val_00003812.JPEG +ILSVRC2012_val_00000868.JPEG +ILSVRC2012_val_00048933.JPEG +ILSVRC2012_val_00031674.JPEG +ILSVRC2012_val_00010365.JPEG +ILSVRC2012_val_00031993.JPEG +ILSVRC2012_val_00020781.JPEG +ILSVRC2012_val_00039298.JPEG +ILSVRC2012_val_00036412.JPEG +ILSVRC2012_val_00020030.JPEG +ILSVRC2012_val_00026160.JPEG +ILSVRC2012_val_00011768.JPEG +ILSVRC2012_val_00016173.JPEG +ILSVRC2012_val_00019705.JPEG +ILSVRC2012_val_00045805.JPEG +ILSVRC2012_val_00029448.JPEG +ILSVRC2012_val_00001705.JPEG +ILSVRC2012_val_00014382.JPEG +ILSVRC2012_val_00041290.JPEG +ILSVRC2012_val_00019811.JPEG +ILSVRC2012_val_00021011.JPEG +ILSVRC2012_val_00028047.JPEG +ILSVRC2012_val_00022124.JPEG +ILSVRC2012_val_00020740.JPEG +ILSVRC2012_val_00001422.JPEG +ILSVRC2012_val_00001736.JPEG +ILSVRC2012_val_00001998.JPEG +ILSVRC2012_val_00002217.JPEG +ILSVRC2012_val_00033399.JPEG +ILSVRC2012_val_00040951.JPEG +ILSVRC2012_val_00007229.JPEG +ILSVRC2012_val_00020069.JPEG +ILSVRC2012_val_00025229.JPEG +ILSVRC2012_val_00021105.JPEG +ILSVRC2012_val_00044105.JPEG +ILSVRC2012_val_00021084.JPEG +ILSVRC2012_val_00004252.JPEG +ILSVRC2012_val_00040577.JPEG +ILSVRC2012_val_00008202.JPEG +ILSVRC2012_val_00004271.JPEG +ILSVRC2012_val_00045627.JPEG +ILSVRC2012_val_00047543.JPEG +ILSVRC2012_val_00008853.JPEG +ILSVRC2012_val_00017189.JPEG +ILSVRC2012_val_00020499.JPEG +ILSVRC2012_val_00040968.JPEG +ILSVRC2012_val_00017833.JPEG +ILSVRC2012_val_00030580.JPEG +ILSVRC2012_val_00034959.JPEG +ILSVRC2012_val_00030030.JPEG +ILSVRC2012_val_00038021.JPEG +ILSVRC2012_val_00040699.JPEG +ILSVRC2012_val_00025476.JPEG +ILSVRC2012_val_00018615.JPEG +ILSVRC2012_val_00025344.JPEG +ILSVRC2012_val_00035646.JPEG +ILSVRC2012_val_00002642.JPEG +ILSVRC2012_val_00017467.JPEG +ILSVRC2012_val_00017082.JPEG +ILSVRC2012_val_00045560.JPEG +ILSVRC2012_val_00023577.JPEG +ILSVRC2012_val_00019005.JPEG +ILSVRC2012_val_00010139.JPEG +ILSVRC2012_val_00034485.JPEG +ILSVRC2012_val_00001849.JPEG +ILSVRC2012_val_00042760.JPEG +ILSVRC2012_val_00028538.JPEG +ILSVRC2012_val_00048610.JPEG +ILSVRC2012_val_00023374.JPEG +ILSVRC2012_val_00016619.JPEG +ILSVRC2012_val_00025456.JPEG +ILSVRC2012_val_00003842.JPEG +ILSVRC2012_val_00026801.JPEG +ILSVRC2012_val_00007623.JPEG +ILSVRC2012_val_00043720.JPEG +ILSVRC2012_val_00026810.JPEG +ILSVRC2012_val_00043997.JPEG +ILSVRC2012_val_00040506.JPEG +ILSVRC2012_val_00026404.JPEG +ILSVRC2012_val_00017070.JPEG +ILSVRC2012_val_00037577.JPEG +ILSVRC2012_val_00005862.JPEG +ILSVRC2012_val_00030483.JPEG +ILSVRC2012_val_00012921.JPEG +ILSVRC2012_val_00038308.JPEG +ILSVRC2012_val_00021110.JPEG +ILSVRC2012_val_00022280.JPEG +ILSVRC2012_val_00038074.JPEG +ILSVRC2012_val_00012038.JPEG +ILSVRC2012_val_00026054.JPEG +ILSVRC2012_val_00010289.JPEG +ILSVRC2012_val_00039273.JPEG +ILSVRC2012_val_00007280.JPEG +ILSVRC2012_val_00007907.JPEG +ILSVRC2012_val_00047924.JPEG +ILSVRC2012_val_00029219.JPEG +ILSVRC2012_val_00027075.JPEG +ILSVRC2012_val_00029370.JPEG +ILSVRC2012_val_00046916.JPEG +ILSVRC2012_val_00019353.JPEG +ILSVRC2012_val_00027316.JPEG +ILSVRC2012_val_00001478.JPEG +ILSVRC2012_val_00010743.JPEG +ILSVRC2012_val_00035904.JPEG +ILSVRC2012_val_00009526.JPEG +ILSVRC2012_val_00049213.JPEG +ILSVRC2012_val_00014884.JPEG +ILSVRC2012_val_00010182.JPEG +ILSVRC2012_val_00029679.JPEG +ILSVRC2012_val_00033177.JPEG +ILSVRC2012_val_00022350.JPEG +ILSVRC2012_val_00040667.JPEG +ILSVRC2012_val_00045476.JPEG +ILSVRC2012_val_00016793.JPEG +ILSVRC2012_val_00043880.JPEG +ILSVRC2012_val_00037562.JPEG +ILSVRC2012_val_00045267.JPEG +ILSVRC2012_val_00043714.JPEG +ILSVRC2012_val_00006534.JPEG +ILSVRC2012_val_00016171.JPEG +ILSVRC2012_val_00035693.JPEG +ILSVRC2012_val_00040010.JPEG +ILSVRC2012_val_00002466.JPEG +ILSVRC2012_val_00036176.JPEG +ILSVRC2012_val_00040031.JPEG +ILSVRC2012_val_00002916.JPEG +ILSVRC2012_val_00045910.JPEG +ILSVRC2012_val_00025480.JPEG +ILSVRC2012_val_00047551.JPEG +ILSVRC2012_val_00027461.JPEG +ILSVRC2012_val_00038737.JPEG +ILSVRC2012_val_00049361.JPEG +ILSVRC2012_val_00026019.JPEG +ILSVRC2012_val_00026650.JPEG +ILSVRC2012_val_00045950.JPEG +ILSVRC2012_val_00023893.JPEG +ILSVRC2012_val_00025401.JPEG +ILSVRC2012_val_00047339.JPEG +ILSVRC2012_val_00021419.JPEG +ILSVRC2012_val_00019877.JPEG +ILSVRC2012_val_00019919.JPEG +ILSVRC2012_val_00022965.JPEG +ILSVRC2012_val_00029112.JPEG +ILSVRC2012_val_00019201.JPEG +ILSVRC2012_val_00036554.JPEG +ILSVRC2012_val_00026883.JPEG +ILSVRC2012_val_00001607.JPEG +ILSVRC2012_val_00025758.JPEG +ILSVRC2012_val_00014742.JPEG +ILSVRC2012_val_00012660.JPEG +ILSVRC2012_val_00045264.JPEG +ILSVRC2012_val_00013859.JPEG +ILSVRC2012_val_00039053.JPEG +ILSVRC2012_val_00017397.JPEG +ILSVRC2012_val_00001757.JPEG +ILSVRC2012_val_00010218.JPEG +ILSVRC2012_val_00033092.JPEG +ILSVRC2012_val_00004874.JPEG +ILSVRC2012_val_00039156.JPEG +ILSVRC2012_val_00040207.JPEG +ILSVRC2012_val_00039267.JPEG +ILSVRC2012_val_00000720.JPEG +ILSVRC2012_val_00000551.JPEG +ILSVRC2012_val_00006291.JPEG +ILSVRC2012_val_00021894.JPEG +ILSVRC2012_val_00030125.JPEG +ILSVRC2012_val_00003860.JPEG +ILSVRC2012_val_00028883.JPEG +ILSVRC2012_val_00041331.JPEG +ILSVRC2012_val_00034495.JPEG +ILSVRC2012_val_00046778.JPEG +ILSVRC2012_val_00020374.JPEG +ILSVRC2012_val_00039110.JPEG +ILSVRC2012_val_00012537.JPEG +ILSVRC2012_val_00025400.JPEG +ILSVRC2012_val_00005037.JPEG +ILSVRC2012_val_00035958.JPEG +ILSVRC2012_val_00037035.JPEG +ILSVRC2012_val_00036779.JPEG +ILSVRC2012_val_00013777.JPEG +ILSVRC2012_val_00049880.JPEG +ILSVRC2012_val_00040228.JPEG +ILSVRC2012_val_00020453.JPEG +ILSVRC2012_val_00027223.JPEG +ILSVRC2012_val_00025890.JPEG +ILSVRC2012_val_00026552.JPEG +ILSVRC2012_val_00031435.JPEG +ILSVRC2012_val_00037895.JPEG +ILSVRC2012_val_00045272.JPEG +ILSVRC2012_val_00030549.JPEG +ILSVRC2012_val_00033015.JPEG +ILSVRC2012_val_00010567.JPEG +ILSVRC2012_val_00009390.JPEG +ILSVRC2012_val_00026309.JPEG +ILSVRC2012_val_00005315.JPEG +ILSVRC2012_val_00027421.JPEG +ILSVRC2012_val_00006401.JPEG +ILSVRC2012_val_00039771.JPEG +ILSVRC2012_val_00001281.JPEG +ILSVRC2012_val_00033007.JPEG +ILSVRC2012_val_00002534.JPEG +ILSVRC2012_val_00013566.JPEG +ILSVRC2012_val_00044082.JPEG +ILSVRC2012_val_00021031.JPEG +ILSVRC2012_val_00021806.JPEG +ILSVRC2012_val_00006411.JPEG +ILSVRC2012_val_00025923.JPEG +ILSVRC2012_val_00041258.JPEG +ILSVRC2012_val_00036558.JPEG +ILSVRC2012_val_00012661.JPEG +ILSVRC2012_val_00017350.JPEG +ILSVRC2012_val_00043580.JPEG +ILSVRC2012_val_00017933.JPEG +ILSVRC2012_val_00029903.JPEG +ILSVRC2012_val_00012530.JPEG +ILSVRC2012_val_00006781.JPEG +ILSVRC2012_val_00038516.JPEG +ILSVRC2012_val_00041992.JPEG +ILSVRC2012_val_00000594.JPEG +ILSVRC2012_val_00049079.JPEG +ILSVRC2012_val_00008421.JPEG +ILSVRC2012_val_00030667.JPEG +ILSVRC2012_val_00038688.JPEG +ILSVRC2012_val_00043084.JPEG +ILSVRC2012_val_00020813.JPEG +ILSVRC2012_val_00040325.JPEG +ILSVRC2012_val_00005966.JPEG +ILSVRC2012_val_00017505.JPEG +ILSVRC2012_val_00022558.JPEG +ILSVRC2012_val_00041355.JPEG +ILSVRC2012_val_00004531.JPEG +ILSVRC2012_val_00047320.JPEG +ILSVRC2012_val_00046089.JPEG +ILSVRC2012_val_00037709.JPEG +ILSVRC2012_val_00002583.JPEG +ILSVRC2012_val_00020620.JPEG +ILSVRC2012_val_00026128.JPEG +ILSVRC2012_val_00008415.JPEG +ILSVRC2012_val_00030194.JPEG +ILSVRC2012_val_00032709.JPEG +ILSVRC2012_val_00008418.JPEG +ILSVRC2012_val_00043840.JPEG +ILSVRC2012_val_00042443.JPEG +ILSVRC2012_val_00046549.JPEG +ILSVRC2012_val_00042280.JPEG +ILSVRC2012_val_00043235.JPEG +ILSVRC2012_val_00020707.JPEG +ILSVRC2012_val_00026248.JPEG +ILSVRC2012_val_00034379.JPEG +ILSVRC2012_val_00019165.JPEG +ILSVRC2012_val_00038083.JPEG +ILSVRC2012_val_00002654.JPEG +ILSVRC2012_val_00007448.JPEG +ILSVRC2012_val_00043743.JPEG +ILSVRC2012_val_00013160.JPEG +ILSVRC2012_val_00043012.JPEG +ILSVRC2012_val_00019424.JPEG +ILSVRC2012_val_00008666.JPEG +ILSVRC2012_val_00016098.JPEG +ILSVRC2012_val_00031671.JPEG +ILSVRC2012_val_00004675.JPEG +ILSVRC2012_val_00003936.JPEG +ILSVRC2012_val_00000194.JPEG +ILSVRC2012_val_00044702.JPEG +ILSVRC2012_val_00005834.JPEG +ILSVRC2012_val_00024007.JPEG +ILSVRC2012_val_00025213.JPEG +ILSVRC2012_val_00021774.JPEG +ILSVRC2012_val_00036894.JPEG +ILSVRC2012_val_00008825.JPEG +ILSVRC2012_val_00004879.JPEG +ILSVRC2012_val_00016237.JPEG +ILSVRC2012_val_00034479.JPEG +ILSVRC2012_val_00016665.JPEG +ILSVRC2012_val_00049677.JPEG +ILSVRC2012_val_00032674.JPEG +ILSVRC2012_val_00003794.JPEG +ILSVRC2012_val_00038431.JPEG +ILSVRC2012_val_00034188.JPEG +ILSVRC2012_val_00039175.JPEG +ILSVRC2012_val_00016675.JPEG +ILSVRC2012_val_00017888.JPEG +ILSVRC2012_val_00004296.JPEG +ILSVRC2012_val_00003572.JPEG +ILSVRC2012_val_00046960.JPEG +ILSVRC2012_val_00006079.JPEG +ILSVRC2012_val_00047639.JPEG +ILSVRC2012_val_00030336.JPEG +ILSVRC2012_val_00034735.JPEG +ILSVRC2012_val_00021144.JPEG +ILSVRC2012_val_00010940.JPEG +ILSVRC2012_val_00029580.JPEG +ILSVRC2012_val_00004398.JPEG +ILSVRC2012_val_00002347.JPEG +ILSVRC2012_val_00014998.JPEG +ILSVRC2012_val_00010066.JPEG +ILSVRC2012_val_00033055.JPEG +ILSVRC2012_val_00001678.JPEG +ILSVRC2012_val_00024860.JPEG +ILSVRC2012_val_00046183.JPEG +ILSVRC2012_val_00026106.JPEG +ILSVRC2012_val_00015582.JPEG +ILSVRC2012_val_00000621.JPEG +ILSVRC2012_val_00015928.JPEG +ILSVRC2012_val_00014060.JPEG +ILSVRC2012_val_00024950.JPEG +ILSVRC2012_val_00048990.JPEG +ILSVRC2012_val_00026611.JPEG +ILSVRC2012_val_00003260.JPEG +ILSVRC2012_val_00013866.JPEG +ILSVRC2012_val_00006038.JPEG +ILSVRC2012_val_00013515.JPEG +ILSVRC2012_val_00045316.JPEG +ILSVRC2012_val_00003838.JPEG +ILSVRC2012_val_00002834.JPEG +ILSVRC2012_val_00025847.JPEG +ILSVRC2012_val_00042694.JPEG +ILSVRC2012_val_00003518.JPEG +ILSVRC2012_val_00019492.JPEG +ILSVRC2012_val_00009006.JPEG +ILSVRC2012_val_00032160.JPEG +ILSVRC2012_val_00010635.JPEG +ILSVRC2012_val_00017401.JPEG +ILSVRC2012_val_00008028.JPEG +ILSVRC2012_val_00038388.JPEG +ILSVRC2012_val_00000671.JPEG +ILSVRC2012_val_00009414.JPEG +ILSVRC2012_val_00046345.JPEG +ILSVRC2012_val_00048304.JPEG +ILSVRC2012_val_00023359.JPEG +ILSVRC2012_val_00025655.JPEG +ILSVRC2012_val_00014383.JPEG +ILSVRC2012_val_00015905.JPEG +ILSVRC2012_val_00046779.JPEG +ILSVRC2012_val_00022320.JPEG +ILSVRC2012_val_00031960.JPEG +ILSVRC2012_val_00016472.JPEG +ILSVRC2012_val_00042744.JPEG +ILSVRC2012_val_00035859.JPEG +ILSVRC2012_val_00033921.JPEG +ILSVRC2012_val_00009012.JPEG +ILSVRC2012_val_00037473.JPEG +ILSVRC2012_val_00032533.JPEG +ILSVRC2012_val_00030674.JPEG +ILSVRC2012_val_00034402.JPEG +ILSVRC2012_val_00011109.JPEG +ILSVRC2012_val_00028035.JPEG +ILSVRC2012_val_00017985.JPEG +ILSVRC2012_val_00017906.JPEG +ILSVRC2012_val_00026599.JPEG +ILSVRC2012_val_00017673.JPEG +ILSVRC2012_val_00044336.JPEG +ILSVRC2012_val_00013324.JPEG +ILSVRC2012_val_00005466.JPEG +ILSVRC2012_val_00047994.JPEG +ILSVRC2012_val_00047568.JPEG +ILSVRC2012_val_00037362.JPEG +ILSVRC2012_val_00038652.JPEG +ILSVRC2012_val_00040387.JPEG +ILSVRC2012_val_00046350.JPEG +ILSVRC2012_val_00027722.JPEG +ILSVRC2012_val_00048786.JPEG +ILSVRC2012_val_00045722.JPEG +ILSVRC2012_val_00047577.JPEG +ILSVRC2012_val_00049085.JPEG +ILSVRC2012_val_00035252.JPEG +ILSVRC2012_val_00030166.JPEG +ILSVRC2012_val_00026399.JPEG +ILSVRC2012_val_00006970.JPEG +ILSVRC2012_val_00026914.JPEG +ILSVRC2012_val_00007690.JPEG +ILSVRC2012_val_00045050.JPEG +ILSVRC2012_val_00043799.JPEG +ILSVRC2012_val_00042292.JPEG +ILSVRC2012_val_00045218.JPEG +ILSVRC2012_val_00023575.JPEG +ILSVRC2012_val_00035035.JPEG +ILSVRC2012_val_00022705.JPEG +ILSVRC2012_val_00028152.JPEG +ILSVRC2012_val_00042033.JPEG +ILSVRC2012_val_00016307.JPEG +ILSVRC2012_val_00028725.JPEG +ILSVRC2012_val_00032614.JPEG +ILSVRC2012_val_00017839.JPEG +ILSVRC2012_val_00041344.JPEG +ILSVRC2012_val_00012246.JPEG +ILSVRC2012_val_00046579.JPEG +ILSVRC2012_val_00014895.JPEG +ILSVRC2012_val_00026676.JPEG +ILSVRC2012_val_00038291.JPEG +ILSVRC2012_val_00023189.JPEG +ILSVRC2012_val_00001454.JPEG +ILSVRC2012_val_00000276.JPEG +ILSVRC2012_val_00047684.JPEG +ILSVRC2012_val_00046599.JPEG +ILSVRC2012_val_00017271.JPEG +ILSVRC2012_val_00030931.JPEG +ILSVRC2012_val_00013587.JPEG +ILSVRC2012_val_00031797.JPEG +ILSVRC2012_val_00028017.JPEG +ILSVRC2012_val_00008566.JPEG +ILSVRC2012_val_00040621.JPEG +ILSVRC2012_val_00001665.JPEG +ILSVRC2012_val_00030461.JPEG +ILSVRC2012_val_00027077.JPEG +ILSVRC2012_val_00039362.JPEG +ILSVRC2012_val_00011856.JPEG +ILSVRC2012_val_00042968.JPEG +ILSVRC2012_val_00038675.JPEG +ILSVRC2012_val_00039919.JPEG +ILSVRC2012_val_00015036.JPEG +ILSVRC2012_val_00012250.JPEG +ILSVRC2012_val_00018173.JPEG +ILSVRC2012_val_00006377.JPEG +ILSVRC2012_val_00047209.JPEG +ILSVRC2012_val_00043311.JPEG +ILSVRC2012_val_00028927.JPEG +ILSVRC2012_val_00032962.JPEG +ILSVRC2012_val_00035117.JPEG +ILSVRC2012_val_00041535.JPEG +ILSVRC2012_val_00045592.JPEG +ILSVRC2012_val_00003652.JPEG +ILSVRC2012_val_00044652.JPEG +ILSVRC2012_val_00037758.JPEG +ILSVRC2012_val_00020475.JPEG +ILSVRC2012_val_00017373.JPEG +ILSVRC2012_val_00031775.JPEG +ILSVRC2012_val_00005359.JPEG +ILSVRC2012_val_00032372.JPEG +ILSVRC2012_val_00000255.JPEG +ILSVRC2012_val_00007995.JPEG +ILSVRC2012_val_00035178.JPEG +ILSVRC2012_val_00018053.JPEG +ILSVRC2012_val_00043328.JPEG +ILSVRC2012_val_00042974.JPEG +ILSVRC2012_val_00024739.JPEG +ILSVRC2012_val_00047100.JPEG +ILSVRC2012_val_00027687.JPEG +ILSVRC2012_val_00042975.JPEG +ILSVRC2012_val_00049038.JPEG +ILSVRC2012_val_00048835.JPEG +ILSVRC2012_val_00011798.JPEG +ILSVRC2012_val_00005218.JPEG +ILSVRC2012_val_00039206.JPEG +ILSVRC2012_val_00021661.JPEG +ILSVRC2012_val_00010998.JPEG +ILSVRC2012_val_00010424.JPEG +ILSVRC2012_val_00027914.JPEG +ILSVRC2012_val_00035868.JPEG +ILSVRC2012_val_00030234.JPEG +ILSVRC2012_val_00007525.JPEG +ILSVRC2012_val_00039990.JPEG +ILSVRC2012_val_00019432.JPEG +ILSVRC2012_val_00011809.JPEG +ILSVRC2012_val_00002160.JPEG +ILSVRC2012_val_00011418.JPEG +ILSVRC2012_val_00011183.JPEG +ILSVRC2012_val_00028361.JPEG +ILSVRC2012_val_00047709.JPEG +ILSVRC2012_val_00027364.JPEG +ILSVRC2012_val_00041402.JPEG +ILSVRC2012_val_00040146.JPEG +ILSVRC2012_val_00004000.JPEG +ILSVRC2012_val_00021243.JPEG +ILSVRC2012_val_00047708.JPEG +ILSVRC2012_val_00029821.JPEG +ILSVRC2012_val_00043829.JPEG +ILSVRC2012_val_00049097.JPEG +ILSVRC2012_val_00048374.JPEG +ILSVRC2012_val_00040282.JPEG +ILSVRC2012_val_00018394.JPEG +ILSVRC2012_val_00017128.JPEG +ILSVRC2012_val_00032314.JPEG +ILSVRC2012_val_00046643.JPEG +ILSVRC2012_val_00000213.JPEG +ILSVRC2012_val_00020659.JPEG +ILSVRC2012_val_00048877.JPEG +ILSVRC2012_val_00032803.JPEG +ILSVRC2012_val_00036038.JPEG +ILSVRC2012_val_00035358.JPEG +ILSVRC2012_val_00023956.JPEG +ILSVRC2012_val_00010758.JPEG +ILSVRC2012_val_00045388.JPEG +ILSVRC2012_val_00032973.JPEG +ILSVRC2012_val_00014507.JPEG +ILSVRC2012_val_00035662.JPEG +ILSVRC2012_val_00012136.JPEG +ILSVRC2012_val_00006427.JPEG +ILSVRC2012_val_00005442.JPEG +ILSVRC2012_val_00022651.JPEG +ILSVRC2012_val_00018242.JPEG +ILSVRC2012_val_00012774.JPEG +ILSVRC2012_val_00045432.JPEG +ILSVRC2012_val_00001553.JPEG +ILSVRC2012_val_00034265.JPEG +ILSVRC2012_val_00032038.JPEG +ILSVRC2012_val_00033245.JPEG +ILSVRC2012_val_00009083.JPEG +ILSVRC2012_val_00018547.JPEG +ILSVRC2012_val_00046571.JPEG +ILSVRC2012_val_00012048.JPEG +ILSVRC2012_val_00000564.JPEG +ILSVRC2012_val_00026113.JPEG +ILSVRC2012_val_00043243.JPEG +ILSVRC2012_val_00004632.JPEG +ILSVRC2012_val_00017354.JPEG +ILSVRC2012_val_00044335.JPEG +ILSVRC2012_val_00006712.JPEG +ILSVRC2012_val_00017983.JPEG +ILSVRC2012_val_00021446.JPEG +ILSVRC2012_val_00001672.JPEG +ILSVRC2012_val_00044417.JPEG +ILSVRC2012_val_00009933.JPEG +ILSVRC2012_val_00021202.JPEG +ILSVRC2012_val_00003691.JPEG +ILSVRC2012_val_00018102.JPEG +ILSVRC2012_val_00022554.JPEG +ILSVRC2012_val_00029446.JPEG +ILSVRC2012_val_00004769.JPEG +ILSVRC2012_val_00027482.JPEG +ILSVRC2012_val_00015427.JPEG +ILSVRC2012_val_00029045.JPEG +ILSVRC2012_val_00014228.JPEG +ILSVRC2012_val_00014591.JPEG +ILSVRC2012_val_00036717.JPEG +ILSVRC2012_val_00000439.JPEG +ILSVRC2012_val_00025109.JPEG +ILSVRC2012_val_00044018.JPEG +ILSVRC2012_val_00020427.JPEG +ILSVRC2012_val_00037835.JPEG +ILSVRC2012_val_00031746.JPEG +ILSVRC2012_val_00014933.JPEG +ILSVRC2012_val_00045707.JPEG +ILSVRC2012_val_00002314.JPEG +ILSVRC2012_val_00041680.JPEG +ILSVRC2012_val_00013424.JPEG +ILSVRC2012_val_00025297.JPEG +ILSVRC2012_val_00013611.JPEG +ILSVRC2012_val_00002789.JPEG +ILSVRC2012_val_00041296.JPEG +ILSVRC2012_val_00040625.JPEG +ILSVRC2012_val_00048667.JPEG +ILSVRC2012_val_00009279.JPEG +ILSVRC2012_val_00032993.JPEG +ILSVRC2012_val_00004567.JPEG +ILSVRC2012_val_00026273.JPEG +ILSVRC2012_val_00017049.JPEG +ILSVRC2012_val_00023833.JPEG +ILSVRC2012_val_00001574.JPEG +ILSVRC2012_val_00005337.JPEG +ILSVRC2012_val_00015965.JPEG +ILSVRC2012_val_00043666.JPEG +ILSVRC2012_val_00040427.JPEG +ILSVRC2012_val_00006045.JPEG +ILSVRC2012_val_00003892.JPEG +ILSVRC2012_val_00027862.JPEG +ILSVRC2012_val_00049596.JPEG +ILSVRC2012_val_00027869.JPEG +ILSVRC2012_val_00013179.JPEG +ILSVRC2012_val_00032104.JPEG +ILSVRC2012_val_00022515.JPEG +ILSVRC2012_val_00044784.JPEG +ILSVRC2012_val_00030733.JPEG +ILSVRC2012_val_00040417.JPEG +ILSVRC2012_val_00033688.JPEG +ILSVRC2012_val_00006239.JPEG +ILSVRC2012_val_00001064.JPEG +ILSVRC2012_val_00034682.JPEG +ILSVRC2012_val_00005867.JPEG +ILSVRC2012_val_00026177.JPEG +ILSVRC2012_val_00040728.JPEG +ILSVRC2012_val_00037630.JPEG +ILSVRC2012_val_00035657.JPEG +ILSVRC2012_val_00006187.JPEG +ILSVRC2012_val_00024190.JPEG +ILSVRC2012_val_00031854.JPEG +ILSVRC2012_val_00004918.JPEG +ILSVRC2012_val_00016689.JPEG +ILSVRC2012_val_00002844.JPEG +ILSVRC2012_val_00007431.JPEG +ILSVRC2012_val_00038746.JPEG +ILSVRC2012_val_00046715.JPEG +ILSVRC2012_val_00016759.JPEG +ILSVRC2012_val_00015565.JPEG +ILSVRC2012_val_00041383.JPEG +ILSVRC2012_val_00036350.JPEG +ILSVRC2012_val_00034106.JPEG +ILSVRC2012_val_00000289.JPEG +ILSVRC2012_val_00017644.JPEG +ILSVRC2012_val_00025037.JPEG +ILSVRC2012_val_00022503.JPEG +ILSVRC2012_val_00004060.JPEG +ILSVRC2012_val_00006701.JPEG +ILSVRC2012_val_00009471.JPEG +ILSVRC2012_val_00031118.JPEG +ILSVRC2012_val_00009752.JPEG +ILSVRC2012_val_00049992.JPEG +ILSVRC2012_val_00015811.JPEG +ILSVRC2012_val_00034812.JPEG +ILSVRC2012_val_00004392.JPEG +ILSVRC2012_val_00005790.JPEG +ILSVRC2012_val_00040407.JPEG +ILSVRC2012_val_00038068.JPEG +ILSVRC2012_val_00015633.JPEG +ILSVRC2012_val_00048827.JPEG +ILSVRC2012_val_00033671.JPEG +ILSVRC2012_val_00010174.JPEG +ILSVRC2012_val_00001016.JPEG +ILSVRC2012_val_00016135.JPEG +ILSVRC2012_val_00044371.JPEG +ILSVRC2012_val_00022935.JPEG +ILSVRC2012_val_00013595.JPEG +ILSVRC2012_val_00047676.JPEG +ILSVRC2012_val_00014916.JPEG +ILSVRC2012_val_00004871.JPEG +ILSVRC2012_val_00049139.JPEG +ILSVRC2012_val_00001752.JPEG +ILSVRC2012_val_00013028.JPEG +ILSVRC2012_val_00021341.JPEG +ILSVRC2012_val_00027188.JPEG +ILSVRC2012_val_00038999.JPEG +ILSVRC2012_val_00039718.JPEG +ILSVRC2012_val_00025069.JPEG +ILSVRC2012_val_00049197.JPEG +ILSVRC2012_val_00040988.JPEG +ILSVRC2012_val_00035813.JPEG +ILSVRC2012_val_00048531.JPEG +ILSVRC2012_val_00035745.JPEG +ILSVRC2012_val_00009786.JPEG +ILSVRC2012_val_00043052.JPEG +ILSVRC2012_val_00004040.JPEG +ILSVRC2012_val_00033388.JPEG +ILSVRC2012_val_00042753.JPEG +ILSVRC2012_val_00036285.JPEG +ILSVRC2012_val_00004709.JPEG +ILSVRC2012_val_00026637.JPEG +ILSVRC2012_val_00025751.JPEG +ILSVRC2012_val_00023488.JPEG +ILSVRC2012_val_00004532.JPEG +ILSVRC2012_val_00042832.JPEG +ILSVRC2012_val_00031785.JPEG +ILSVRC2012_val_00022226.JPEG +ILSVRC2012_val_00001518.JPEG +ILSVRC2012_val_00029511.JPEG +ILSVRC2012_val_00016597.JPEG +ILSVRC2012_val_00006559.JPEG +ILSVRC2012_val_00008652.JPEG +ILSVRC2012_val_00014625.JPEG +ILSVRC2012_val_00001658.JPEG +ILSVRC2012_val_00021524.JPEG +ILSVRC2012_val_00035343.JPEG +ILSVRC2012_val_00038306.JPEG +ILSVRC2012_val_00013614.JPEG +ILSVRC2012_val_00003990.JPEG +ILSVRC2012_val_00020416.JPEG +ILSVRC2012_val_00006988.JPEG +ILSVRC2012_val_00043772.JPEG +ILSVRC2012_val_00022426.JPEG +ILSVRC2012_val_00004677.JPEG +ILSVRC2012_val_00003683.JPEG +ILSVRC2012_val_00019070.JPEG +ILSVRC2012_val_00043191.JPEG +ILSVRC2012_val_00039084.JPEG +ILSVRC2012_val_00037669.JPEG +ILSVRC2012_val_00030000.JPEG +ILSVRC2012_val_00006381.JPEG +ILSVRC2012_val_00007617.JPEG +ILSVRC2012_val_00026265.JPEG +ILSVRC2012_val_00044063.JPEG +ILSVRC2012_val_00037232.JPEG +ILSVRC2012_val_00040825.JPEG +ILSVRC2012_val_00007332.JPEG +ILSVRC2012_val_00027074.JPEG +ILSVRC2012_val_00027254.JPEG +ILSVRC2012_val_00002564.JPEG +ILSVRC2012_val_00018788.JPEG +ILSVRC2012_val_00037879.JPEG +ILSVRC2012_val_00033279.JPEG +ILSVRC2012_val_00037225.JPEG +ILSVRC2012_val_00045702.JPEG +ILSVRC2012_val_00014716.JPEG +ILSVRC2012_val_00027125.JPEG +ILSVRC2012_val_00041641.JPEG +ILSVRC2012_val_00030083.JPEG +ILSVRC2012_val_00029526.JPEG +ILSVRC2012_val_00026510.JPEG +ILSVRC2012_val_00041594.JPEG +ILSVRC2012_val_00048818.JPEG +ILSVRC2012_val_00025782.JPEG +ILSVRC2012_val_00011782.JPEG +ILSVRC2012_val_00014813.JPEG +ILSVRC2012_val_00028978.JPEG +ILSVRC2012_val_00008749.JPEG +ILSVRC2012_val_00005626.JPEG +ILSVRC2012_val_00049711.JPEG +ILSVRC2012_val_00035437.JPEG +ILSVRC2012_val_00013014.JPEG +ILSVRC2012_val_00045195.JPEG +ILSVRC2012_val_00017910.JPEG +ILSVRC2012_val_00033312.JPEG +ILSVRC2012_val_00034998.JPEG +ILSVRC2012_val_00021047.JPEG +ILSVRC2012_val_00022800.JPEG +ILSVRC2012_val_00025860.JPEG +ILSVRC2012_val_00014871.JPEG +ILSVRC2012_val_00042687.JPEG +ILSVRC2012_val_00002551.JPEG +ILSVRC2012_val_00035589.JPEG +ILSVRC2012_val_00043424.JPEG +ILSVRC2012_val_00016795.JPEG +ILSVRC2012_val_00025180.JPEG +ILSVRC2012_val_00020809.JPEG +ILSVRC2012_val_00014211.JPEG +ILSVRC2012_val_00035564.JPEG +ILSVRC2012_val_00019911.JPEG +ILSVRC2012_val_00009397.JPEG +ILSVRC2012_val_00037751.JPEG +ILSVRC2012_val_00024839.JPEG +ILSVRC2012_val_00008287.JPEG +ILSVRC2012_val_00020500.JPEG +ILSVRC2012_val_00014904.JPEG +ILSVRC2012_val_00043034.JPEG +ILSVRC2012_val_00027911.JPEG +ILSVRC2012_val_00019810.JPEG +ILSVRC2012_val_00026927.JPEG +ILSVRC2012_val_00023173.JPEG +ILSVRC2012_val_00037953.JPEG +ILSVRC2012_val_00025347.JPEG +ILSVRC2012_val_00003298.JPEG +ILSVRC2012_val_00033487.JPEG +ILSVRC2012_val_00033523.JPEG +ILSVRC2012_val_00047736.JPEG +ILSVRC2012_val_00020277.JPEG +ILSVRC2012_val_00041902.JPEG +ILSVRC2012_val_00017251.JPEG +ILSVRC2012_val_00002990.JPEG +ILSVRC2012_val_00043495.JPEG +ILSVRC2012_val_00045231.JPEG +ILSVRC2012_val_00034918.JPEG +ILSVRC2012_val_00038915.JPEG +ILSVRC2012_val_00019257.JPEG +ILSVRC2012_val_00020679.JPEG +ILSVRC2012_val_00003148.JPEG +ILSVRC2012_val_00012503.JPEG +ILSVRC2012_val_00023985.JPEG +ILSVRC2012_val_00020201.JPEG +ILSVRC2012_val_00016819.JPEG +ILSVRC2012_val_00007355.JPEG +ILSVRC2012_val_00024212.JPEG +ILSVRC2012_val_00005145.JPEG +ILSVRC2012_val_00036052.JPEG +ILSVRC2012_val_00048624.JPEG +ILSVRC2012_val_00049889.JPEG +ILSVRC2012_val_00039604.JPEG +ILSVRC2012_val_00032448.JPEG +ILSVRC2012_val_00039880.JPEG +ILSVRC2012_val_00001248.JPEG +ILSVRC2012_val_00023485.JPEG +ILSVRC2012_val_00039962.JPEG +ILSVRC2012_val_00010526.JPEG +ILSVRC2012_val_00025127.JPEG +ILSVRC2012_val_00018624.JPEG +ILSVRC2012_val_00001254.JPEG +ILSVRC2012_val_00013662.JPEG +ILSVRC2012_val_00021299.JPEG +ILSVRC2012_val_00048360.JPEG +ILSVRC2012_val_00007406.JPEG +ILSVRC2012_val_00001622.JPEG +ILSVRC2012_val_00001006.JPEG +ILSVRC2012_val_00019881.JPEG +ILSVRC2012_val_00038584.JPEG +ILSVRC2012_val_00044638.JPEG +ILSVRC2012_val_00017153.JPEG +ILSVRC2012_val_00044671.JPEG +ILSVRC2012_val_00039900.JPEG +ILSVRC2012_val_00020063.JPEG +ILSVRC2012_val_00021454.JPEG +ILSVRC2012_val_00033888.JPEG +ILSVRC2012_val_00018879.JPEG +ILSVRC2012_val_00030299.JPEG +ILSVRC2012_val_00008959.JPEG +ILSVRC2012_val_00005719.JPEG +ILSVRC2012_val_00030348.JPEG +ILSVRC2012_val_00023104.JPEG +ILSVRC2012_val_00036294.JPEG +ILSVRC2012_val_00049163.JPEG +ILSVRC2012_val_00013893.JPEG +ILSVRC2012_val_00035067.JPEG +ILSVRC2012_val_00026058.JPEG +ILSVRC2012_val_00048042.JPEG +ILSVRC2012_val_00010671.JPEG +ILSVRC2012_val_00040704.JPEG +ILSVRC2012_val_00039148.JPEG +ILSVRC2012_val_00012153.JPEG +ILSVRC2012_val_00045698.JPEG +ILSVRC2012_val_00028805.JPEG +ILSVRC2012_val_00042067.JPEG +ILSVRC2012_val_00005502.JPEG +ILSVRC2012_val_00001292.JPEG +ILSVRC2012_val_00039874.JPEG +ILSVRC2012_val_00015306.JPEG +ILSVRC2012_val_00042062.JPEG +ILSVRC2012_val_00045812.JPEG +ILSVRC2012_val_00042545.JPEG +ILSVRC2012_val_00021072.JPEG +ILSVRC2012_val_00030936.JPEG +ILSVRC2012_val_00031038.JPEG +ILSVRC2012_val_00035083.JPEG +ILSVRC2012_val_00040079.JPEG +ILSVRC2012_val_00035612.JPEG +ILSVRC2012_val_00047826.JPEG +ILSVRC2012_val_00029259.JPEG +ILSVRC2012_val_00030367.JPEG +ILSVRC2012_val_00032702.JPEG +ILSVRC2012_val_00011919.JPEG +ILSVRC2012_val_00020365.JPEG +ILSVRC2012_val_00047314.JPEG +ILSVRC2012_val_00029545.JPEG +ILSVRC2012_val_00022976.JPEG +ILSVRC2012_val_00009341.JPEG +ILSVRC2012_val_00000031.JPEG +ILSVRC2012_val_00048868.JPEG +ILSVRC2012_val_00025348.JPEG +ILSVRC2012_val_00018848.JPEG +ILSVRC2012_val_00000809.JPEG +ILSVRC2012_val_00013865.JPEG +ILSVRC2012_val_00042191.JPEG +ILSVRC2012_val_00036501.JPEG +ILSVRC2012_val_00025242.JPEG +ILSVRC2012_val_00044006.JPEG +ILSVRC2012_val_00046363.JPEG +ILSVRC2012_val_00042846.JPEG +ILSVRC2012_val_00030179.JPEG +ILSVRC2012_val_00016681.JPEG +ILSVRC2012_val_00045735.JPEG +ILSVRC2012_val_00013346.JPEG +ILSVRC2012_val_00005203.JPEG +ILSVRC2012_val_00036526.JPEG +ILSVRC2012_val_00013002.JPEG +ILSVRC2012_val_00049870.JPEG +ILSVRC2012_val_00039116.JPEG +ILSVRC2012_val_00022195.JPEG +ILSVRC2012_val_00039196.JPEG +ILSVRC2012_val_00009332.JPEG +ILSVRC2012_val_00031190.JPEG +ILSVRC2012_val_00002563.JPEG +ILSVRC2012_val_00003350.JPEG +ILSVRC2012_val_00014301.JPEG +ILSVRC2012_val_00012375.JPEG +ILSVRC2012_val_00021483.JPEG +ILSVRC2012_val_00007112.JPEG +ILSVRC2012_val_00019507.JPEG +ILSVRC2012_val_00046217.JPEG +ILSVRC2012_val_00044860.JPEG +ILSVRC2012_val_00015141.JPEG +ILSVRC2012_val_00042923.JPEG +ILSVRC2012_val_00027931.JPEG +ILSVRC2012_val_00031113.JPEG +ILSVRC2012_val_00042885.JPEG +ILSVRC2012_val_00001587.JPEG +ILSVRC2012_val_00035216.JPEG +ILSVRC2012_val_00020848.JPEG +ILSVRC2012_val_00044241.JPEG +ILSVRC2012_val_00035183.JPEG +ILSVRC2012_val_00028059.JPEG +ILSVRC2012_val_00025218.JPEG +ILSVRC2012_val_00016335.JPEG +ILSVRC2012_val_00027891.JPEG +ILSVRC2012_val_00012535.JPEG +ILSVRC2012_val_00024276.JPEG +ILSVRC2012_val_00029574.JPEG +ILSVRC2012_val_00033666.JPEG +ILSVRC2012_val_00030978.JPEG +ILSVRC2012_val_00035435.JPEG +ILSVRC2012_val_00001985.JPEG +ILSVRC2012_val_00043233.JPEG +ILSVRC2012_val_00048478.JPEG +ILSVRC2012_val_00018383.JPEG +ILSVRC2012_val_00038768.JPEG +ILSVRC2012_val_00031880.JPEG +ILSVRC2012_val_00027728.JPEG +ILSVRC2012_val_00000706.JPEG +ILSVRC2012_val_00047914.JPEG +ILSVRC2012_val_00005864.JPEG +ILSVRC2012_val_00029781.JPEG +ILSVRC2012_val_00023213.JPEG +ILSVRC2012_val_00027001.JPEG +ILSVRC2012_val_00003797.JPEG +ILSVRC2012_val_00033681.JPEG +ILSVRC2012_val_00030162.JPEG +ILSVRC2012_val_00001833.JPEG +ILSVRC2012_val_00002035.JPEG +ILSVRC2012_val_00009521.JPEG +ILSVRC2012_val_00044288.JPEG +ILSVRC2012_val_00017166.JPEG +ILSVRC2012_val_00038442.JPEG +ILSVRC2012_val_00037582.JPEG +ILSVRC2012_val_00009754.JPEG +ILSVRC2012_val_00042034.JPEG +ILSVRC2012_val_00014665.JPEG +ILSVRC2012_val_00038261.JPEG +ILSVRC2012_val_00041045.JPEG +ILSVRC2012_val_00006728.JPEG +ILSVRC2012_val_00002676.JPEG +ILSVRC2012_val_00023730.JPEG +ILSVRC2012_val_00000892.JPEG +ILSVRC2012_val_00005785.JPEG +ILSVRC2012_val_00011711.JPEG +ILSVRC2012_val_00002209.JPEG +ILSVRC2012_val_00043615.JPEG +ILSVRC2012_val_00012025.JPEG +ILSVRC2012_val_00006658.JPEG +ILSVRC2012_val_00041994.JPEG +ILSVRC2012_val_00017961.JPEG +ILSVRC2012_val_00007095.JPEG +ILSVRC2012_val_00022708.JPEG +ILSVRC2012_val_00025734.JPEG +ILSVRC2012_val_00040233.JPEG +ILSVRC2012_val_00001563.JPEG +ILSVRC2012_val_00005628.JPEG +ILSVRC2012_val_00026670.JPEG +ILSVRC2012_val_00047769.JPEG +ILSVRC2012_val_00029341.JPEG +ILSVRC2012_val_00023848.JPEG +ILSVRC2012_val_00029011.JPEG +ILSVRC2012_val_00019523.JPEG +ILSVRC2012_val_00000011.JPEG +ILSVRC2012_val_00002038.JPEG +ILSVRC2012_val_00012843.JPEG +ILSVRC2012_val_00010507.JPEG +ILSVRC2012_val_00013573.JPEG +ILSVRC2012_val_00020570.JPEG +ILSVRC2012_val_00029231.JPEG +ILSVRC2012_val_00035697.JPEG +ILSVRC2012_val_00004904.JPEG +ILSVRC2012_val_00020445.JPEG +ILSVRC2012_val_00031206.JPEG +ILSVRC2012_val_00015567.JPEG +ILSVRC2012_val_00019929.JPEG +ILSVRC2012_val_00001644.JPEG +ILSVRC2012_val_00009977.JPEG +ILSVRC2012_val_00040095.JPEG +ILSVRC2012_val_00045511.JPEG +ILSVRC2012_val_00034560.JPEG +ILSVRC2012_val_00017329.JPEG +ILSVRC2012_val_00022390.JPEG +ILSVRC2012_val_00044539.JPEG +ILSVRC2012_val_00026711.JPEG +ILSVRC2012_val_00005126.JPEG +ILSVRC2012_val_00035069.JPEG +ILSVRC2012_val_00002150.JPEG +ILSVRC2012_val_00022569.JPEG +ILSVRC2012_val_00031309.JPEG +ILSVRC2012_val_00014787.JPEG +ILSVRC2012_val_00018142.JPEG +ILSVRC2012_val_00038128.JPEG +ILSVRC2012_val_00018474.JPEG +ILSVRC2012_val_00009575.JPEG +ILSVRC2012_val_00017599.JPEG +ILSVRC2012_val_00047158.JPEG +ILSVRC2012_val_00031276.JPEG +ILSVRC2012_val_00029258.JPEG +ILSVRC2012_val_00039169.JPEG +ILSVRC2012_val_00042704.JPEG +ILSVRC2012_val_00035558.JPEG +ILSVRC2012_val_00009367.JPEG +ILSVRC2012_val_00043780.JPEG +ILSVRC2012_val_00039548.JPEG +ILSVRC2012_val_00030618.JPEG +ILSVRC2012_val_00030588.JPEG +ILSVRC2012_val_00047337.JPEG +ILSVRC2012_val_00020135.JPEG +ILSVRC2012_val_00022252.JPEG +ILSVRC2012_val_00024433.JPEG +ILSVRC2012_val_00004808.JPEG +ILSVRC2012_val_00037553.JPEG +ILSVRC2012_val_00021530.JPEG +ILSVRC2012_val_00036534.JPEG +ILSVRC2012_val_00015999.JPEG +ILSVRC2012_val_00028384.JPEG +ILSVRC2012_val_00032067.JPEG +ILSVRC2012_val_00005775.JPEG +ILSVRC2012_val_00023059.JPEG +ILSVRC2012_val_00007300.JPEG +ILSVRC2012_val_00033629.JPEG +ILSVRC2012_val_00030679.JPEG +ILSVRC2012_val_00001680.JPEG +ILSVRC2012_val_00037629.JPEG +ILSVRC2012_val_00005631.JPEG +ILSVRC2012_val_00014180.JPEG +ILSVRC2012_val_00004445.JPEG +ILSVRC2012_val_00001682.JPEG +ILSVRC2012_val_00040498.JPEG +ILSVRC2012_val_00023474.JPEG +ILSVRC2012_val_00023180.JPEG +ILSVRC2012_val_00045918.JPEG +ILSVRC2012_val_00020022.JPEG +ILSVRC2012_val_00006606.JPEG +ILSVRC2012_val_00043661.JPEG +ILSVRC2012_val_00013733.JPEG +ILSVRC2012_val_00021645.JPEG +ILSVRC2012_val_00017617.JPEG +ILSVRC2012_val_00006620.JPEG +ILSVRC2012_val_00049348.JPEG +ILSVRC2012_val_00035255.JPEG +ILSVRC2012_val_00018402.JPEG +ILSVRC2012_val_00033456.JPEG +ILSVRC2012_val_00027231.JPEG +ILSVRC2012_val_00047425.JPEG +ILSVRC2012_val_00008808.JPEG +ILSVRC2012_val_00032896.JPEG +ILSVRC2012_val_00002708.JPEG +ILSVRC2012_val_00042909.JPEG +ILSVRC2012_val_00007892.JPEG +ILSVRC2012_val_00044341.JPEG +ILSVRC2012_val_00028901.JPEG +ILSVRC2012_val_00048862.JPEG +ILSVRC2012_val_00039992.JPEG +ILSVRC2012_val_00012935.JPEG +ILSVRC2012_val_00015029.JPEG +ILSVRC2012_val_00013959.JPEG +ILSVRC2012_val_00022751.JPEG +ILSVRC2012_val_00049467.JPEG +ILSVRC2012_val_00030868.JPEG +ILSVRC2012_val_00034313.JPEG +ILSVRC2012_val_00002918.JPEG +ILSVRC2012_val_00027700.JPEG +ILSVRC2012_val_00015226.JPEG +ILSVRC2012_val_00044885.JPEG +ILSVRC2012_val_00045973.JPEG +ILSVRC2012_val_00024202.JPEG +ILSVRC2012_val_00002056.JPEG +ILSVRC2012_val_00010949.JPEG +ILSVRC2012_val_00037173.JPEG +ILSVRC2012_val_00007490.JPEG +ILSVRC2012_val_00006615.JPEG +ILSVRC2012_val_00044867.JPEG +ILSVRC2012_val_00041670.JPEG +ILSVRC2012_val_00002048.JPEG +ILSVRC2012_val_00022341.JPEG +ILSVRC2012_val_00048542.JPEG +ILSVRC2012_val_00030396.JPEG +ILSVRC2012_val_00042943.JPEG +ILSVRC2012_val_00006366.JPEG +ILSVRC2012_val_00027396.JPEG +ILSVRC2012_val_00033434.JPEG +ILSVRC2012_val_00043671.JPEG +ILSVRC2012_val_00046032.JPEG +ILSVRC2012_val_00018019.JPEG +ILSVRC2012_val_00027523.JPEG +ILSVRC2012_val_00039875.JPEG +ILSVRC2012_val_00029834.JPEG +ILSVRC2012_val_00046293.JPEG +ILSVRC2012_val_00002716.JPEG +ILSVRC2012_val_00007561.JPEG +ILSVRC2012_val_00016481.JPEG +ILSVRC2012_val_00005610.JPEG +ILSVRC2012_val_00029745.JPEG +ILSVRC2012_val_00005156.JPEG +ILSVRC2012_val_00004911.JPEG +ILSVRC2012_val_00011262.JPEG +ILSVRC2012_val_00041639.JPEG +ILSVRC2012_val_00044405.JPEG +ILSVRC2012_val_00036150.JPEG +ILSVRC2012_val_00013413.JPEG +ILSVRC2012_val_00032418.JPEG +ILSVRC2012_val_00012747.JPEG +ILSVRC2012_val_00047145.JPEG +ILSVRC2012_val_00033503.JPEG +ILSVRC2012_val_00019536.JPEG +ILSVRC2012_val_00049462.JPEG +ILSVRC2012_val_00009912.JPEG +ILSVRC2012_val_00010375.JPEG +ILSVRC2012_val_00018499.JPEG +ILSVRC2012_val_00011773.JPEG +ILSVRC2012_val_00039375.JPEG +ILSVRC2012_val_00005620.JPEG +ILSVRC2012_val_00037453.JPEG +ILSVRC2012_val_00005355.JPEG +ILSVRC2012_val_00040102.JPEG +ILSVRC2012_val_00024159.JPEG +ILSVRC2012_val_00029850.JPEG +ILSVRC2012_val_00017088.JPEG +ILSVRC2012_val_00004767.JPEG +ILSVRC2012_val_00006983.JPEG +ILSVRC2012_val_00010362.JPEG +ILSVRC2012_val_00041889.JPEG +ILSVRC2012_val_00027497.JPEG +ILSVRC2012_val_00020942.JPEG +ILSVRC2012_val_00021520.JPEG +ILSVRC2012_val_00015727.JPEG +ILSVRC2012_val_00040437.JPEG +ILSVRC2012_val_00025366.JPEG +ILSVRC2012_val_00042855.JPEG +ILSVRC2012_val_00040634.JPEG +ILSVRC2012_val_00027302.JPEG +ILSVRC2012_val_00005149.JPEG +ILSVRC2012_val_00039193.JPEG +ILSVRC2012_val_00006857.JPEG +ILSVRC2012_val_00030359.JPEG +ILSVRC2012_val_00017883.JPEG +ILSVRC2012_val_00026423.JPEG +ILSVRC2012_val_00021267.JPEG +ILSVRC2012_val_00010164.JPEG +ILSVRC2012_val_00038887.JPEG +ILSVRC2012_val_00018544.JPEG +ILSVRC2012_val_00013872.JPEG +ILSVRC2012_val_00038034.JPEG +ILSVRC2012_val_00030237.JPEG +ILSVRC2012_val_00038450.JPEG +ILSVRC2012_val_00028161.JPEG +ILSVRC2012_val_00013208.JPEG +ILSVRC2012_val_00049885.JPEG +ILSVRC2012_val_00039603.JPEG +ILSVRC2012_val_00024712.JPEG +ILSVRC2012_val_00027986.JPEG +ILSVRC2012_val_00042604.JPEG +ILSVRC2012_val_00028701.JPEG +ILSVRC2012_val_00018917.JPEG +ILSVRC2012_val_00032798.JPEG +ILSVRC2012_val_00024882.JPEG +ILSVRC2012_val_00007836.JPEG +ILSVRC2012_val_00036046.JPEG +ILSVRC2012_val_00031691.JPEG +ILSVRC2012_val_00012156.JPEG +ILSVRC2012_val_00034198.JPEG +ILSVRC2012_val_00024988.JPEG +ILSVRC2012_val_00014181.JPEG +ILSVRC2012_val_00005874.JPEG +ILSVRC2012_val_00021393.JPEG +ILSVRC2012_val_00018761.JPEG +ILSVRC2012_val_00049998.JPEG +ILSVRC2012_val_00043057.JPEG +ILSVRC2012_val_00043063.JPEG +ILSVRC2012_val_00039150.JPEG +ILSVRC2012_val_00006901.JPEG +ILSVRC2012_val_00026149.JPEG +ILSVRC2012_val_00047667.JPEG +ILSVRC2012_val_00029822.JPEG +ILSVRC2012_val_00003226.JPEG +ILSVRC2012_val_00041669.JPEG +ILSVRC2012_val_00011342.JPEG +ILSVRC2012_val_00016400.JPEG +ILSVRC2012_val_00028246.JPEG +ILSVRC2012_val_00022482.JPEG +ILSVRC2012_val_00013153.JPEG +ILSVRC2012_val_00011625.JPEG +ILSVRC2012_val_00047834.JPEG +ILSVRC2012_val_00043415.JPEG +ILSVRC2012_val_00000154.JPEG +ILSVRC2012_val_00024673.JPEG +ILSVRC2012_val_00004148.JPEG +ILSVRC2012_val_00025081.JPEG +ILSVRC2012_val_00044473.JPEG +ILSVRC2012_val_00045650.JPEG +ILSVRC2012_val_00002939.JPEG +ILSVRC2012_val_00041022.JPEG +ILSVRC2012_val_00042730.JPEG +ILSVRC2012_val_00043773.JPEG +ILSVRC2012_val_00032589.JPEG +ILSVRC2012_val_00003509.JPEG +ILSVRC2012_val_00027886.JPEG +ILSVRC2012_val_00015769.JPEG +ILSVRC2012_val_00033978.JPEG +ILSVRC2012_val_00032920.JPEG +ILSVRC2012_val_00017665.JPEG +ILSVRC2012_val_00005577.JPEG +ILSVRC2012_val_00028797.JPEG +ILSVRC2012_val_00034778.JPEG +ILSVRC2012_val_00015555.JPEG +ILSVRC2012_val_00028221.JPEG +ILSVRC2012_val_00041122.JPEG +ILSVRC2012_val_00041168.JPEG +ILSVRC2012_val_00041117.JPEG +ILSVRC2012_val_00049666.JPEG +ILSVRC2012_val_00034527.JPEG +ILSVRC2012_val_00005360.JPEG +ILSVRC2012_val_00038460.JPEG +ILSVRC2012_val_00018327.JPEG +ILSVRC2012_val_00026553.JPEG +ILSVRC2012_val_00018272.JPEG +ILSVRC2012_val_00012993.JPEG +ILSVRC2012_val_00020940.JPEG +ILSVRC2012_val_00025832.JPEG +ILSVRC2012_val_00036339.JPEG +ILSVRC2012_val_00026193.JPEG +ILSVRC2012_val_00014475.JPEG +ILSVRC2012_val_00002707.JPEG +ILSVRC2012_val_00025647.JPEG +ILSVRC2012_val_00032519.JPEG +ILSVRC2012_val_00005895.JPEG +ILSVRC2012_val_00016221.JPEG +ILSVRC2012_val_00025592.JPEG +ILSVRC2012_val_00029326.JPEG +ILSVRC2012_val_00000995.JPEG +ILSVRC2012_val_00023689.JPEG +ILSVRC2012_val_00022975.JPEG +ILSVRC2012_val_00046307.JPEG +ILSVRC2012_val_00004043.JPEG +ILSVRC2012_val_00022793.JPEG +ILSVRC2012_val_00031755.JPEG +ILSVRC2012_val_00021433.JPEG +ILSVRC2012_val_00016097.JPEG +ILSVRC2012_val_00033240.JPEG +ILSVRC2012_val_00012559.JPEG +ILSVRC2012_val_00031317.JPEG +ILSVRC2012_val_00003300.JPEG +ILSVRC2012_val_00004569.JPEG +ILSVRC2012_val_00033310.JPEG +ILSVRC2012_val_00014323.JPEG +ILSVRC2012_val_00036169.JPEG +ILSVRC2012_val_00030957.JPEG +ILSVRC2012_val_00033645.JPEG +ILSVRC2012_val_00012453.JPEG +ILSVRC2012_val_00000965.JPEG +ILSVRC2012_val_00027177.JPEG +ILSVRC2012_val_00031602.JPEG +ILSVRC2012_val_00016643.JPEG +ILSVRC2012_val_00041313.JPEG +ILSVRC2012_val_00007450.JPEG +ILSVRC2012_val_00036691.JPEG +ILSVRC2012_val_00013378.JPEG +ILSVRC2012_val_00030121.JPEG +ILSVRC2012_val_00027438.JPEG +ILSVRC2012_val_00026519.JPEG +ILSVRC2012_val_00037504.JPEG +ILSVRC2012_val_00002148.JPEG +ILSVRC2012_val_00038045.JPEG +ILSVRC2012_val_00013412.JPEG +ILSVRC2012_val_00016213.JPEG +ILSVRC2012_val_00012189.JPEG +ILSVRC2012_val_00012220.JPEG +ILSVRC2012_val_00011135.JPEG +ILSVRC2012_val_00018492.JPEG +ILSVRC2012_val_00026390.JPEG +ILSVRC2012_val_00002645.JPEG +ILSVRC2012_val_00047444.JPEG +ILSVRC2012_val_00039270.JPEG +ILSVRC2012_val_00015447.JPEG +ILSVRC2012_val_00048403.JPEG +ILSVRC2012_val_00003295.JPEG +ILSVRC2012_val_00008292.JPEG +ILSVRC2012_val_00005792.JPEG +ILSVRC2012_val_00017291.JPEG +ILSVRC2012_val_00026101.JPEG +ILSVRC2012_val_00003379.JPEG +ILSVRC2012_val_00024192.JPEG +ILSVRC2012_val_00043129.JPEG +ILSVRC2012_val_00013765.JPEG +ILSVRC2012_val_00010938.JPEG +ILSVRC2012_val_00031349.JPEG +ILSVRC2012_val_00030398.JPEG +ILSVRC2012_val_00010086.JPEG +ILSVRC2012_val_00009722.JPEG +ILSVRC2012_val_00011812.JPEG +ILSVRC2012_val_00029508.JPEG +ILSVRC2012_val_00011529.JPEG +ILSVRC2012_val_00017006.JPEG +ILSVRC2012_val_00024064.JPEG +ILSVRC2012_val_00030809.JPEG +ILSVRC2012_val_00033897.JPEG +ILSVRC2012_val_00013451.JPEG +ILSVRC2012_val_00006475.JPEG +ILSVRC2012_val_00022694.JPEG +ILSVRC2012_val_00036185.JPEG +ILSVRC2012_val_00044987.JPEG +ILSVRC2012_val_00027211.JPEG +ILSVRC2012_val_00017300.JPEG +ILSVRC2012_val_00040743.JPEG +ILSVRC2012_val_00024552.JPEG +ILSVRC2012_val_00033732.JPEG +ILSVRC2012_val_00016779.JPEG +ILSVRC2012_val_00011829.JPEG +ILSVRC2012_val_00014245.JPEG +ILSVRC2012_val_00029947.JPEG +ILSVRC2012_val_00034320.JPEG +ILSVRC2012_val_00018707.JPEG +ILSVRC2012_val_00014154.JPEG +ILSVRC2012_val_00047162.JPEG +ILSVRC2012_val_00035789.JPEG +ILSVRC2012_val_00044500.JPEG +ILSVRC2012_val_00018197.JPEG +ILSVRC2012_val_00003376.JPEG +ILSVRC2012_val_00044614.JPEG +ILSVRC2012_val_00027662.JPEG +ILSVRC2012_val_00006789.JPEG +ILSVRC2012_val_00040515.JPEG +ILSVRC2012_val_00016093.JPEG +ILSVRC2012_val_00000205.JPEG +ILSVRC2012_val_00000348.JPEG +ILSVRC2012_val_00020894.JPEG +ILSVRC2012_val_00003551.JPEG +ILSVRC2012_val_00008679.JPEG +ILSVRC2012_val_00012040.JPEG +ILSVRC2012_val_00002088.JPEG +ILSVRC2012_val_00032529.JPEG +ILSVRC2012_val_00006428.JPEG +ILSVRC2012_val_00000697.JPEG +ILSVRC2012_val_00045213.JPEG +ILSVRC2012_val_00047569.JPEG +ILSVRC2012_val_00013316.JPEG +ILSVRC2012_val_00027803.JPEG +ILSVRC2012_val_00009761.JPEG +ILSVRC2012_val_00012575.JPEG +ILSVRC2012_val_00022582.JPEG +ILSVRC2012_val_00013170.JPEG +ILSVRC2012_val_00000282.JPEG +ILSVRC2012_val_00024327.JPEG +ILSVRC2012_val_00011471.JPEG +ILSVRC2012_val_00032937.JPEG +ILSVRC2012_val_00003107.JPEG +ILSVRC2012_val_00046278.JPEG +ILSVRC2012_val_00003731.JPEG +ILSVRC2012_val_00013557.JPEG +ILSVRC2012_val_00018979.JPEG +ILSVRC2012_val_00007941.JPEG +ILSVRC2012_val_00027204.JPEG +ILSVRC2012_val_00032474.JPEG +ILSVRC2012_val_00026606.JPEG +ILSVRC2012_val_00045900.JPEG +ILSVRC2012_val_00041600.JPEG +ILSVRC2012_val_00002949.JPEG +ILSVRC2012_val_00044418.JPEG +ILSVRC2012_val_00007824.JPEG +ILSVRC2012_val_00034989.JPEG +ILSVRC2012_val_00046215.JPEG +ILSVRC2012_val_00004662.JPEG +ILSVRC2012_val_00037501.JPEG +ILSVRC2012_val_00014330.JPEG +ILSVRC2012_val_00031469.JPEG +ILSVRC2012_val_00038741.JPEG +ILSVRC2012_val_00006737.JPEG +ILSVRC2012_val_00015220.JPEG +ILSVRC2012_val_00018921.JPEG +ILSVRC2012_val_00022528.JPEG +ILSVRC2012_val_00014043.JPEG +ILSVRC2012_val_00012247.JPEG +ILSVRC2012_val_00018789.JPEG +ILSVRC2012_val_00043803.JPEG +ILSVRC2012_val_00045383.JPEG +ILSVRC2012_val_00005026.JPEG +ILSVRC2012_val_00037393.JPEG +ILSVRC2012_val_00001432.JPEG +ILSVRC2012_val_00002377.JPEG +ILSVRC2012_val_00028298.JPEG +ILSVRC2012_val_00037492.JPEG +ILSVRC2012_val_00028886.JPEG +ILSVRC2012_val_00017512.JPEG +ILSVRC2012_val_00039318.JPEG +ILSVRC2012_val_00003690.JPEG +ILSVRC2012_val_00024577.JPEG +ILSVRC2012_val_00042017.JPEG +ILSVRC2012_val_00025249.JPEG +ILSVRC2012_val_00004513.JPEG +ILSVRC2012_val_00000330.JPEG +ILSVRC2012_val_00039787.JPEG +ILSVRC2012_val_00015902.JPEG +ILSVRC2012_val_00042634.JPEG +ILSVRC2012_val_00022389.JPEG +ILSVRC2012_val_00041893.JPEG +ILSVRC2012_val_00032078.JPEG +ILSVRC2012_val_00043892.JPEG +ILSVRC2012_val_00000162.JPEG +ILSVRC2012_val_00020612.JPEG +ILSVRC2012_val_00003626.JPEG +ILSVRC2012_val_00005454.JPEG +ILSVRC2012_val_00047537.JPEG +ILSVRC2012_val_00044345.JPEG +ILSVRC2012_val_00002854.JPEG +ILSVRC2012_val_00037783.JPEG +ILSVRC2012_val_00019841.JPEG +ILSVRC2012_val_00047739.JPEG +ILSVRC2012_val_00008621.JPEG +ILSVRC2012_val_00034003.JPEG +ILSVRC2012_val_00024701.JPEG +ILSVRC2012_val_00031050.JPEG +ILSVRC2012_val_00007288.JPEG +ILSVRC2012_val_00016022.JPEG +ILSVRC2012_val_00049663.JPEG +ILSVRC2012_val_00003775.JPEG +ILSVRC2012_val_00044090.JPEG +ILSVRC2012_val_00023990.JPEG +ILSVRC2012_val_00041754.JPEG +ILSVRC2012_val_00029240.JPEG +ILSVRC2012_val_00041705.JPEG +ILSVRC2012_val_00001101.JPEG +ILSVRC2012_val_00006800.JPEG +ILSVRC2012_val_00008715.JPEG +ILSVRC2012_val_00041113.JPEG +ILSVRC2012_val_00035537.JPEG +ILSVRC2012_val_00046328.JPEG +ILSVRC2012_val_00026025.JPEG +ILSVRC2012_val_00015797.JPEG +ILSVRC2012_val_00041220.JPEG +ILSVRC2012_val_00033362.JPEG +ILSVRC2012_val_00023500.JPEG +ILSVRC2012_val_00031146.JPEG +ILSVRC2012_val_00004220.JPEG +ILSVRC2012_val_00023321.JPEG +ILSVRC2012_val_00042607.JPEG +ILSVRC2012_val_00028226.JPEG +ILSVRC2012_val_00040101.JPEG +ILSVRC2012_val_00000910.JPEG +ILSVRC2012_val_00010329.JPEG +ILSVRC2012_val_00017753.JPEG +ILSVRC2012_val_00026816.JPEG +ILSVRC2012_val_00008065.JPEG +ILSVRC2012_val_00037404.JPEG +ILSVRC2012_val_00033457.JPEG +ILSVRC2012_val_00020621.JPEG +ILSVRC2012_val_00049323.JPEG +ILSVRC2012_val_00009781.JPEG +ILSVRC2012_val_00006752.JPEG +ILSVRC2012_val_00039470.JPEG +ILSVRC2012_val_00031908.JPEG +ILSVRC2012_val_00041043.JPEG +ILSVRC2012_val_00040901.JPEG +ILSVRC2012_val_00045577.JPEG +ILSVRC2012_val_00000521.JPEG +ILSVRC2012_val_00036913.JPEG +ILSVRC2012_val_00023141.JPEG +ILSVRC2012_val_00014080.JPEG +ILSVRC2012_val_00028712.JPEG +ILSVRC2012_val_00009924.JPEG +ILSVRC2012_val_00019103.JPEG +ILSVRC2012_val_00038037.JPEG +ILSVRC2012_val_00009662.JPEG +ILSVRC2012_val_00001448.JPEG +ILSVRC2012_val_00041540.JPEG +ILSVRC2012_val_00042726.JPEG +ILSVRC2012_val_00043350.JPEG +ILSVRC2012_val_00037326.JPEG +ILSVRC2012_val_00041006.JPEG +ILSVRC2012_val_00045197.JPEG +ILSVRC2012_val_00024342.JPEG +ILSVRC2012_val_00019265.JPEG +ILSVRC2012_val_00035706.JPEG +ILSVRC2012_val_00021115.JPEG +ILSVRC2012_val_00012601.JPEG +ILSVRC2012_val_00000128.JPEG +ILSVRC2012_val_00004860.JPEG +ILSVRC2012_val_00041919.JPEG +ILSVRC2012_val_00000143.JPEG +ILSVRC2012_val_00038194.JPEG +ILSVRC2012_val_00022918.JPEG +ILSVRC2012_val_00041779.JPEG +ILSVRC2012_val_00009870.JPEG +ILSVRC2012_val_00011524.JPEG +ILSVRC2012_val_00042757.JPEG +ILSVRC2012_val_00001762.JPEG +ILSVRC2012_val_00000306.JPEG +ILSVRC2012_val_00044373.JPEG +ILSVRC2012_val_00017266.JPEG +ILSVRC2012_val_00010592.JPEG +ILSVRC2012_val_00011482.JPEG +ILSVRC2012_val_00029628.JPEG +ILSVRC2012_val_00008282.JPEG +ILSVRC2012_val_00048258.JPEG +ILSVRC2012_val_00020240.JPEG +ILSVRC2012_val_00028670.JPEG +ILSVRC2012_val_00032620.JPEG +ILSVRC2012_val_00038155.JPEG +ILSVRC2012_val_00035454.JPEG +ILSVRC2012_val_00046911.JPEG +ILSVRC2012_val_00024977.JPEG +ILSVRC2012_val_00010341.JPEG +ILSVRC2012_val_00034555.JPEG +ILSVRC2012_val_00018882.JPEG +ILSVRC2012_val_00035406.JPEG +ILSVRC2012_val_00034863.JPEG +ILSVRC2012_val_00017986.JPEG +ILSVRC2012_val_00033992.JPEG +ILSVRC2012_val_00041150.JPEG +ILSVRC2012_val_00042305.JPEG +ILSVRC2012_val_00002215.JPEG +ILSVRC2012_val_00018129.JPEG +ILSVRC2012_val_00003993.JPEG +ILSVRC2012_val_00011014.JPEG +ILSVRC2012_val_00039085.JPEG +ILSVRC2012_val_00035013.JPEG +ILSVRC2012_val_00005521.JPEG +ILSVRC2012_val_00043316.JPEG +ILSVRC2012_val_00012359.JPEG +ILSVRC2012_val_00004535.JPEG +ILSVRC2012_val_00002896.JPEG +ILSVRC2012_val_00015113.JPEG +ILSVRC2012_val_00042125.JPEG +ILSVRC2012_val_00032800.JPEG +ILSVRC2012_val_00040397.JPEG +ILSVRC2012_val_00023303.JPEG +ILSVRC2012_val_00039964.JPEG +ILSVRC2012_val_00022563.JPEG +ILSVRC2012_val_00032142.JPEG +ILSVRC2012_val_00017992.JPEG +ILSVRC2012_val_00041283.JPEG +ILSVRC2012_val_00020120.JPEG +ILSVRC2012_val_00001766.JPEG +ILSVRC2012_val_00002602.JPEG +ILSVRC2012_val_00018066.JPEG +ILSVRC2012_val_00040345.JPEG +ILSVRC2012_val_00017720.JPEG +ILSVRC2012_val_00021802.JPEG +ILSVRC2012_val_00032725.JPEG +ILSVRC2012_val_00019564.JPEG +ILSVRC2012_val_00039692.JPEG +ILSVRC2012_val_00004533.JPEG +ILSVRC2012_val_00003182.JPEG +ILSVRC2012_val_00008524.JPEG +ILSVRC2012_val_00030531.JPEG +ILSVRC2012_val_00035403.JPEG +ILSVRC2012_val_00010356.JPEG +ILSVRC2012_val_00044538.JPEG +ILSVRC2012_val_00018974.JPEG +ILSVRC2012_val_00040818.JPEG +ILSVRC2012_val_00002570.JPEG +ILSVRC2012_val_00003442.JPEG +ILSVRC2012_val_00047033.JPEG +ILSVRC2012_val_00004165.JPEG +ILSVRC2012_val_00014811.JPEG +ILSVRC2012_val_00036742.JPEG +ILSVRC2012_val_00030646.JPEG +ILSVRC2012_val_00023380.JPEG +ILSVRC2012_val_00011250.JPEG +ILSVRC2012_val_00011864.JPEG +ILSVRC2012_val_00012014.JPEG +ILSVRC2012_val_00021600.JPEG +ILSVRC2012_val_00010979.JPEG +ILSVRC2012_val_00017764.JPEG +ILSVRC2012_val_00016702.JPEG +ILSVRC2012_val_00016197.JPEG +ILSVRC2012_val_00017035.JPEG +ILSVRC2012_val_00044214.JPEG +ILSVRC2012_val_00024763.JPEG +ILSVRC2012_val_00045762.JPEG +ILSVRC2012_val_00033644.JPEG +ILSVRC2012_val_00027530.JPEG +ILSVRC2012_val_00011379.JPEG +ILSVRC2012_val_00045015.JPEG +ILSVRC2012_val_00012819.JPEG +ILSVRC2012_val_00047060.JPEG +ILSVRC2012_val_00044059.JPEG +ILSVRC2012_val_00044957.JPEG +ILSVRC2012_val_00036609.JPEG +ILSVRC2012_val_00039290.JPEG +ILSVRC2012_val_00044426.JPEG +ILSVRC2012_val_00037676.JPEG +ILSVRC2012_val_00001356.JPEG +ILSVRC2012_val_00030906.JPEG +ILSVRC2012_val_00003128.JPEG +ILSVRC2012_val_00042177.JPEG +ILSVRC2012_val_00022806.JPEG +ILSVRC2012_val_00022611.JPEG +ILSVRC2012_val_00022101.JPEG +ILSVRC2012_val_00018827.JPEG +ILSVRC2012_val_00039278.JPEG +ILSVRC2012_val_00043778.JPEG +ILSVRC2012_val_00010670.JPEG +ILSVRC2012_val_00026388.JPEG +ILSVRC2012_val_00004668.JPEG +ILSVRC2012_val_00040556.JPEG +ILSVRC2012_val_00035482.JPEG +ILSVRC2012_val_00007882.JPEG +ILSVRC2012_val_00043852.JPEG +ILSVRC2012_val_00002454.JPEG +ILSVRC2012_val_00030805.JPEG +ILSVRC2012_val_00013701.JPEG +ILSVRC2012_val_00002671.JPEG +ILSVRC2012_val_00016060.JPEG +ILSVRC2012_val_00028634.JPEG +ILSVRC2012_val_00049726.JPEG +ILSVRC2012_val_00016732.JPEG +ILSVRC2012_val_00001411.JPEG +ILSVRC2012_val_00042834.JPEG +ILSVRC2012_val_00041925.JPEG +ILSVRC2012_val_00020939.JPEG +ILSVRC2012_val_00015097.JPEG +ILSVRC2012_val_00028420.JPEG +ILSVRC2012_val_00037946.JPEG +ILSVRC2012_val_00004246.JPEG +ILSVRC2012_val_00027299.JPEG +ILSVRC2012_val_00009048.JPEG +ILSVRC2012_val_00014522.JPEG +ILSVRC2012_val_00026400.JPEG +ILSVRC2012_val_00027841.JPEG +ILSVRC2012_val_00020641.JPEG +ILSVRC2012_val_00006240.JPEG +ILSVRC2012_val_00007603.JPEG +ILSVRC2012_val_00045301.JPEG +ILSVRC2012_val_00000772.JPEG +ILSVRC2012_val_00018275.JPEG +ILSVRC2012_val_00034080.JPEG +ILSVRC2012_val_00006705.JPEG +ILSVRC2012_val_00028230.JPEG +ILSVRC2012_val_00009457.JPEG +ILSVRC2012_val_00026175.JPEG +ILSVRC2012_val_00022811.JPEG +ILSVRC2012_val_00039183.JPEG +ILSVRC2012_val_00017158.JPEG +ILSVRC2012_val_00007013.JPEG +ILSVRC2012_val_00010298.JPEG +ILSVRC2012_val_00020317.JPEG +ILSVRC2012_val_00023878.JPEG +ILSVRC2012_val_00038489.JPEG +ILSVRC2012_val_00027393.JPEG +ILSVRC2012_val_00029869.JPEG +ILSVRC2012_val_00043668.JPEG +ILSVRC2012_val_00027391.JPEG +ILSVRC2012_val_00011763.JPEG +ILSVRC2012_val_00019050.JPEG +ILSVRC2012_val_00032472.JPEG +ILSVRC2012_val_00008868.JPEG +ILSVRC2012_val_00048126.JPEG +ILSVRC2012_val_00004930.JPEG +ILSVRC2012_val_00007361.JPEG +ILSVRC2012_val_00017668.JPEG +ILSVRC2012_val_00028092.JPEG +ILSVRC2012_val_00009286.JPEG +ILSVRC2012_val_00006548.JPEG +ILSVRC2012_val_00038548.JPEG +ILSVRC2012_val_00023793.JPEG +ILSVRC2012_val_00047389.JPEG +ILSVRC2012_val_00029817.JPEG +ILSVRC2012_val_00001707.JPEG +ILSVRC2012_val_00001934.JPEG +ILSVRC2012_val_00013376.JPEG +ILSVRC2012_val_00004358.JPEG +ILSVRC2012_val_00021807.JPEG +ILSVRC2012_val_00002366.JPEG +ILSVRC2012_val_00028453.JPEG +ILSVRC2012_val_00031776.JPEG +ILSVRC2012_val_00020943.JPEG +ILSVRC2012_val_00022738.JPEG +ILSVRC2012_val_00004013.JPEG +ILSVRC2012_val_00041070.JPEG +ILSVRC2012_val_00048832.JPEG +ILSVRC2012_val_00024418.JPEG +ILSVRC2012_val_00049429.JPEG +ILSVRC2012_val_00004011.JPEG +ILSVRC2012_val_00008107.JPEG +ILSVRC2012_val_00034276.JPEG +ILSVRC2012_val_00023395.JPEG +ILSVRC2012_val_00026084.JPEG +ILSVRC2012_val_00024520.JPEG +ILSVRC2012_val_00035168.JPEG +ILSVRC2012_val_00028200.JPEG +ILSVRC2012_val_00046559.JPEG +ILSVRC2012_val_00027549.JPEG +ILSVRC2012_val_00039498.JPEG +ILSVRC2012_val_00045935.JPEG +ILSVRC2012_val_00015181.JPEG +ILSVRC2012_val_00034969.JPEG +ILSVRC2012_val_00001485.JPEG +ILSVRC2012_val_00049208.JPEG +ILSVRC2012_val_00008870.JPEG +ILSVRC2012_val_00013963.JPEG +ILSVRC2012_val_00026284.JPEG +ILSVRC2012_val_00041943.JPEG +ILSVRC2012_val_00001136.JPEG +ILSVRC2012_val_00047927.JPEG +ILSVRC2012_val_00006354.JPEG +ILSVRC2012_val_00019935.JPEG +ILSVRC2012_val_00044783.JPEG +ILSVRC2012_val_00015920.JPEG +ILSVRC2012_val_00002934.JPEG +ILSVRC2012_val_00019552.JPEG +ILSVRC2012_val_00042965.JPEG +ILSVRC2012_val_00006514.JPEG +ILSVRC2012_val_00043766.JPEG +ILSVRC2012_val_00047238.JPEG +ILSVRC2012_val_00038505.JPEG +ILSVRC2012_val_00039719.JPEG +ILSVRC2012_val_00027309.JPEG +ILSVRC2012_val_00028722.JPEG +ILSVRC2012_val_00018962.JPEG +ILSVRC2012_val_00015552.JPEG +ILSVRC2012_val_00037464.JPEG +ILSVRC2012_val_00041612.JPEG +ILSVRC2012_val_00045914.JPEG +ILSVRC2012_val_00009529.JPEG +ILSVRC2012_val_00025521.JPEG +ILSVRC2012_val_00027893.JPEG +ILSVRC2012_val_00039957.JPEG +ILSVRC2012_val_00020538.JPEG +ILSVRC2012_val_00038657.JPEG +ILSVRC2012_val_00019539.JPEG +ILSVRC2012_val_00023966.JPEG +ILSVRC2012_val_00026754.JPEG +ILSVRC2012_val_00030147.JPEG +ILSVRC2012_val_00045961.JPEG +ILSVRC2012_val_00031495.JPEG +ILSVRC2012_val_00040424.JPEG +ILSVRC2012_val_00011341.JPEG +ILSVRC2012_val_00017136.JPEG +ILSVRC2012_val_00015082.JPEG +ILSVRC2012_val_00003013.JPEG +ILSVRC2012_val_00005164.JPEG +ILSVRC2012_val_00041905.JPEG +ILSVRC2012_val_00036901.JPEG +ILSVRC2012_val_00040732.JPEG +ILSVRC2012_val_00047078.JPEG +ILSVRC2012_val_00044744.JPEG +ILSVRC2012_val_00010881.JPEG +ILSVRC2012_val_00024246.JPEG +ILSVRC2012_val_00008644.JPEG +ILSVRC2012_val_00024365.JPEG +ILSVRC2012_val_00033597.JPEG +ILSVRC2012_val_00040218.JPEG +ILSVRC2012_val_00024820.JPEG +ILSVRC2012_val_00032475.JPEG +ILSVRC2012_val_00002042.JPEG +ILSVRC2012_val_00020665.JPEG +ILSVRC2012_val_00048826.JPEG +ILSVRC2012_val_00020391.JPEG +ILSVRC2012_val_00036851.JPEG +ILSVRC2012_val_00046218.JPEG +ILSVRC2012_val_00027203.JPEG +ILSVRC2012_val_00010602.JPEG +ILSVRC2012_val_00047908.JPEG +ILSVRC2012_val_00017566.JPEG +ILSVRC2012_val_00008908.JPEG +ILSVRC2012_val_00009378.JPEG +ILSVRC2012_val_00024524.JPEG +ILSVRC2012_val_00048249.JPEG +ILSVRC2012_val_00037333.JPEG +ILSVRC2012_val_00043595.JPEG +ILSVRC2012_val_00015265.JPEG +ILSVRC2012_val_00018530.JPEG +ILSVRC2012_val_00003524.JPEG +ILSVRC2012_val_00046207.JPEG +ILSVRC2012_val_00020531.JPEG +ILSVRC2012_val_00036591.JPEG +ILSVRC2012_val_00049981.JPEG +ILSVRC2012_val_00018011.JPEG +ILSVRC2012_val_00022238.JPEG +ILSVRC2012_val_00009205.JPEG +ILSVRC2012_val_00048316.JPEG +ILSVRC2012_val_00036420.JPEG +ILSVRC2012_val_00049731.JPEG +ILSVRC2012_val_00020692.JPEG +ILSVRC2012_val_00020834.JPEG +ILSVRC2012_val_00006822.JPEG +ILSVRC2012_val_00012187.JPEG +ILSVRC2012_val_00043542.JPEG +ILSVRC2012_val_00014455.JPEG +ILSVRC2012_val_00023862.JPEG +ILSVRC2012_val_00037834.JPEG +ILSVRC2012_val_00003105.JPEG +ILSVRC2012_val_00043289.JPEG +ILSVRC2012_val_00031173.JPEG +ILSVRC2012_val_00044380.JPEG +ILSVRC2012_val_00042489.JPEG +ILSVRC2012_val_00033270.JPEG +ILSVRC2012_val_00012606.JPEG +ILSVRC2012_val_00023281.JPEG +ILSVRC2012_val_00028866.JPEG +ILSVRC2012_val_00005032.JPEG +ILSVRC2012_val_00014370.JPEG +ILSVRC2012_val_00029132.JPEG +ILSVRC2012_val_00010115.JPEG +ILSVRC2012_val_00021298.JPEG +ILSVRC2012_val_00042172.JPEG +ILSVRC2012_val_00027345.JPEG +ILSVRC2012_val_00009836.JPEG +ILSVRC2012_val_00037681.JPEG +ILSVRC2012_val_00030939.JPEG +ILSVRC2012_val_00049576.JPEG +ILSVRC2012_val_00024107.JPEG +ILSVRC2012_val_00029567.JPEG +ILSVRC2012_val_00005268.JPEG +ILSVRC2012_val_00031188.JPEG +ILSVRC2012_val_00011050.JPEG +ILSVRC2012_val_00015741.JPEG +ILSVRC2012_val_00023722.JPEG +ILSVRC2012_val_00027023.JPEG +ILSVRC2012_val_00030562.JPEG +ILSVRC2012_val_00044343.JPEG +ILSVRC2012_val_00036980.JPEG +ILSVRC2012_val_00007958.JPEG +ILSVRC2012_val_00012229.JPEG +ILSVRC2012_val_00023772.JPEG +ILSVRC2012_val_00024091.JPEG +ILSVRC2012_val_00022685.JPEG +ILSVRC2012_val_00028470.JPEG +ILSVRC2012_val_00038485.JPEG +ILSVRC2012_val_00046507.JPEG +ILSVRC2012_val_00002658.JPEG +ILSVRC2012_val_00034078.JPEG +ILSVRC2012_val_00009285.JPEG +ILSVRC2012_val_00047674.JPEG +ILSVRC2012_val_00039164.JPEG +ILSVRC2012_val_00046051.JPEG +ILSVRC2012_val_00042712.JPEG +ILSVRC2012_val_00049243.JPEG +ILSVRC2012_val_00014108.JPEG +ILSVRC2012_val_00016031.JPEG +ILSVRC2012_val_00040808.JPEG +ILSVRC2012_val_00042875.JPEG +ILSVRC2012_val_00049164.JPEG +ILSVRC2012_val_00006560.JPEG +ILSVRC2012_val_00049924.JPEG +ILSVRC2012_val_00031866.JPEG +ILSVRC2012_val_00040311.JPEG +ILSVRC2012_val_00047714.JPEG +ILSVRC2012_val_00020736.JPEG +ILSVRC2012_val_00013176.JPEG +ILSVRC2012_val_00048334.JPEG +ILSVRC2012_val_00041987.JPEG +ILSVRC2012_val_00011658.JPEG +ILSVRC2012_val_00011445.JPEG +ILSVRC2012_val_00049191.JPEG +ILSVRC2012_val_00017785.JPEG +ILSVRC2012_val_00027141.JPEG +ILSVRC2012_val_00029909.JPEG +ILSVRC2012_val_00029419.JPEG +ILSVRC2012_val_00037762.JPEG +ILSVRC2012_val_00000414.JPEG +ILSVRC2012_val_00027793.JPEG +ILSVRC2012_val_00019619.JPEG +ILSVRC2012_val_00034791.JPEG +ILSVRC2012_val_00017077.JPEG +ILSVRC2012_val_00001829.JPEG +ILSVRC2012_val_00009850.JPEG +ILSVRC2012_val_00014624.JPEG +ILSVRC2012_val_00043739.JPEG +ILSVRC2012_val_00010187.JPEG +ILSVRC2012_val_00019618.JPEG +ILSVRC2012_val_00008918.JPEG +ILSVRC2012_val_00022306.JPEG +ILSVRC2012_val_00001950.JPEG +ILSVRC2012_val_00022834.JPEG +ILSVRC2012_val_00003541.JPEG +ILSVRC2012_val_00024533.JPEG +ILSVRC2012_val_00008970.JPEG +ILSVRC2012_val_00043505.JPEG +ILSVRC2012_val_00040961.JPEG +ILSVRC2012_val_00000934.JPEG +ILSVRC2012_val_00018168.JPEG +ILSVRC2012_val_00034804.JPEG +ILSVRC2012_val_00014430.JPEG +ILSVRC2012_val_00036373.JPEG +ILSVRC2012_val_00034431.JPEG +ILSVRC2012_val_00038424.JPEG +ILSVRC2012_val_00019974.JPEG +ILSVRC2012_val_00008018.JPEG +ILSVRC2012_val_00020192.JPEG +ILSVRC2012_val_00003947.JPEG +ILSVRC2012_val_00033370.JPEG +ILSVRC2012_val_00001535.JPEG +ILSVRC2012_val_00000026.JPEG +ILSVRC2012_val_00029866.JPEG +ILSVRC2012_val_00013991.JPEG +ILSVRC2012_val_00017956.JPEG +ILSVRC2012_val_00014481.JPEG +ILSVRC2012_val_00031792.JPEG +ILSVRC2012_val_00017422.JPEG +ILSVRC2012_val_00007354.JPEG +ILSVRC2012_val_00046776.JPEG +ILSVRC2012_val_00014515.JPEG +ILSVRC2012_val_00031346.JPEG +ILSVRC2012_val_00014493.JPEG +ILSVRC2012_val_00002744.JPEG +ILSVRC2012_val_00011028.JPEG +ILSVRC2012_val_00014943.JPEG +ILSVRC2012_val_00010944.JPEG +ILSVRC2012_val_00045350.JPEG +ILSVRC2012_val_00010935.JPEG +ILSVRC2012_val_00010418.JPEG +ILSVRC2012_val_00014041.JPEG +ILSVRC2012_val_00033913.JPEG +ILSVRC2012_val_00030100.JPEG +ILSVRC2012_val_00042592.JPEG +ILSVRC2012_val_00045929.JPEG +ILSVRC2012_val_00039303.JPEG +ILSVRC2012_val_00048538.JPEG +ILSVRC2012_val_00016464.JPEG +ILSVRC2012_val_00008570.JPEG +ILSVRC2012_val_00013984.JPEG +ILSVRC2012_val_00044712.JPEG +ILSVRC2012_val_00006068.JPEG +ILSVRC2012_val_00000117.JPEG +ILSVRC2012_val_00020007.JPEG +ILSVRC2012_val_00024153.JPEG +ILSVRC2012_val_00021002.JPEG +ILSVRC2012_val_00023117.JPEG +ILSVRC2012_val_00048345.JPEG +ILSVRC2012_val_00032253.JPEG +ILSVRC2012_val_00011559.JPEG +ILSVRC2012_val_00020562.JPEG +ILSVRC2012_val_00032893.JPEG +ILSVRC2012_val_00010224.JPEG +ILSVRC2012_val_00038834.JPEG +ILSVRC2012_val_00032530.JPEG +ILSVRC2012_val_00004460.JPEG +ILSVRC2012_val_00042349.JPEG +ILSVRC2012_val_00004666.JPEG +ILSVRC2012_val_00016822.JPEG +ILSVRC2012_val_00034474.JPEG +ILSVRC2012_val_00006365.JPEG +ILSVRC2012_val_00023740.JPEG +ILSVRC2012_val_00003485.JPEG +ILSVRC2012_val_00043030.JPEG +ILSVRC2012_val_00040256.JPEG +ILSVRC2012_val_00002841.JPEG +ILSVRC2012_val_00017133.JPEG +ILSVRC2012_val_00036251.JPEG +ILSVRC2012_val_00041214.JPEG +ILSVRC2012_val_00025983.JPEG +ILSVRC2012_val_00026738.JPEG +ILSVRC2012_val_00041591.JPEG +ILSVRC2012_val_00044640.JPEG +ILSVRC2012_val_00004953.JPEG +ILSVRC2012_val_00037606.JPEG +ILSVRC2012_val_00009917.JPEG +ILSVRC2012_val_00016290.JPEG +ILSVRC2012_val_00030638.JPEG +ILSVRC2012_val_00022645.JPEG +ILSVRC2012_val_00046095.JPEG +ILSVRC2012_val_00047746.JPEG +ILSVRC2012_val_00042568.JPEG +ILSVRC2012_val_00027195.JPEG +ILSVRC2012_val_00017239.JPEG +ILSVRC2012_val_00038529.JPEG +ILSVRC2012_val_00001178.JPEG +ILSVRC2012_val_00009625.JPEG +ILSVRC2012_val_00045924.JPEG +ILSVRC2012_val_00028646.JPEG +ILSVRC2012_val_00033723.JPEG +ILSVRC2012_val_00034289.JPEG +ILSVRC2012_val_00031737.JPEG +ILSVRC2012_val_00042843.JPEG +ILSVRC2012_val_00025415.JPEG +ILSVRC2012_val_00036443.JPEG +ILSVRC2012_val_00022753.JPEG +ILSVRC2012_val_00019802.JPEG +ILSVRC2012_val_00027248.JPEG +ILSVRC2012_val_00042659.JPEG +ILSVRC2012_val_00029306.JPEG +ILSVRC2012_val_00039594.JPEG +ILSVRC2012_val_00003307.JPEG +ILSVRC2012_val_00017786.JPEG +ILSVRC2012_val_00028291.JPEG +ILSVRC2012_val_00029202.JPEG +ILSVRC2012_val_00034285.JPEG +ILSVRC2012_val_00028714.JPEG +ILSVRC2012_val_00001667.JPEG +ILSVRC2012_val_00021208.JPEG +ILSVRC2012_val_00013306.JPEG +ILSVRC2012_val_00040849.JPEG +ILSVRC2012_val_00029498.JPEG +ILSVRC2012_val_00019493.JPEG +ILSVRC2012_val_00018695.JPEG +ILSVRC2012_val_00016110.JPEG +ILSVRC2012_val_00049783.JPEG +ILSVRC2012_val_00043053.JPEG +ILSVRC2012_val_00037382.JPEG +ILSVRC2012_val_00040754.JPEG +ILSVRC2012_val_00007383.JPEG +ILSVRC2012_val_00011474.JPEG +ILSVRC2012_val_00029746.JPEG +ILSVRC2012_val_00039621.JPEG +ILSVRC2012_val_00012336.JPEG +ILSVRC2012_val_00001488.JPEG +ILSVRC2012_val_00041937.JPEG +ILSVRC2012_val_00042491.JPEG +ILSVRC2012_val_00036947.JPEG +ILSVRC2012_val_00019704.JPEG +ILSVRC2012_val_00007968.JPEG +ILSVRC2012_val_00020712.JPEG +ILSVRC2012_val_00003439.JPEG +ILSVRC2012_val_00002435.JPEG +ILSVRC2012_val_00013133.JPEG +ILSVRC2012_val_00038853.JPEG +ILSVRC2012_val_00029897.JPEG +ILSVRC2012_val_00020485.JPEG +ILSVRC2012_val_00034764.JPEG +ILSVRC2012_val_00007569.JPEG +ILSVRC2012_val_00022450.JPEG +ILSVRC2012_val_00026531.JPEG +ILSVRC2012_val_00037272.JPEG +ILSVRC2012_val_00022644.JPEG +ILSVRC2012_val_00048348.JPEG +ILSVRC2012_val_00040440.JPEG +ILSVRC2012_val_00003044.JPEG +ILSVRC2012_val_00014379.JPEG +ILSVRC2012_val_00016943.JPEG +ILSVRC2012_val_00046055.JPEG +ILSVRC2012_val_00014024.JPEG +ILSVRC2012_val_00016183.JPEG +ILSVRC2012_val_00029099.JPEG +ILSVRC2012_val_00006593.JPEG +ILSVRC2012_val_00042031.JPEG +ILSVRC2012_val_00017843.JPEG +ILSVRC2012_val_00003430.JPEG +ILSVRC2012_val_00031679.JPEG +ILSVRC2012_val_00011490.JPEG +ILSVRC2012_val_00036769.JPEG +ILSVRC2012_val_00010126.JPEG +ILSVRC2012_val_00046775.JPEG +ILSVRC2012_val_00018950.JPEG +ILSVRC2012_val_00015380.JPEG +ILSVRC2012_val_00019494.JPEG +ILSVRC2012_val_00027058.JPEG +ILSVRC2012_val_00039391.JPEG +ILSVRC2012_val_00013617.JPEG +ILSVRC2012_val_00026009.JPEG +ILSVRC2012_val_00020070.JPEG +ILSVRC2012_val_00010993.JPEG +ILSVRC2012_val_00014634.JPEG +ILSVRC2012_val_00015692.JPEG +ILSVRC2012_val_00024000.JPEG +ILSVRC2012_val_00013802.JPEG +ILSVRC2012_val_00047285.JPEG +ILSVRC2012_val_00048052.JPEG +ILSVRC2012_val_00033719.JPEG +ILSVRC2012_val_00045600.JPEG +ILSVRC2012_val_00046774.JPEG +ILSVRC2012_val_00020721.JPEG +ILSVRC2012_val_00029044.JPEG +ILSVRC2012_val_00022492.JPEG +ILSVRC2012_val_00031715.JPEG +ILSVRC2012_val_00031139.JPEG +ILSVRC2012_val_00026827.JPEG +ILSVRC2012_val_00030815.JPEG +ILSVRC2012_val_00013303.JPEG +ILSVRC2012_val_00040571.JPEG +ILSVRC2012_val_00031583.JPEG +ILSVRC2012_val_00024647.JPEG +ILSVRC2012_val_00028596.JPEG +ILSVRC2012_val_00035261.JPEG +ILSVRC2012_val_00018992.JPEG +ILSVRC2012_val_00034445.JPEG +ILSVRC2012_val_00036095.JPEG +ILSVRC2012_val_00023063.JPEG +ILSVRC2012_val_00005581.JPEG +ILSVRC2012_val_00046120.JPEG +ILSVRC2012_val_00035161.JPEG +ILSVRC2012_val_00032666.JPEG +ILSVRC2012_val_00038974.JPEG +ILSVRC2012_val_00015701.JPEG +ILSVRC2012_val_00019389.JPEG +ILSVRC2012_val_00037038.JPEG +ILSVRC2012_val_00010773.JPEG +ILSVRC2012_val_00009888.JPEG +ILSVRC2012_val_00031497.JPEG +ILSVRC2012_val_00000616.JPEG +ILSVRC2012_val_00045044.JPEG +ILSVRC2012_val_00034012.JPEG +ILSVRC2012_val_00012254.JPEG +ILSVRC2012_val_00044561.JPEG +ILSVRC2012_val_00017836.JPEG +ILSVRC2012_val_00049913.JPEG +ILSVRC2012_val_00037198.JPEG +ILSVRC2012_val_00018065.JPEG +ILSVRC2012_val_00048237.JPEG +ILSVRC2012_val_00011019.JPEG +ILSVRC2012_val_00005197.JPEG +ILSVRC2012_val_00037812.JPEG +ILSVRC2012_val_00026570.JPEG +ILSVRC2012_val_00005363.JPEG +ILSVRC2012_val_00021672.JPEG +ILSVRC2012_val_00028879.JPEG +ILSVRC2012_val_00016167.JPEG +ILSVRC2012_val_00004553.JPEG +ILSVRC2012_val_00031493.JPEG +ILSVRC2012_val_00021861.JPEG +ILSVRC2012_val_00010360.JPEG +ILSVRC2012_val_00037719.JPEG +ILSVRC2012_val_00016895.JPEG +ILSVRC2012_val_00025871.JPEG +ILSVRC2012_val_00048498.JPEG +ILSVRC2012_val_00036476.JPEG +ILSVRC2012_val_00008741.JPEG +ILSVRC2012_val_00010889.JPEG +ILSVRC2012_val_00016246.JPEG +ILSVRC2012_val_00031277.JPEG +ILSVRC2012_val_00027575.JPEG +ILSVRC2012_val_00000963.JPEG +ILSVRC2012_val_00011871.JPEG +ILSVRC2012_val_00048483.JPEG +ILSVRC2012_val_00044299.JPEG +ILSVRC2012_val_00039814.JPEG +ILSVRC2012_val_00025226.JPEG +ILSVRC2012_val_00033999.JPEG +ILSVRC2012_val_00010927.JPEG +ILSVRC2012_val_00022971.JPEG +ILSVRC2012_val_00029204.JPEG +ILSVRC2012_val_00007098.JPEG +ILSVRC2012_val_00048941.JPEG +ILSVRC2012_val_00022477.JPEG +ILSVRC2012_val_00014290.JPEG +ILSVRC2012_val_00010930.JPEG +ILSVRC2012_val_00005333.JPEG +ILSVRC2012_val_00016284.JPEG +ILSVRC2012_val_00021463.JPEG +ILSVRC2012_val_00011647.JPEG +ILSVRC2012_val_00005110.JPEG +ILSVRC2012_val_00039374.JPEG +ILSVRC2012_val_00007027.JPEG +ILSVRC2012_val_00015022.JPEG +ILSVRC2012_val_00030870.JPEG +ILSVRC2012_val_00036275.JPEG +ILSVRC2012_val_00049810.JPEG +ILSVRC2012_val_00029007.JPEG +ILSVRC2012_val_00047880.JPEG +ILSVRC2012_val_00029201.JPEG +ILSVRC2012_val_00002260.JPEG +ILSVRC2012_val_00004825.JPEG +ILSVRC2012_val_00049368.JPEG +ILSVRC2012_val_00011064.JPEG +ILSVRC2012_val_00019962.JPEG +ILSVRC2012_val_00020107.JPEG +ILSVRC2012_val_00034596.JPEG +ILSVRC2012_val_00031995.JPEG +ILSVRC2012_val_00026021.JPEG +ILSVRC2012_val_00022157.JPEG +ILSVRC2012_val_00033290.JPEG +ILSVRC2012_val_00028205.JPEG +ILSVRC2012_val_00026066.JPEG +ILSVRC2012_val_00032885.JPEG +ILSVRC2012_val_00023036.JPEG +ILSVRC2012_val_00038029.JPEG +ILSVRC2012_val_00006408.JPEG +ILSVRC2012_val_00008746.JPEG +ILSVRC2012_val_00025172.JPEG +ILSVRC2012_val_00036431.JPEG +ILSVRC2012_val_00024641.JPEG +ILSVRC2012_val_00040857.JPEG +ILSVRC2012_val_00015339.JPEG +ILSVRC2012_val_00013270.JPEG +ILSVRC2012_val_00023779.JPEG +ILSVRC2012_val_00043115.JPEG +ILSVRC2012_val_00022363.JPEG +ILSVRC2012_val_00006088.JPEG +ILSVRC2012_val_00043210.JPEG +ILSVRC2012_val_00015596.JPEG +ILSVRC2012_val_00006724.JPEG +ILSVRC2012_val_00013292.JPEG +ILSVRC2012_val_00024101.JPEG +ILSVRC2012_val_00013419.JPEG +ILSVRC2012_val_00040948.JPEG +ILSVRC2012_val_00029692.JPEG +ILSVRC2012_val_00039341.JPEG +ILSVRC2012_val_00003086.JPEG +ILSVRC2012_val_00007980.JPEG +ILSVRC2012_val_00017108.JPEG +ILSVRC2012_val_00018194.JPEG +ILSVRC2012_val_00034179.JPEG +ILSVRC2012_val_00010669.JPEG +ILSVRC2012_val_00046963.JPEG +ILSVRC2012_val_00039431.JPEG +ILSVRC2012_val_00017044.JPEG +ILSVRC2012_val_00025284.JPEG +ILSVRC2012_val_00031808.JPEG +ILSVRC2012_val_00039018.JPEG +ILSVRC2012_val_00040646.JPEG +ILSVRC2012_val_00015532.JPEG +ILSVRC2012_val_00043496.JPEG +ILSVRC2012_val_00018681.JPEG +ILSVRC2012_val_00002804.JPEG +ILSVRC2012_val_00014117.JPEG +ILSVRC2012_val_00033949.JPEG +ILSVRC2012_val_00043431.JPEG +ILSVRC2012_val_00021070.JPEG +ILSVRC2012_val_00039389.JPEG +ILSVRC2012_val_00020060.JPEG +ILSVRC2012_val_00013111.JPEG +ILSVRC2012_val_00039712.JPEG +ILSVRC2012_val_00037344.JPEG +ILSVRC2012_val_00026736.JPEG +ILSVRC2012_val_00048004.JPEG +ILSVRC2012_val_00039932.JPEG +ILSVRC2012_val_00004853.JPEG +ILSVRC2012_val_00026014.JPEG +ILSVRC2012_val_00003453.JPEG +ILSVRC2012_val_00003382.JPEG +ILSVRC2012_val_00016743.JPEG +ILSVRC2012_val_00042445.JPEG +ILSVRC2012_val_00047349.JPEG +ILSVRC2012_val_00030902.JPEG +ILSVRC2012_val_00004175.JPEG +ILSVRC2012_val_00032850.JPEG +ILSVRC2012_val_00005821.JPEG +ILSVRC2012_val_00020058.JPEG +ILSVRC2012_val_00023328.JPEG +ILSVRC2012_val_00040355.JPEG +ILSVRC2012_val_00001147.JPEG +ILSVRC2012_val_00037460.JPEG +ILSVRC2012_val_00042724.JPEG +ILSVRC2012_val_00011156.JPEG +ILSVRC2012_val_00004985.JPEG +ILSVRC2012_val_00035489.JPEG +ILSVRC2012_val_00018393.JPEG +ILSVRC2012_val_00014268.JPEG +ILSVRC2012_val_00036338.JPEG +ILSVRC2012_val_00034053.JPEG +ILSVRC2012_val_00013050.JPEG +ILSVRC2012_val_00048388.JPEG +ILSVRC2012_val_00004609.JPEG +ILSVRC2012_val_00004294.JPEG +ILSVRC2012_val_00024467.JPEG +ILSVRC2012_val_00031169.JPEG +ILSVRC2012_val_00001642.JPEG +ILSVRC2012_val_00042949.JPEG +ILSVRC2012_val_00039809.JPEG +ILSVRC2012_val_00031501.JPEG +ILSVRC2012_val_00025981.JPEG +ILSVRC2012_val_00017769.JPEG +ILSVRC2012_val_00027896.JPEG +ILSVRC2012_val_00022287.JPEG +ILSVRC2012_val_00022258.JPEG +ILSVRC2012_val_00040191.JPEG +ILSVRC2012_val_00012526.JPEG +ILSVRC2012_val_00034634.JPEG +ILSVRC2012_val_00040526.JPEG +ILSVRC2012_val_00016396.JPEG +ILSVRC2012_val_00048728.JPEG +ILSVRC2012_val_00034191.JPEG +ILSVRC2012_val_00036005.JPEG +ILSVRC2012_val_00034387.JPEG +ILSVRC2012_val_00032922.JPEG +ILSVRC2012_val_00012954.JPEG +ILSVRC2012_val_00004876.JPEG +ILSVRC2012_val_00044651.JPEG +ILSVRC2012_val_00037398.JPEG +ILSVRC2012_val_00010883.JPEG +ILSVRC2012_val_00026632.JPEG +ILSVRC2012_val_00020242.JPEG +ILSVRC2012_val_00001954.JPEG +ILSVRC2012_val_00005694.JPEG +ILSVRC2012_val_00013681.JPEG +ILSVRC2012_val_00014356.JPEG +ILSVRC2012_val_00033863.JPEG +ILSVRC2012_val_00035654.JPEG +ILSVRC2012_val_00019886.JPEG +ILSVRC2012_val_00047866.JPEG +ILSVRC2012_val_00021351.JPEG +ILSVRC2012_val_00045139.JPEG +ILSVRC2012_val_00023909.JPEG +ILSVRC2012_val_00010162.JPEG +ILSVRC2012_val_00049165.JPEG +ILSVRC2012_val_00020491.JPEG +ILSVRC2012_val_00031208.JPEG +ILSVRC2012_val_00044420.JPEG +ILSVRC2012_val_00023363.JPEG +ILSVRC2012_val_00003908.JPEG +ILSVRC2012_val_00018056.JPEG +ILSVRC2012_val_00036644.JPEG +ILSVRC2012_val_00000796.JPEG +ILSVRC2012_val_00011249.JPEG +ILSVRC2012_val_00012399.JPEG +ILSVRC2012_val_00001413.JPEG +ILSVRC2012_val_00022891.JPEG +ILSVRC2012_val_00049015.JPEG +ILSVRC2012_val_00047456.JPEG +ILSVRC2012_val_00011597.JPEG +ILSVRC2012_val_00015557.JPEG +ILSVRC2012_val_00027782.JPEG +ILSVRC2012_val_00037493.JPEG +ILSVRC2012_val_00000504.JPEG +ILSVRC2012_val_00021016.JPEG +ILSVRC2012_val_00002053.JPEG +ILSVRC2012_val_00027616.JPEG +ILSVRC2012_val_00045588.JPEG +ILSVRC2012_val_00044091.JPEG +ILSVRC2012_val_00039946.JPEG +ILSVRC2012_val_00011038.JPEG +ILSVRC2012_val_00019127.JPEG +ILSVRC2012_val_00023071.JPEG +ILSVRC2012_val_00040757.JPEG +ILSVRC2012_val_00020164.JPEG +ILSVRC2012_val_00013643.JPEG +ILSVRC2012_val_00030387.JPEG +ILSVRC2012_val_00018912.JPEG +ILSVRC2012_val_00035687.JPEG +ILSVRC2012_val_00040278.JPEG +ILSVRC2012_val_00009163.JPEG +ILSVRC2012_val_00023857.JPEG +ILSVRC2012_val_00037991.JPEG +ILSVRC2012_val_00025057.JPEG +ILSVRC2012_val_00028908.JPEG +ILSVRC2012_val_00010737.JPEG +ILSVRC2012_val_00015519.JPEG +ILSVRC2012_val_00010983.JPEG +ILSVRC2012_val_00013517.JPEG +ILSVRC2012_val_00033350.JPEG +ILSVRC2012_val_00010611.JPEG +ILSVRC2012_val_00031130.JPEG +ILSVRC2012_val_00024581.JPEG +ILSVRC2012_val_00023266.JPEG +ILSVRC2012_val_00023056.JPEG +ILSVRC2012_val_00031333.JPEG +ILSVRC2012_val_00026264.JPEG +ILSVRC2012_val_00037355.JPEG +ILSVRC2012_val_00015767.JPEG \ No newline at end of file diff --git a/big_vision/evaluators/proj/uvim/common.py b/big_vision/evaluators/proj/uvim/common.py new file mode 100644 index 0000000000000000000000000000000000000000..937fde88ea416c87346e2dd9dfbd3de8940e6867 --- /dev/null +++ b/big_vision/evaluators/proj/uvim/common.py @@ -0,0 +1,64 @@ +# Copyright 2022 Big Vision 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. + +"""Common utilities used in evaluators.""" +import math +import jax +import tensorflow as tf +import tensorflow_datasets as tfds + + +def get_jax_process_dataset(dataset, split, global_batch_size, pp_fn, + dataset_dir=None, cache=True, add_tfds_id=False): + """Returns dataset to be processed by current jax host. + + The dataset is sharded and padded with zeros such that all processes + have equal number of batches. The first 2 dimensions of the dataset + elements are: [local_device_count, device_batch_size]. + + Args: + dataset: dataset name. + split: dataset split. + global_batch_size: batch size to be process per iteration on the dataset. + pp_fn: preprocessing function to apply per example. + dataset_dir: path for tfds to find the prepared data. + cache: whether to cache the dataset after batching. + add_tfds_id: whether to add the unique `tfds_id` string to each example. + """ + assert global_batch_size % jax.device_count() == 0 + total_examples = tfds.load( + dataset, split=split, data_dir=dataset_dir).cardinality() + num_batches = math.ceil(total_examples / global_batch_size) + + process_split = tfds.even_splits( + split, n=jax.process_count(), drop_remainder=False)[jax.process_index()] + data = tfds.load( + dataset, + split=process_split, + data_dir=dataset_dir, + read_config=tfds.ReadConfig(add_tfds_id=add_tfds_id)).map(pp_fn) + pad_data = tf.data.Dataset.from_tensors( + jax.tree_map(lambda x: tf.zeros(x.shape, x.dtype), data.element_spec) + ).repeat() + + data = data.concatenate(pad_data) + data = data.batch(global_batch_size // jax.device_count()) + data = data.batch(jax.local_device_count()) + data = data.take(num_batches) + if cache: + # Eval datasets are often used many times and caching the dataset after + # batching allows one to have the buffers ready to be used and not have + # to wait for preprocessing to be done over and over. + data = data.cache() + return data diff --git a/big_vision/evaluators/proj/uvim/compute_mean.py b/big_vision/evaluators/proj/uvim/compute_mean.py new file mode 100644 index 0000000000000000000000000000000000000000..86bceac51b5a97231cff86ab4344331d67efe35a --- /dev/null +++ b/big_vision/evaluators/proj/uvim/compute_mean.py @@ -0,0 +1,79 @@ +# Copyright 2022 Big Vision 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. + +"""Evaluator for computing mean of per-example metrics.""" +import functools +from typing import Mapping + +from big_vision import input_pipeline +from big_vision.datasets import core as ds_core +from big_vision.pp import builder as pp_builder + +import jax +import jax.numpy as jnp +import numpy as np + + +# Note: global to avoid jax re-compiling across different evaluator instances. +@functools.partial(jax.pmap, static_broadcasted_argnums=0, axis_name='batch') +def _run_predict_fn(predict_fn, params, batch): + """Sum per-example metrics weighted by `_mask`.""" + mask = batch['_mask'] + metrics = predict_fn(params, batch) + # Sanity check output format of predict_fn. + assert isinstance(metrics, Mapping), 'predict_fn must return a dict' + for y in jax.tree_leaves(metrics): + if y.shape != mask.shape: + raise ValueError( + f'Expected per-example metrics of shape {mask.shape} found ' + f'{jax.tree_map(lambda x: x.shape, metrics)}.') + metrics = {**metrics, '_mask': mask} + metrics = jax.tree_map(lambda x: jnp.inner(x, mask), metrics) + return jax.lax.psum(metrics, axis_name='batch') + + +class Evaluator: + """Report the mean of per-example metrics computed by predict_fn. + + `predict_fn(params, batch)` must return a dict from metric name to + per-example metrics of shape [batch_size]. + """ + + def __init__(self, predict_fn, data, pp_fn, batch_size, + cache_final=True, cache_raw=False, prefetch=1): + data = ds_core.get(**data) + self.dataset, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), batch_size=batch_size, + num_ex_per_process=data.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn), + cache_final=cache_final, cache_raw=cache_raw) + self.data_iter = input_pipeline.start_input_pipeline(self.dataset, prefetch) + self.predict_fn = predict_fn + + def run(self, params): + """Computes all metrics.""" + metrics = [] + + # Compute batch metrics without blocking. + for _, batch in zip(range(self.steps), self.data_iter): + batch_metrics = _run_predict_fn(self.predict_fn, params, batch) + metrics.append(batch_metrics) + + # Transfer metrics from device 0 to host (blocking). + metrics = jax.device_get(jax.tree_map(lambda x: x[0], metrics)) + + metrics_sum = jax.tree_map(lambda *x: np.sum(x), *metrics) + mask_sum = metrics_sum.pop('_mask') + for key, value_sum in metrics_sum.items(): + yield (key, value_sum / mask_sum) diff --git a/big_vision/evaluators/proj/uvim/nyu_depth.py b/big_vision/evaluators/proj/uvim/nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..ea5d3e706d9ae50f8395930b7ddd033c1fc120e2 --- /dev/null +++ b/big_vision/evaluators/proj/uvim/nyu_depth.py @@ -0,0 +1,154 @@ +# Copyright 2022 Big Vision 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. + +"""Evaluation for NYU depth. + +At evaluation time the ground truth is cropped and clipped. Values outside of +the test crop or clipping range are not included in eval calculations. + +In this evaluator, it is assume that the groud truth is already cropped, so the +entire image is evaluated. However, the evaluator does perform the clipping. + +Reference implementations: + https://github.com/zhyever/Monocular-Depth-Estimation-Toolbox/blo(internal link)a0f341244260ff61541191a613dd74bc/depth/datasets/nyu.py + https://github.com/vinvino02/GLPDepth/blob/7f3c78df4ecd6e7c79fd0c4b73c95d61f4aa2121/code/utils/metrics.py + https://github.com/shariqfarooq123/AdaBins/blob/2fb686a66a304f0a719bc53d77412460af97fd61/evaluate.py +""" + +import functools + +import big_vision.evaluators.proj.uvim.common as common +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +import numpy as np +import tensorflow as tf + +EVAL_CROP_H = 426 +EVAL_CROP_W = 560 + + +class Evaluator: + """Evaluator for NYU depth.""" + + def __init__(self, + predict_fn, + pp_fn, + batch_size, + dataset, + split, + min_depth=1e-3, + max_depth=10, + dataset_dir=None, + predict_kwargs=None): + self.min_depth = min_depth + self.max_depth = max_depth + + def predict(params, batch): + pred = predict_fn(params, batch, **(predict_kwargs or {})) + + return jax.lax.all_gather({ + "mask": batch["mask"], + "gt": jnp.squeeze(batch["ground_truth"], axis=-1), + "y": pred["depth"], + }, axis_name="data", axis=0) + + self.predict_fn = jax.pmap(predict, axis_name="data") + + # Prepare data for each process and pad with zeros so all processes have the + # same number of batches. + def preprocess(example): + return { + "mask": tf.constant(1), + **pp_builder.get_preprocess_fn(pp_fn)(example), + } + + self.process_batch_size = batch_size // jax.process_count() + + self.data = common.get_jax_process_dataset( + dataset=dataset, + dataset_dir=dataset_dir, + split=split, + global_batch_size=batch_size, + pp_fn=preprocess) + + def run(self, params): + """Run eval.""" + # Assumes that the ground truth is processed by the eval crop. + eval_mask = np.ones((EVAL_CROP_H, EVAL_CROP_W), dtype=np.bool_) + rmses = [] + abs_res = [] + abs_logs = [] + d1s = [] + d2s = [] + d3s = [] + for batch in self.data.as_numpy_iterator(): + # Outputs is a dict with values shaped (gather/same, devices, batch, ...) + out = self.predict_fn(params, batch) + + if jax.process_index(): # Host0 gets all preds and does eval. + continue + + # First, we remove the "gather" dim and transfer the result to host, + # leading to numpy arrays of (devices, device_batch, ...) + out = jax.tree_map(lambda x: jax.device_get(x[0]), out) + # Then the bool-indexing with mask resulting in flat (global_batch, ...) + out = jax.tree_map(lambda x: x[out["mask"] == 1], out) # pylint:disable=cell-var-from-loop + + for gt, pred in zip(out["gt"], out["y"]): + pred = _resize_nearest(pred, (EVAL_CROP_H, EVAL_CROP_W)) + valid_mask = np.logical_and(gt > self.min_depth, gt < self.max_depth) + valid_mask = np.logical_and(valid_mask, eval_mask) + + rmses.append(_compute_rmse(gt[valid_mask], pred[valid_mask])) + abs_res.append(_compute_abs_re(gt[valid_mask], pred[valid_mask])) + abs_logs.append(_compute_abs_log(gt[valid_mask], pred[valid_mask])) + d1s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=1)) + d2s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=2)) + d3s.append(_compute_delta(gt[valid_mask], pred[valid_mask], order=3)) + + if jax.process_index(): # Host0 gets all preds and does eval. + return + + yield "RMSE", np.mean(rmses) + yield "abs_RE", np.mean(abs_res) + yield "log10", np.mean(abs_logs) + yield "delta1", np.mean(d1s) + yield "delta2", np.mean(d2s) + yield "delta3", np.mean(d3s) + + +@functools.partial(jax.jit, static_argnums=(1,), backend="cpu") +def _resize_nearest(image, shape): + return jax.image.resize(image, shape, "nearest") + + +def _compute_rmse(gt, pred): + diff = gt - pred + return np.sqrt(np.mean(np.power(diff, 2))) + + +def _compute_abs_re(gt, pred): + diff = np.abs(gt - pred) + return np.mean(diff / gt) + + +def _compute_abs_log(gt, pred): + diff = np.abs(np.log10(gt) - np.log10(pred)) + return np.mean(diff) + + +def _compute_delta(gt, pred, order): + rel_diff = np.maximum(gt / pred, pred / gt) + return np.sum(rel_diff < 1.25**order) / rel_diff.size diff --git a/big_vision/evaluators/proj/uvim/psnr.py b/big_vision/evaluators/proj/uvim/psnr.py new file mode 100644 index 0000000000000000000000000000000000000000..dc9404b86b4d38077182063bebb8f7a37f5a4c73 --- /dev/null +++ b/big_vision/evaluators/proj/uvim/psnr.py @@ -0,0 +1,100 @@ +# Copyright 2022 Big Vision 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. + +"""Compute PSNR, currently used for colorization and superresolution.""" + +import functools + +import big_vision.evaluators.proj.uvim.common as common +import big_vision.pp.builder as pp_builder +import jax +import jax.numpy as jnp +import numpy as np +import tensorflow as tf + + +class Evaluator: + """PSNR evaluator. + + `predict_fn` accepts arbitrary dictionaries of parameters and data, where + the data dictionary is produced by the `pp_fn` op. It is expected to output a + single-key dict containing an RGB image with intensities in [-1,1]. + """ + + def __init__(self, + predict_fn, + pp_fn, + batch_size, + dataset="imagenet2012", + split="validation", + predict_kwargs=None): + + def predict(params, batch): + + def _f(x): + y = predict_fn(params, x, **(predict_kwargs or {})) + # Assume image intensities are in [-1,1]. + # Evaluator expects a dict with a single item. + pred, = y.values() + return _psnr(pred, x["labels"], 2.) + return jax.lax.all_gather({ + "mask": batch["mask"], + "psnr": _f(batch["input"]), + }, axis_name="data", axis=0) + + self.predict_fn = jax.pmap(predict, axis_name="data") + + # Prepare data for each process and pad with zeros so all processes have the + # same number of batches. + def preprocess(example): + return { + "mask": tf.constant(1), + "input": pp_builder.get_preprocess_fn(pp_fn)(example), + } + + self.data = common.get_jax_process_dataset( + dataset, + split, + global_batch_size=batch_size, + add_tfds_id=True, + pp_fn=preprocess) + + def run(self, params): + """Run eval.""" + psnrs = [] + + for batch in self.data.as_numpy_iterator(): + # Outputs is a dict with values shaped (gather/same, devices, batch, ...) + out = self.predict_fn(params, batch) + + if jax.process_index(): # Host0 gets all preds and does eval. + continue + + # First, we remove the "gather" dim and transfer the result to host, + # leading to numpy arrays of (devices, device_batch, ...) + out = jax.tree_map(lambda x: jax.device_get(x[0]), out) + mask = out["mask"] + batch_psnrs = out["psnr"][mask != 0] + psnrs.extend(batch_psnrs) + + if jax.process_index(): # Host0 gets all preds and does eval. + return + + yield "PSNR", np.mean(psnrs) + + +@functools.partial(jax.vmap, in_axes=[0, 0, None]) +def _psnr(img0, img1, dynamic_range): + mse = jnp.mean(jnp.power(img0 - img1, 2)) + return 20. * jnp.log10(dynamic_range) - 10. * jnp.log10(mse) diff --git a/big_vision/evaluators/proj/uvim/save_predictions.py b/big_vision/evaluators/proj/uvim/save_predictions.py new file mode 100644 index 0000000000000000000000000000000000000000..26bb75cf2a34c09c85e8018a0dd0c9c4de102cdd --- /dev/null +++ b/big_vision/evaluators/proj/uvim/save_predictions.py @@ -0,0 +1,95 @@ +# Copyright 2022 Big Vision 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. + +"""Evaluator to save predictions.""" +# pylint: disable=consider-using-from-import +import os + +from absl import flags +from absl import logging +import big_vision.evaluators.proj.uvim.common as common +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +import jax +import numpy as np +import tensorflow as tf + + +class Evaluator: + """Save predictions in "{FLAGS.workdir}/{outfile}". + + Results can then be easily inspected in a notebook such as: + + ``` + results = utils.load_checkpoint(None, "") + inputs, outputs = (results["inputs"], results["outputs"]) + ``` + """ + + def __init__(self, predict_fn, pp_fn, dataset, split, batch_size, outfile, + predict_kwargs=None, dataset_dir=None): + # Prepare to run predict on all processes and gather predictions on all + # devices. Note: if needed consider only gather across processes. + def predict(params, batch): + y = predict_fn(params, batch['inputs'], **(predict_kwargs or {})) + res = {'inputs': batch['inputs'], 'outputs': y, 'mask': batch['mask']} + return jax.lax.all_gather(res, axis_name='data', axis=0, tiled=True) + + self.predict_fn = jax.pmap(predict, axis_name='data') + + # Prepare data for each process and pad with zeros so all processes have the + # same number of batches. + def preprocess(example): + return { + 'mask': tf.constant(1), + 'inputs': pp_builder.get_preprocess_fn(pp_fn)(example), + } + self.data = common.get_jax_process_dataset( + dataset=dataset, split=split, + dataset_dir=dataset_dir, + global_batch_size=batch_size, + pp_fn=preprocess) + + self.path = os.path.join(flags.FLAGS.workdir, outfile) + + def run(self, params): + """Compute all predictions, gather in main host and save in outfile.""" + count = 0 + outputs = [] + for batch in self.data.as_numpy_iterator(): + out = self.predict_fn(params, batch) + if jax.process_index(): + continue + + out = jax.device_get(jax.tree_map(lambda x: x[0], out)) + out = jax.tree_map(lambda x: x[out['mask'] == 1], out) # pylint: disable=cell-var-from-loop + count += out['mask'].shape[0] + out.pop('mask') + outputs.append(out) + + logging.log_every_n_seconds( + logging.INFO, 'Save predictions: processed %i examples so far.', 30, + count) + + if jax.process_index(): + return + + logging.info('Save predictions: processed %d examples.', count) + + # Actually save in filesystem. + outputs = jax.tree_map(lambda *x: np.concatenate(x, axis=0), *outputs) + u.save_checkpoint(outputs, self.path, compressed=True) + return + + yield None # pylint: disable=unreachable diff --git a/big_vision/evaluators/save.py b/big_vision/evaluators/save.py new file mode 100644 index 0000000000000000000000000000000000000000..49bcfc59b9fd9c613611b1edcbd157b2d8c2d6d5 --- /dev/null +++ b/big_vision/evaluators/save.py @@ -0,0 +1,121 @@ +# Copyright 2024 Big Vision 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. + +"""Evaluator that save inputs and outputs of prediction functions.""" +import functools + +from absl import flags +from absl import logging + +from big_vision import input_pipeline +from big_vision import optax as bv_optax +from big_vision import utils +from big_vision.datasets import core as ds_core +from big_vision.pp import builder as pp_builder + +import jax +import numpy as np + +# Temporary global flag to facilitate backwards compatability. Will be removed +# by the end of year 2023. +API = 'jit' + + +# Note: global to avoid jax re-compiling across different evaluator instances. +def _run_predict_fn(predict_fn, train_state, batch): + """Run predict_fn and gather all outputs on all devices.""" + y = predict_fn(train_state, batch) + return {'inputs': batch, 'outputs': y} + + +class Evaluator: + """Evaluator that saves the inputs and outputs of a prediction function. + + Example configuration: + + ``` + config.evals.save_pred = { + 'type': 'save', + 'pred': 'inference', + 'outfile': '{workdir}/inference-{step:09d}.npz', + 'data': ..., 'pp_fn': ..., 'log_steps': ..., + } + ``` + + Results can then be easily inspected in a notebook such as: + + ``` + results = utils.load_checkpoint("") + inputs, outputs = (results["inputs"], results["outputs"]) + ``` + """ + + def __init__(self, predict_fn, data, pp_fn, batch_size, outfile, + cache_final=True, cache_raw=False, prefetch=1, *, devices): + replicate = jax.sharding.NamedSharding( + jax.sharding.Mesh(devices, ('devices',)), + jax.sharding.PartitionSpec() + ) + self.predict_fn = functools.partial( + jax.jit(_run_predict_fn, static_argnums=0, out_shardings=replicate), + predict_fn, + ) + + data = ds_core.get(**data) + self.dataset, self.steps = input_pipeline.make_for_inference( + data.get_tfdata(ordered=True), + batch_size=batch_size, + num_ex_per_process=data.num_examples_per_process(), + preprocess_fn=pp_builder.get_preprocess_fn(pp_fn), + cache_final=cache_final, + cache_raw=cache_raw, + ) + self.data_iter = input_pipeline.start_global( + self.dataset, devices, prefetch + ) + + self.outfile = outfile + + def run(self, train_state): + """Compute all predictions, gather in main host and save in outfile.""" + step = jax.device_get(bv_optax.get_count(train_state['opt'], jittable=True)) + outfile = self.outfile.format(workdir=flags.FLAGS.workdir, step=step) + + count = 0 + outputs = [] + for _, batch in zip(range(self.steps), self.data_iter): + out = self.predict_fn(train_state, batch) + if jax.process_index(): + continue + + out = jax.device_get(out) + mask = out['inputs']['_mask'] + out = jax.tree.map(lambda x: x[mask == 1], out) # pylint: disable=cell-var-from-loop + count += mask.shape[0] + out['inputs'].pop('_mask') + outputs.append(out) + + logging.log_every_n_seconds( + logging.INFO, 'Processed %i examples so far.', 60, + count) + + if jax.process_index(): + return + + logging.info('Saving %d examples in %s', count, outfile) + outputs = jax.tree.map(lambda *x: np.concatenate(x, axis=0), *outputs) + utils.save_checkpoint(outputs, outfile, compressed=True) + return + + yield None # pylint: disable=unreachable diff --git a/big_vision/input_pipeline.py b/big_vision/input_pipeline.py new file mode 100644 index 0000000000000000000000000000000000000000..742e09eeb9f974ad11cc6d9e92c9a25fb394bb7d --- /dev/null +++ b/big_vision/input_pipeline.py @@ -0,0 +1,346 @@ +# Copyright 2024 Big Vision 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. + +"""ImageNet input pipeline.""" +import collections +import functools +import itertools +import math +import multiprocessing.pool + +from absl import logging +from big_vision.datasets import sequence_packing +import big_vision.datasets.core as ds_core +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +import einops +import jax +import numpy as np +import tensorflow as tf + + +DEFAULT_NUM_PARALLEL_CALLS = 100 + + +def make_for_train( + data, preprocess_fn, batch_size, + shuffle_buffer_size=None, cache_raw=False, + num_parallel_calls=DEFAULT_NUM_PARALLEL_CALLS, prefetch=2, + *, + pre_filter_fn=None, post_filter_fn=None, + pack=None, skip_errors=False, +): + """Makes an input pipeline for training.""" + # Use data filtering at your own risk: the actual split sizes won't be known + # in advance, so epoch-based things won't work correctly. + + data = _add_tpu_host_options(data) + + data = data.filter(pre_filter_fn) if pre_filter_fn else data + data = data.cache() if cache_raw else data + + # First shuffle and then repeat (each with a different shuffle). This way + # the data for one epoch is all seen before the next one is processed and + # significantly affects the number of times each example is seen when + # processing for small number of epochs. + if shuffle_buffer_size: + data = data.shuffle(shuffle_buffer_size, reshuffle_each_iteration=True) + data = data.repeat(None) + + data = data.map(preprocess_fn, num_parallel_calls=num_parallel_calls) + data = data.filter(post_filter_fn) if post_filter_fn else data + + data = data.ignore_errors(log_warning=True) if skip_errors else data + + data = sequence_packing.pack_dataset(data, pack) if pack else data + + # Drop remainder makes shape fully static, so we can later use it if needed. + if batch_size: + data = data.batch(batch_size // jax.process_count(), drop_remainder=True) + if prefetch: # None means autotune, but we never want that. + data = data.prefetch(prefetch) + return data + + +def training(input_config): + """Reads the data from a single dataset, or mixes it from multiple. + + The data is read either from one or mixed from multiple datasets, depending + on the `input_config`. + + Args: + input_config: Configures the input pipeline. See input_pipeline_test for + examples. + + Returns: + A tuple containing (possibly mixed) tf.data.Dataset and a total number of + training examples. + """ + per_pipeline_configs = ( + "shuffle_buffer_size", "cache_raw", "num_parallel_calls", + "pre_filter_fn", "post_filter_fn", "pack", "skip_errors") + def config_to_kw(config): + assert "filter_fn" not in config, "Deprecated; use `pre_filter_fn` instead." + return {k: config[k] for k in per_pipeline_configs if k in config} + + batch_size = input_config.batch_size + # Handle separately the common case when no mixing happens. + if isinstance(input_config.data.get("name"), str): + train_data = ds_core.get(**input_config.data) + train_ds = make_for_train( + data=train_data.get_tfdata(ordered=False), + batch_size=batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(input_config.get("pp")), + prefetch=input_config.get("prefetch", 2), # Default 2 for bwd compat. + **config_to_kw(input_config) + ) + return train_ds, train_data.total_examples + + # A helpful error instead of silent ignore: + for k in per_pipeline_configs: + assert k not in input_config, f"{k} is per-dataset in multi-input." + + # Parallelize the loading of datasets when doing data mixture. + # For larger mixes, we sometimes spend >5min when doing sequentially. + # NOTE: functools.cache is thread-safe. + def _make(name_and_weight): + name, weight = name_and_weight + dataset = input_config[name] + train_data = ds_core.get(**dataset.data) + dataset = make_for_train( + data=train_data.get_tfdata(ordered=False), + # Don't batch the data just yet, it will be done after + # mixing the different datasets below. + batch_size=None, + preprocess_fn=pp_builder.get_preprocess_fn(dataset.get("pp"), name), + prefetch=0, # Prefetching each pipeline leads to huge OOMs. + **config_to_kw(dataset) + ) + if keys := input_config.get("keep_only"): + dataset = dataset.map(lambda d, keys=keys: {k: d[k] for k in keys}) + return name, dataset, weight, train_data.total_examples + + names, datasets, weights, totals = [], [], [], [] + pool = multiprocessing.pool.ThreadPool(len(input_config.data)) + for name, dataset, weight, total in pool.map( + # Skip weight=0 datasets as a convenient optimization in sweeps. + _make, ((name, w) for name, w in input_config.data.items() if w)): + names.append(name) + datasets.append(dataset) + weights.append(weight) + totals.append(total) + + # Normalize the weights such that they sum up to 1. + weights = [x / sum(weights) for x in weights] + + logging.info( + "NOTE: Total dataset mix size: %d\nContributions:\n%s", sum(totals), + "\n".join(f"{ds}: {n} ({w * 100:.1g}%)" + for ds, n, w in zip(names, totals, weights)) + ) + + train_ds = tf.data.Dataset.sample_from_datasets( + datasets, weights, stop_on_empty_dataset=True) + if input_config.get("pack"): + train_ds = sequence_packing.pack_dataset(train_ds, input_config.get("pack")) + train_ds = train_ds.batch( + input_config["batch_size"] // jax.process_count(), drop_remainder=True) + if (pf := input_config.get("prefetch", 2)): + train_ds = train_ds.prefetch(pf) + + return train_ds, sum(totals) + + +# The pipeline below is used for evals in multi-{G,T}PU and multi-host settings. +# As the total number of examples may not be evenly divisible accross all +# devices, we use the `infinite tf.data padding` trick, which was suggested by +# Andreas Steiner and also implemented by him in the clu library: +# https://github.com/google/CommonLoopUtils/blob/84b777c42dfd3fb6685537138433bfeb5241a006/clu/deterministic_data.py#L304. +def make_for_inference( + data, preprocess_fn, batch_size, num_ex_per_process, + cache_raw=False, cache_final=False, + num_parallel_calls=DEFAULT_NUM_PARALLEL_CALLS, prefetch=1, +): + """Makes an input pipeline for inference.""" + + data = _add_tpu_host_options(data) + data = data.cache() if cache_raw else data + data = data.map(_add_internal_fields(preprocess_fn), + num_parallel_calls=num_parallel_calls) + data = data.concatenate(_get_pad_data(data)) + + local_batch_size = batch_size // jax.process_count() + # This is just like `batch`, but allows batching elements of different shapes + # into a tf.RaggedTensor. Elements of the same fixed shape remain tf.Tensors. + # Since we do 'infinite' padding it is safe to drop the remainder. + data = data.ragged_batch(batch_size=local_batch_size, drop_remainder=True) + + # We need to make sure that all hosts process all data and exactly the same + # number of batches. Below we take max per-host num examples and use it on all + # hosts to derive the number of batches. + num_batches = math.ceil(max(num_ex_per_process) / local_batch_size) + data = data.take(num_batches) + + # Note we cache data after a finite number of batches is taken. + data = data.cache() if cache_final else data + data = data.repeat() + data = data.prefetch(prefetch) if prefetch else data + return data, num_batches + + +def _get_pad_data(data): + def zeros_like_spec(spec): + # For unknown/flexible dimensions (None), just use 0 instead. + return tf.zeros([x or 0 for x in spec.shape], spec.dtype) + + zero = jax.tree.map(zeros_like_spec, data.element_spec) + return tf.data.Dataset.from_tensors(zero).repeat() + + +def _add_internal_fields(pp_fn): + """Wraps pp_fn to add _mask and _id keys.""" + # Adds internal keys, that we either, in this order of preference: + # 1. keep from result of pp_fn, + # 2. carry over from raw (not pp_fn'd) example, or + # 3. add, if that makes sense. + def _pp_fn(example): + result = pp_fn(example) + # _mask will be False on padded examples (see _get_pad_data). + result.setdefault("_mask", example.get("_mask", tf.constant(True))) + # Not all data-sources can provide an ID. Only carry-over if it can: + if "_id" in example and "_id" not in result: + result["_id"] = example["_id"] + return result + return _pp_fn + + +def _add_tpu_host_options(data): + options = tf.data.Options() + options.threading.private_threadpool_size = 48 + options.threading.max_intra_op_parallelism = 1 + + # Stop a whole bunch of magic stuff that eats up all RAM: + options.experimental_optimization.inject_prefetch = False + + return data.with_options(options) + + +def prefetch_iterator(it, n): + """Runs iterator `it` ahead for `n` steps. Adapted from flax.""" + if not n: + yield from it + return + queue = collections.deque() + + def enqueue(n_steps): # Enqueues *up to* `n` elements from the iterator. + for data in itertools.islice(it, n_steps): + # Prefetching will parallelize any processing that happens in a different + # thread (like `jax.device_put()`), but it will be of no use for + # processing that happens in the same thread. + queue.append(data) + + enqueue(n) # Fill up the buffer. + while queue: + yield queue.popleft() + enqueue(1) + + +def threadstart_iterator(it): + """Starts an iterator right away in a background thread.""" + # We already want to "start" the iterator in order to start the underlying + # dataset prefetch mechanisms, so here we get the first element. But we don't + # want to lose it from training, so we yield that one afterwards. + # (internal link) + pool = multiprocessing.pool.ThreadPool(processes=1) + first_ex_promise = pool.apply_async(lambda: next(it)) + + yield first_ex_promise.get() + yield from it + + +def tf_to_numpy(x): + """Convert any TF types to numpy.""" + if isinstance(x, tf.Tensor): + if x.dtype != tf.string: # Dense, non-string tensor? Easy! + return x.numpy() + else: # A dense string tensor? Turn into actual strings, not bytes. + return np.vectorize(bytes.decode, otypes=[str])(x.numpy()) + + # The rest deals with RaggedTensors, for two main reasons: + # - For strings, recursively apply the above conversion + # - For common cases (eg batch of images), return more reasonable shapes. + + # Replace all None's in the shape by a fixed number, in the (somewhat common) + # case that they are marked ragged, but really all have the same shape. + real_shape = list(x.shape) + for i, s in enumerate(real_shape[1:]): + if s is not None: continue + rowlens = np.diff(x.nested_row_splits[i]) + if len(set(rowlens)) == 1: + real_shape[i + 1] = rowlens[0] + + if None not in real_shape: + return tf_to_numpy(x.flat_values).reshape(real_shape) + + # It's actually ragged, reconstruct the array from the variable length pieces. + splits = x.row_splits.numpy() + rows = [tf_to_numpy(x.values[splits[i]:splits[i + 1]]) + for i in range(len(splits) - 1)] + return np.fromiter(rows, dtype=object) + + +# Note that the order of global devices for sharding data is important and +# should be compatible with device order used for models params, state, etc. +def start_global( + data, global_devices, n_prefetch=1, keep_on_cpu=frozenset(), warmup=False): + """Starts the global input pipeline.""" + def maybe_shard(name, x): + if name in keep_on_cpu: + return tf_to_numpy(x) + return u.make_fsarray_from_local_slice(x, global_devices) + + it = iter(data) + if warmup: # actually pre-fill shuffle buffers etc. + it = threadstart_iterator(it) + + it = (u.tree_map_with_names(maybe_shard, elem) for elem in it) + return prefetch_iterator(it, n_prefetch) + + +########################################################################## +# The code below is pmap-specific and is deprecated, please switch to jit. +########################################################################## + + +def shard_and_put(x, shard=True, put=True): + x = np.asarray(memoryview(x)) # No-copy conversion: http://(internal link) + if shard: + x = einops.rearrange(x, "(d l) ... -> d l ...", d=jax.local_device_count()) + if shard and put: # Only works for pmap (for now). + x = jax.device_put_sharded(list(x), jax.local_devices()) + return x + + +def start_input_pipeline(data, n_prefetch=1, shard=True): + fn = functools.partial(shard_and_put, shard=shard, put=n_prefetch) + it = (jax.tree.map(fn, elem) for elem in iter(data)) + return prefetch_iterator(it, n_prefetch) + + +def start_ragged_input_pipeline(data, n_prefetch=1, shard=True, ragged=None): + def maybe_shard_and_put(name, x): + return x if name in (ragged or {}) else shard_and_put(x, shard) + + it = (u.tree_map_with_names(maybe_shard_and_put, elem) for elem in iter(data)) + return prefetch_iterator(it, n_prefetch) diff --git a/big_vision/models/__init__.py b/big_vision/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/models/__pycache__/__init__.cpython-310.pyc b/big_vision/models/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..603ea115ab9b47e6c081a2c0c9467c3628afdb90 Binary files /dev/null and b/big_vision/models/__pycache__/__init__.cpython-310.pyc differ diff --git a/big_vision/models/__pycache__/common.cpython-310.pyc b/big_vision/models/__pycache__/common.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9480224c591001a2758b2e3250169385e9199d45 Binary files /dev/null and b/big_vision/models/__pycache__/common.cpython-310.pyc differ diff --git a/big_vision/models/__pycache__/vit.cpython-310.pyc b/big_vision/models/__pycache__/vit.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5004d0b5784f7011c901383a5a15959bff98576 Binary files /dev/null and b/big_vision/models/__pycache__/vit.cpython-310.pyc differ diff --git a/big_vision/models/bit.py b/big_vision/models/bit.py new file mode 100644 index 0000000000000000000000000000000000000000..cd4235df9ec87549590396deb69739e310e6770d --- /dev/null +++ b/big_vision/models/bit.py @@ -0,0 +1,162 @@ +# Copyright 2024 Big Vision 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. + +"""ResNet V1 with GroupNorm.""" + +from typing import Optional, Sequence, Union + +from big_vision import utils +from big_vision.models import common +import flax +import flax.linen as nn +import flax.training.checkpoints +import jax.numpy as jnp +import numpy as np + + +def weight_standardize(w, axis, eps): + w = w - jnp.mean(w, axis=axis) + w = w / (jnp.std(w, axis=axis) + eps) + return w + + +class StdConv(nn.Conv): + + def param(self, name, *a, **kw): + param = super().param(name, *a, **kw) + if name == "kernel": + param = weight_standardize(param, axis=[0, 1, 2], eps=1e-5) + return param + + +class ResidualUnit(nn.Module): + """Bottleneck ResNet block.""" + nmid: Optional[int] = None + strides: Sequence[int] = (1, 1) + + @nn.compact + def __call__(self, x): + nmid = self.nmid or x.shape[-1] // 4 + nout = nmid * 4 + + residual = x + if x.shape[-1] != nout or self.strides != (1, 1): + residual = StdConv(nout, (1, 1), self.strides, use_bias=False, + name="conv_proj")(residual) + residual = nn.GroupNorm(name="gn_proj")(residual) + + y = StdConv(nmid, (1, 1), use_bias=False, name="conv1")(x) + y = nn.GroupNorm(name="gn1")(y) + y = nn.relu(y) + y = StdConv(nmid, (3, 3), self.strides, use_bias=False, name="conv2")(y) + y = nn.GroupNorm(name="gn2")(y) + y = nn.relu(y) + y = StdConv(nout, (1, 1), use_bias=False, name="conv3")(y) + + y = nn.GroupNorm(name="gn3", scale_init=nn.initializers.zeros)(y) + y = nn.relu(residual + y) + return y + + +class ResNetStage(nn.Module): + """One stage of ResNet.""" + block_size: int + first_stride: Sequence[int] = (1, 1) + nmid: Optional[int] = None + + @nn.compact + def __call__(self, x): + x = ResidualUnit(self.nmid, strides=self.first_stride, name="unit1")(x) + for i in range(1, self.block_size): + x = ResidualUnit(self.nmid, name=f"unit{i + 1}")(x) + return x + + +class Model(nn.Module): + """ResNetV1.""" + num_classes: Optional[int] = None + width: float = 1 + depth: Union[int, Sequence[int]] = 50 + + @nn.compact + def __call__(self, image, *, train=False): + del train # Unused + blocks = get_block_desc(self.depth) + width = int(64 * self.width) + + out = {} + + # Root block + x = StdConv(width, (7, 7), (2, 2), use_bias=False, name="conv_root")(image) + x = nn.GroupNorm(name="gn_root")(x) + x = nn.relu(x) + x = nn.max_pool(x, (3, 3), strides=(2, 2), padding="SAME") + out["stem"] = x + + # Stages + x = ResNetStage(blocks[0], nmid=width, name="block1")(x) + out["stage1"] = x + for i, block_size in enumerate(blocks[1:], 1): + x = ResNetStage(block_size, nmid=width * 2 ** i, + first_stride=(2, 2), name=f"block{i + 1}")(x) + out[f"stage{i + 1}"] = x + out["pre_logits_2d"] = x + + # Head + x = out["pre_logits"] = jnp.mean(x, axis=(1, 2)) + + if self.num_classes: + head = nn.Dense(self.num_classes, name="head", + kernel_init=nn.initializers.zeros) + out["logits_2d"] = head(out["pre_logits_2d"]) + x = out["logits"] = head(out["pre_logits"]) + + return x, out + + +# A dictionary mapping the number of layers in a resnet to the number of +# blocks in each stage of the model. +# NOTE: Does not include 18/34 as they also need non-bottleneck block! +def get_block_desc(depth): + if isinstance(depth, list): # Be robust to silly mistakes. + depth = tuple(depth) + return { + 26: [2, 2, 2, 2], # From timm, gets ~75% on ImageNet. + 50: [3, 4, 6, 3], + 101: [3, 4, 23, 3], + 152: [3, 8, 36, 3], + 200: [3, 24, 36, 3] + }.get(depth, depth) + + +def fix_old_checkpoints(params): + """Modifies params from old checkpoints to run with current implementation.""" + params = flax.core.unfreeze( + flax.training.checkpoints.convert_pre_linen(params)) + # Old linen used to store non-squeezed GN params. + params = flax.traverse_util.unflatten_dict({ + k: np.squeeze(v) if (set(k) + & {"gn_root", "gn_proj", "gn1", "gn2", "gn3"}) else v + for k, v in flax.traverse_util.flatten_dict(params).items() + }) + return params + + +def load(init_params, init_file, model_cfg, dont_load=()): + """Load init from checkpoint.""" + del model_cfg # Unused + params = utils.load_params(init_file) + params = common.merge_params(params, init_params, dont_load) + params = fix_old_checkpoints(params) + return params diff --git a/big_vision/models/bit_paper.py b/big_vision/models/bit_paper.py new file mode 100644 index 0000000000000000000000000000000000000000..26e5ba83616ce046a78d1a9b3fa32f8b4cbc1000 --- /dev/null +++ b/big_vision/models/bit_paper.py @@ -0,0 +1,260 @@ +# Copyright 2024 Big Vision 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. + +"""BiT models as in the paper (ResNet V2) w/ loading of public weights. + +See reproduction proof: http://(internal link)/qY70qs6j944 +""" + +import functools +import re +from typing import Optional, Sequence, Union + +from big_vision import utils as u +from big_vision.models import bit +from big_vision.models import common +import flax.linen as nn +import jax.numpy as jnp + + +def standardize(x, axis, eps): + x = x - jnp.mean(x, axis=axis, keepdims=True) + x = x / jnp.sqrt(jnp.mean(jnp.square(x), axis=axis, keepdims=True) + eps) + return x + + +# Defined our own, because we compute normalizing variance slightly differently, +# which does affect performance when loading pre-trained weights! +class GroupNorm(nn.Module): + """Group normalization (arxiv.org/abs/1803.08494).""" + ngroups: int = 32 + + @nn.compact + def __call__(self, x): + + input_shape = x.shape + group_shape = x.shape[:-1] + (self.ngroups, x.shape[-1] // self.ngroups) + + x = x.reshape(group_shape) + + # Standardize along spatial and group dimensions + x = standardize(x, axis=[1, 2, 4], eps=1e-5) + x = x.reshape(input_shape) + + bias_scale_shape = tuple([1, 1, 1] + [input_shape[-1]]) + x = x * self.param('scale', nn.initializers.ones, bias_scale_shape) + x = x + self.param('bias', nn.initializers.zeros, bias_scale_shape) + return x + + +class StdConv(nn.Conv): + + def param(self, name, *a, **kw): + param = super().param(name, *a, **kw) + if name == 'kernel': + param = standardize(param, axis=[0, 1, 2], eps=1e-10) + return param + + +class RootBlock(nn.Module): + """Root block of ResNet.""" + width: int + + @nn.compact + def __call__(self, x): + x = StdConv(self.width, (7, 7), (2, 2), padding=[(3, 3), (3, 3)], + use_bias=False, name='conv_root')(x) + x = nn.max_pool(x, (3, 3), strides=(2, 2), padding=[(1, 1), (1, 1)]) + return x + + +class ResidualUnit(nn.Module): + """Bottleneck ResNet block.""" + nmid: Optional[int] = None + strides: Sequence[int] = (1, 1) + + @nn.compact + def __call__(self, x): + nmid = self.nmid or x.shape[-1] // 4 + nout = nmid * 4 + conv = functools.partial(StdConv, use_bias=False) + + residual = x + x = GroupNorm(name='gn1')(x) + x = nn.relu(x) + + if x.shape[-1] != nout or self.strides != (1, 1): + residual = conv(nout, (1, 1), self.strides, name='conv_proj')(x) + + x = conv(nmid, (1, 1), name='conv1')(x) + x = GroupNorm(name='gn2')(x) + x = nn.relu(x) + x = conv(nmid, (3, 3), self.strides, padding=[(1, 1), (1, 1)], + name='conv2')(x) + x = GroupNorm(name='gn3')(x) + x = nn.relu(x) + x = conv(nout, (1, 1), name='conv3')(x) + + return x + residual + + +class ResNetStage(nn.Module): + """A stage (sequence of same-resolution blocks).""" + block_size: int + nmid: Optional[int] = None + first_stride: Sequence[int] = (1, 1) + + @nn.compact + def __call__(self, x): + out = {} + x = out['unit01'] = ResidualUnit( + self.nmid, strides=self.first_stride, name='unit01')(x) + for i in range(1, self.block_size): + x = out[f'unit{i+1:02d}'] = ResidualUnit( + self.nmid, name=f'unit{i+1:02d}')(x) + return x, out + + +class Model(nn.Module): + """ResNetV2.""" + num_classes: Optional[int] = None + width: int = 1 + depth: Union[int, Sequence[int]] = 50 # 50/101/152, or list of block depths. + head_zeroinit: bool = True + + @nn.compact + def __call__(self, image, *, train=False): + blocks = bit.get_block_desc(self.depth) + width = int(64 * self.width) + out = {} + + x = out['stem'] = RootBlock(width=width, name='root_block')(image) + + # Blocks + x, out['stage1'] = ResNetStage(blocks[0], nmid=width, name='block1')(x) + for i, block_size in enumerate(blocks[1:], 1): + x, out[f'stage{i + 1}'] = ResNetStage( + block_size, width * 2 ** i, + first_stride=(2, 2), name=f'block{i + 1}')(x) + + # Pre-head + x = out['norm_pre_head'] = GroupNorm(name='norm-pre-head')(x) + x = out['pre_logits_2d'] = nn.relu(x) + x = out['pre_logits'] = jnp.mean(x, axis=(1, 2)) + + # Head + if self.num_classes: + kw = {'kernel_init': nn.initializers.zeros} if self.head_zeroinit else {} + head = nn.Dense(self.num_classes, name='head', **kw) + out['logits_2d'] = head(out['pre_logits_2d']) + x = out['logits'] = head(out['pre_logits']) + + return x, out + + +def load(init_params, init_file, model_cfg, dont_load=()): + """Loads the TF-dumped NumPy or big_vision checkpoint. + + Args: + init_params: random init params from which the new head is taken. + init_file: comes from `config.model_init`, can either be an absolute + path (ie starts with /) to the checkpoint, or a string like + "L-imagenet2012" describing one of the variants from the paper. + model_cfg: the model configuration. + dont_load: list of param names to be reset to init. + + Returns: + The loaded parameters. + """ + + # Support for vanity model names from the paper. + vanity = { + 'FunMatch-224px-i1k82.8': 'gs://bit_models/distill/R50x1_224.npz', + 'FunMatch-160px-i1k80.5': 'gs://bit_models/distill/R50x1_160.npz', + } + if init_file[0] in ('L', 'M', 'S'): # The models from the original paper. + # Supported names are of the following type: + # - 'M' or 'S': the original "upstream" model without fine-tuning. + # - 'M-ILSVRC2012': i21k model fine-tuned on i1k. + # - 'M-run0-caltech101': i21k model fine-tuned on VTAB's caltech101. + # each VTAB fine-tuning was run 3x, so there's run0, run1, run2. + if '-' in init_file: + up, down = init_file[0], init_file[1:] + else: + up, down = init_file, '' + down = {'-imagenet2012': '-ILSVRC2012'}.get(down, down) # normalize + fname = f'BiT-{up}-R{model_cfg.depth}x{model_cfg.width}{down}.npz' + fname = f'gs://bit_models/{fname}' + else: + fname = vanity.get(init_file, init_file) + + params = u.load_params(fname) + params = maybe_convert_big_transfer_format(params) + return common.merge_params(params, init_params, dont_load) + + +def maybe_convert_big_transfer_format(params_tf): + """If the checkpoint comes from legacy codebase, convert it.""" + + # Only do anything at all if we recognize the format. + if 'resnet' not in params_tf: + return params_tf + + # For ease of processing and backwards compatibility, flatten again: + params_tf = dict(u.tree_flatten_with_names(params_tf)[0]) + + # Works around some files containing weird naming of variables: + for k in list(params_tf): + k2 = re.sub('/standardized_conv2d_\\d+/', '/standardized_conv2d/', k) + if k2 != k: + params_tf[k2] = params_tf[k] + del params_tf[k] + + params = { + 'root_block': {'conv_root': {'kernel': params_tf[ + 'resnet/root_block/standardized_conv2d/kernel']}}, + 'norm-pre-head': { + 'bias': params_tf['resnet/group_norm/beta'][None, None, None], + 'scale': params_tf['resnet/group_norm/gamma'][None, None, None], + }, + 'head': { + 'kernel': params_tf['resnet/head/conv2d/kernel'][0, 0], + 'bias': params_tf['resnet/head/conv2d/bias'], + } + } + + for block in ('block1', 'block2', 'block3', 'block4'): + params[block] = {} + units = set([re.findall(r'unit\d+', p)[0] for p in params_tf.keys() + if p.find(block) >= 0]) + for unit in units: + params[block][unit] = {} + for i, group in enumerate('abc', 1): + params[block][unit][f'conv{i}'] = { + 'kernel': params_tf[f'resnet/{block}/{unit}/{group}/standardized_conv2d/kernel'] # pylint: disable=line-too-long + } + params[block][unit][f'gn{i}'] = { + 'bias': params_tf[f'resnet/{block}/{unit}/{group}/group_norm/beta'][None, None, None], # pylint: disable=line-too-long + 'scale': params_tf[f'resnet/{block}/{unit}/{group}/group_norm/gamma'][None, None, None], # pylint: disable=line-too-long + } + + projs = [p for p in params_tf.keys() + if p.find(f'{block}/{unit}/a/proj') >= 0] + assert len(projs) <= 1 + if projs: + params[block][unit]['conv_proj'] = { + 'kernel': params_tf[projs[0]] + } + + return params diff --git a/big_vision/models/common.py b/big_vision/models/common.py new file mode 100644 index 0000000000000000000000000000000000000000..175dfa77a1360bc2a0276fa12245c8d357b39406 --- /dev/null +++ b/big_vision/models/common.py @@ -0,0 +1,133 @@ +# Copyright 2024 Big Vision 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. + +"""Utilities shared across models.""" + +from absl import logging +import big_vision.utils as u +import flax.linen as nn +import jax +import jax.numpy as jnp + + +def merge_params(loaded, inited, dont_load=(), match_dtype=False): + """Makes `loaded` pytree match `init`, warning or failing on mismatch. + + Args: + loaded: pytree of parameters, typically loaded from a checkpoint. + inited: pytree of parameter, typically coming from model init. + dont_load: List of regexes for parameters which shall not be taken + from `loaded`, either because they should remain at their init value, + or because they are missing on either side. + match_dtype: returned pytree as leaves converted to dtype from `inited`. + + Returns: + If successful, a new pytree which matches the structure of `init` + but contains values from `loaded`, except for `dont_load`. + + If structures don't match and mismatches are not covered by regexes in + `dont_load` argument, then raises an exception with more information. + """ + if inited is None: # A useful shortcut for example for colabs. + return loaded + + dont_load = u.check_and_compile_patterns(dont_load) + + def should_merge(name): + return not any(pattern.fullmatch(name) for pattern in dont_load) + + loaded_flat, _ = u.tree_flatten_with_names(loaded) + inited_flat, _ = u.tree_flatten_with_names(inited) + loaded_flat = {k: v for k, v in loaded_flat} + inited_flat = {k: v for k, v in inited_flat} + + # Let's first build the pytree from all common keys. + merged = {} + for name, init_val in inited_flat.items(): + # param is present in both. Load or ignore it! + if name in loaded_flat and should_merge(name): + merged[name] = loaded_flat[name] + if match_dtype: + merged[name] = loaded_flat[name].astype(init_val.dtype) + else: + logging.info("Ignoring checkpoint and using init value for %s", name) + merged[name] = init_val + + def pp(title, names, indent=" "): # Just pretty-printing + if names: + return f"{title}:\n" + "\n".join(f"{indent}{k}" for k in sorted(names)) + else: + return "" + + # Now, if there are keys that only exist in inited or loaded, be helpful: + not_in_loaded = inited_flat.keys() - loaded_flat.keys() + not_in_inited = loaded_flat.keys() - inited_flat.keys() + logging.info(pp("Parameters in model but not in checkpoint", not_in_loaded)) + logging.info(pp("Parameters in checkpoint but not in model", not_in_inited)) + + # And now see if any of them are not explicitly ignored => an error + not_in_loaded = {k for k in not_in_loaded if should_merge(k)} + not_in_inited = {k for k in not_in_inited if should_merge(k)} + + if not_in_loaded or not_in_inited: + raise ValueError( + pp("Params in checkpoint", loaded_flat.keys()) + "\n" + + pp("Params in model (code)", inited_flat.keys()) + "\n" + + pp("Params in model (code) but not in checkpoint and not `dont_load`ed", + not_in_loaded, indent=" - ") + "\n" + # Special indent for tests. + pp("Params in checkpoint but not in model (code) and not `dont_load`ed", + not_in_inited, indent=" + ")) # Special indent for tests. + + return u.recover_tree(merged.keys(), merged.values()) + + +class AddPositionEmbs(nn.Module): + """Adds positional embeddings to the inputs, supports caching for decode. + + Attributes: + decode: whether to run in single-position autoregressive mode. + """ + decode: bool = False + + @nn.compact + def __call__(self, inputs, posemb): + """Applies AddPositionEmbs module. + + Adds posemb to the inputs, supports single-position autoregressive mode. + + Args: + inputs: input data [batch_size, seq_len, emb_dim]. + posemb: positional embeddings. + + Returns: + output: inputs modulated by pos-embeddings [batch_size, seq_len, emb_dim]. + """ + assert inputs.ndim == 3, f"Unexpected inputs shape: {inputs.shape}" + _, seq_len, emb_dim = inputs.shape + pe = posemb[:, :seq_len, :] + + if self.decode: + is_initialized = self.has_variable("cache", "cache_index") + # We use a cache position index for tracking decoding position. + cache_index = self.variable("cache", "cache_index", + lambda: jnp.array(0, dtype=jnp.uint32)) + if is_initialized: + i = cache_index.value + cache_index.value = i + 1 + # Returns posemb[0, i, :], the positional embedding for the + # current decoding position. + pe = jax.lax.dynamic_slice(posemb, + start_indices=jnp.array((0, i, 0)), + slice_sizes=(1, 1, emb_dim)) + return inputs + pe diff --git a/big_vision/models/mlp_mixer.py b/big_vision/models/mlp_mixer.py new file mode 100644 index 0000000000000000000000000000000000000000..58bd4b99d21f061693da007b26dd24013e341851 --- /dev/null +++ b/big_vision/models/mlp_mixer.py @@ -0,0 +1,177 @@ +# Copyright 2024 Big Vision 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. + +"""MLP-Mixer model.""" + +from typing import Optional, Tuple +from absl import logging + +from big_vision import utils +from big_vision.models import common + +import einops +import flax.linen as nn +import flax.training.checkpoints +import jax +import jax.numpy as jnp + + +class MlpBlock(nn.Module): + mlp_dim: int + + @nn.compact + def __call__(self, x): + y = nn.Dense(self.mlp_dim)(x) + y = nn.gelu(y) + return nn.Dense(x.shape[-1])(y) + + +class MixerBlock(nn.Module): + """Mixer block layer.""" + tokens_mlp_dim: int + channels_mlp_dim: int + drop_p: float + + @nn.compact + def __call__(self, x, *, train=False): + y = nn.LayerNorm()(x) + y = jnp.swapaxes(y, 1, 2) + y = MlpBlock(self.tokens_mlp_dim, name="token_mixing")(y) + y = jnp.swapaxes(y, 1, 2) + x = x + y * _stoch_depth_mask(x, self.drop_p, not train, self.make_rng) + y = nn.LayerNorm()(x) + y = MlpBlock(self.channels_mlp_dim, name="channel_mixing")(y) + return x + y * _stoch_depth_mask(x, self.drop_p, not train, self.make_rng) + + +class MlpMixer(nn.Module): + """Mixer architecture.""" + patch_size: Tuple[int, int] + num_classes: Optional[int] + num_blocks: int + hidden_dim: int + tokens_mlp_dim: int + channels_mlp_dim: int + model_name: Optional[str] = None + stoch_depth: float = 0.0 + + @nn.compact + def __call__(self, image, *, train=False): + out = {} + x = out["stem"] = nn.Conv(self.hidden_dim, self.patch_size, + strides=self.patch_size, name="stem")(image) + x = out["input_tokens"] = einops.rearrange(x, "n h w c -> n (h w) c") + for i in range(self.num_blocks): + drop_p = (i / max(self.num_blocks - 1, 1)) * self.stoch_depth + x = out[f"block_{i}"] = MixerBlock( + self.tokens_mlp_dim, self.channels_mlp_dim, drop_p)(x, train=train) + x = nn.LayerNorm(name="pre_head_layer_norm")(x) + x = out["pre_logits"] = jnp.mean(x, axis=1) + if self.num_classes: + x = out["logits"] = nn.Dense( + self.num_classes, kernel_init=nn.initializers.zeros, name="head")(x) + return x, out + + +def Model(num_classes=None, *, variant=None, **kw): # pylint: disable=invalid-name + """Factory function to easily create a Model variant like "L/16".""" + + if variant is not None: + model_size, patch = variant.split("/") + kw.setdefault("patch_size", (int(patch), int(patch))) + config = { + "S": { + "hidden_dim": 512, + "num_blocks": 8, + "channels_mlp_dim": 2048, + "tokens_mlp_dim": 256 + }, + "B": { + "hidden_dim": 768, + "num_blocks": 12, + "channels_mlp_dim": 3072, + "tokens_mlp_dim": 384 + }, + "L": { + "hidden_dim": 1024, + "num_blocks": 24, + "channels_mlp_dim": 4096, + "tokens_mlp_dim": 512 + }, + "H": { + "hidden_dim": 1280, + "num_blocks": 32, + "channels_mlp_dim": 5120, + "tokens_mlp_dim": 640 + }, + }[model_size] + + for k, v in config.items(): + kw.setdefault(k, v) + + logging.info("Mixer config: %s", kw) + return MlpMixer(num_classes=num_classes, **kw) + + +def load(init_params, init_file, model_cfg, dont_load=()): + """Load checkpoint.""" + + del model_cfg + # Shortcut names for some canonical paper checkpoints: + init_file = { + # pylint: disable=line-too-long + # Pretrained models from the MLP-Mixer paper: https://arxiv.org/abs/2105.01601. + "B-i1k/16": "gs://mixer_models/imagenet1k/Mixer-B_16.npz", + "L-i1k/16": "gs://mixer_models/imagenet1k/Mixer-L_16.npz", + "B-i21k/16": "gs://mixer_models/imagenet21k/Mixer-B_16.npz", + "L-i21k/16": "gs://mixer_models/imagenet21k/Mixer-L_16.npz", + # pylint: enable=line-too-long + }.get(init_file, init_file) + restored_params = utils.load_params(init_file) + restored_params = flax.training.checkpoints.convert_pre_linen(restored_params) + + if "Mixer" in restored_params: + restored_params["pre_head_layer_norm"] = restored_params["Mixer"].pop( + "encoder_norm" + ) + restored_params["stem"] = restored_params.pop("embedding") + def unflatten_dense(d): + return { + "Dense_0": { + "bias": d["bias1"].squeeze(), + "kernel": d["kernel1"].squeeze(), + }, + "Dense_1": { + "bias": d["bias2"].squeeze(), + "kernel": d["kernel2"].squeeze(), + }, + } + for k, v in restored_params["Mixer"].items(): + assert k.startswith("encoderblock_"), k + v["token_mixing"] = unflatten_dense(v.pop("token_mixing_phase_0")) + v["channel_mixing"] = unflatten_dense(v.pop("channel_mixing_phase_0")) + restored_params["MixerBlock_" + k[len("encoderblock_"):]] = v + del restored_params["Mixer"] + + # possibly use the random init for some of the params (such as, the head). + restored_params = common.merge_params(restored_params, init_params, dont_load) + + return restored_params + + +def _stoch_depth_mask(x, drop_p, deterministic, make_rng): + if not deterministic and drop_p: + shape = (x.shape[0],) + (1,) * (x.ndim - 1) + return 1.0 - jax.random.bernoulli(make_rng("dropout"), drop_p, shape) + return 1.0 diff --git a/big_vision/models/ppp/__init__.py b/big_vision/models/ppp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/models/ppp/__pycache__/__init__.cpython-310.pyc b/big_vision/models/ppp/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba798b42460e2246acce9722abb04d642e957d53 Binary files /dev/null and b/big_vision/models/ppp/__pycache__/__init__.cpython-310.pyc differ diff --git a/big_vision/models/ppp/__pycache__/gemma.cpython-310.pyc b/big_vision/models/ppp/__pycache__/gemma.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..15bffc57a3184b00bcdebcd48e8ba5874ea78233 Binary files /dev/null and b/big_vision/models/ppp/__pycache__/gemma.cpython-310.pyc differ diff --git a/big_vision/models/ppp/gemma.py b/big_vision/models/ppp/gemma.py new file mode 100644 index 0000000000000000000000000000000000000000..e11606fba89dedb988ea541f39f608bc7d0cd515 --- /dev/null +++ b/big_vision/models/ppp/gemma.py @@ -0,0 +1,533 @@ +# Copyright 2024 Big Vision 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. + +"""gemma reimplementation for big_vision. + +We follow this einsum axis naming convention: + B: batch + T: query length + S: k/v length + N: num query heads + K: num k/v heads + G: num query heads per k/v head + H: head dim + D: d_model ("features") + +Example Colab using the models via the PaliGemma decoding logic: +(internal link) + +Doc locating the variable initializers in the original code and validating them: +(internal link) +""" + + +from big_vision.models import common +import big_vision.utils as u +import einops +import flax +import flax.linen as nn +import jax +import jax.numpy as jnp +import ml_collections +import numpy as np +import orbax.checkpoint + + +def get_config(variant): + """Returns config for specified gemma variant.""" + if variant == "gemma_2b": + return ml_collections.ConfigDict( + dict( + variant=variant, + width=2048, + depth=18, + mlp_dim=16_384, + num_heads=8, + num_kv_heads=1, + head_dim=256, + norm_eps=1e-6, + vocab_size=256_128, + scan=True, + remat_policy="nothing_saveable", + ) + ) + if variant == "gemma_7b": + return ml_collections.ConfigDict( + dict( + variant=variant, + width=3072, + depth=28, + mlp_dim=24_576, + num_heads=16, + num_kv_heads=16, + head_dim=256, + norm_eps=1e-6, + vocab_size=256_128, + scan=True, + remat_policy="nothing_saveable", + ) + ) + raise ValueError(f"Unknown variant: {variant}") + + +def _apply_rope(x, *, positions, max_wavelength=10_000): + """Applies RoPE positions [B, L] to x [B, L, H, D].""" + freq_exponents = (2. / x.shape[-1]) * jnp.arange(x.shape[-1] // 2) + timescale = (max_wavelength ** freq_exponents) + radians = positions[..., None] / timescale[None, None, :] + radians = radians[..., None, :] + # radians.shape = [...,L,1,d=D/2] + sin, cos = jnp.sin(radians), jnp.cos(radians) + x1, x2 = jnp.split(x, 2, axis=-1) + res = jnp.concatenate([x1 * cos - x2 * sin, x2 * cos + x1 * sin], axis=-1) + return res + + +def _update_kv_cache(module, k, v, cache_size, cache_dtype): + """Updates KV cache and returns its current contents.""" + initialized = module.has_variable("cache", "idx") + batch_size, update_len, num_heads, head_dim = k.shape + cache_dtype = cache_dtype or k.dtype + + # Idx of which cache row to update next is the same for all examples, so that + # it allows to update with dynamic_update_slice. But in order to keep things + # nicely partitioned we store it with leading batch dimension and use only + # the first entry. + idx = module.variable("cache", "idx", jnp.zeros, (batch_size,), jnp.int32) + + kv_shape = (batch_size, cache_size, num_heads, head_dim) + k_cache = module.variable( + "cache", "k_cache", jnp.zeros, kv_shape, cache_dtype) + v_cache = module.variable( + "cache", "v_cache", jnp.zeros, kv_shape, cache_dtype) + + if initialized: # write k, v in the next cache position. + assert update_len == 1, update_len + # Note: idx is the same for all examples. Use value from example 0. + indices = (0, idx.value[0], 0, 0) + k_cache.value = jax.lax.dynamic_update_slice( + k_cache.value, k.astype(cache_dtype), indices) + v_cache.value = jax.lax.dynamic_update_slice( + v_cache.value, v.astype(cache_dtype), indices) + idx.value = idx.value + 1 + else: # init cache with k, v after padding to cache_size. + prefill_len = k.shape[1] + pad_width = ((0, 0), (0, cache_size - prefill_len), (0, 0), (0, 0)) + k_cache.value = jnp.pad(k.astype(cache_dtype), pad_width) + v_cache.value = jnp.pad(v.astype(cache_dtype), pad_width) + idx.value = idx.value + prefill_len + + return k_cache.value.astype(k.dtype), v_cache.value.astype(v.dtype) + + +def trunc_norm_init(in_axis, out_axis, batch_axis): + return nn.initializers.variance_scaling( + 1.0, "fan_in", "truncated_normal", + in_axis=in_axis, out_axis=out_axis, batch_axis=batch_axis) + + +class Einsum(nn.Module): + shape: tuple[int, ...] + w_init: nn.initializers.Initializer = nn.initializers.zeros_init() + + @nn.compact + def __call__(self, eqn, x): + w = self.param("w", self.w_init, self.shape) + return jnp.einsum(eqn, x, w) + + +class RMSNorm(nn.Module): + + @nn.compact + def __call__(self, x): + scale = self.param("scale", nn.initializers.zeros_init(), (x.shape[-1])) + var = jnp.mean(jnp.square(x), axis=-1, keepdims=True) + normed_inputs = jnp.asarray(x * jnp.reciprocal(jnp.sqrt(var + 1e-06))) + normed_inputs = normed_inputs * (1 + scale) + return normed_inputs + + +class Embedder(nn.Module): + """Embedder module.""" + + vocab_size: int + embed_dim: int + + def setup(self): + self.input_embedding_table = self.param( + "input_embedding", + nn.initializers.variance_scaling( + scale=1.0, mode="fan_in", distribution="normal", + in_axis=1, out_axis=0,), + (self.vocab_size, self.embed_dim), + ) + + def encode(self, x): + x = self.input_embedding_table[(x,)] + x *= jnp.sqrt(self.embed_dim).astype(x.dtype) + return x + + def decode(self, x): + return jnp.dot(x, self.input_embedding_table.T) + + +class Attention(nn.Module): + """Attention module.""" + + num_heads: int + num_kv_heads: int + features: int + head_dim: int + + cache_dtype: str | None = None + + def setup(self): + if self.num_kv_heads == self.num_heads: + self.qkv_einsum = Einsum( + shape=(3, self.num_heads, self.features, self.head_dim), + w_init=trunc_norm_init( + in_axis=(2,), out_axis=(0, 1, 3), batch_axis=()), + ) + else: + # MQA + self.q_einsum = Einsum( + shape=(self.num_heads, self.features, self.head_dim), + w_init=trunc_norm_init(in_axis=(1,), out_axis=(0, 2), batch_axis=()), + ) + self.kv_einsum = Einsum( + shape=(2, self.num_kv_heads, self.features, self.head_dim), + w_init=trunc_norm_init( + in_axis=(2,), out_axis=(0, 1, 3), batch_axis=()), + ) + self.attn_vec_einsum = Einsum( + shape=(self.num_heads, self.head_dim, self.features), + w_init=trunc_norm_init(in_axis=(0, 1), out_axis=(2,), batch_axis=()), + ) + + @nn.compact + def __call__(self, x, positions, attn_mask, decode, deterministic=True): + if self.num_kv_heads == self.num_heads: + q, k, v = self.qkv_einsum("BSD,3KDH->3BSKH", x) + else: + q = self.q_einsum("BTD,NDH->BTNH", x) + k, v = self.kv_einsum("BSD,2KDH->2BSKH", x) + + q = _apply_rope(q, positions=positions) + q *= self.head_dim**-0.5 + + k = _apply_rope(k, positions=positions) + if decode: + k, v = _update_kv_cache(self, k, v, + cache_size=attn_mask.shape[-1], + cache_dtype=self.cache_dtype) + + q = einops.rearrange(q, "B T (K G) H -> B T K G H", K=self.num_kv_heads) + logits = jnp.einsum("BTKGH,BSKH->BKGTS", q, k) + logits = logits.astype(jnp.float32) + + if attn_mask.shape != (q.shape[0], 1, q.shape[1], k.shape[1]): + raise ValueError( + f"Attention mask with shape {attn_mask.shape} but shapes for q and k " + f"are: {q.shape} and {k.shape}" + ) + + # big_neg = jnp.finfo(logits.dtype).min + big_neg = -2.3819763e38 # See gemma/modules.py + masked_logits = jnp.where(attn_mask[:, :, None, :, :], logits, big_neg) + + probs = jax.nn.softmax(masked_logits, axis=-1).astype(k.dtype) + + encoded = jnp.einsum("BKGTS,BSKH->BTKGH", probs, v) + encoded = einops.rearrange(encoded, "B T K G H -> B T (K G) H") + attn_output = self.attn_vec_einsum("BTNH,NHD->BTD", encoded) + + return attn_output + + +class FeedForward(nn.Module): + """Feed forward module.""" + + features: int + hidden_dim: int + + @nn.compact + def __call__(self, x): + w_gating = self.param( + "gating_einsum", + trunc_norm_init(in_axis=(1,), out_axis=(0, 2), batch_axis=()), + ((2, self.features, self.hidden_dim)), + ) + ff_gate = jnp.dot(x, w_gating[0]) + gate_value = nn.gelu(ff_gate) + + ff1 = jnp.dot(x, w_gating[1]) + activations = gate_value * ff1 + + w_linear = self.param( + "linear", + trunc_norm_init(in_axis=(0,), out_axis=(1,), batch_axis=()), + (self.hidden_dim, self.features), + ) + outputs = jnp.dot(activations, w_linear) + + return outputs + + +class Block(nn.Module): + """Transformer block.""" + + num_heads: int + num_kv_heads: int + embed_dim: int + head_dim: int + hidden_dim: int + + dropout: float = 0.0 + dropout_bdims: tuple[int, ...] = () + cache_dtype: str | None = None + + def setup(self): + self.pre_attention_norm = RMSNorm() + self.attn = Attention( + num_heads=self.num_heads, + num_kv_heads=self.num_kv_heads, + features=self.embed_dim, + head_dim=self.head_dim, + cache_dtype=self.cache_dtype, + ) + self.pre_ffw_norm = RMSNorm() + self.mlp = FeedForward(features=self.embed_dim, hidden_dim=self.hidden_dim) + if self.dropout: + self.drop = nn.Dropout(self.dropout, self.dropout_bdims) + else: + self.drop = lambda x, _: x + + def __call__(self, x, unused_scan_arg, positions, attn_mask, + decode, deterministic=True): + x = nn.with_logical_constraint(x, ("act_batch", "act_len", "act_emb")) + inputs_normalized = self.pre_attention_norm(x) + attn_output = self.attn(inputs_normalized, positions, attn_mask, + decode, deterministic) + attn_output = self.drop(attn_output, deterministic) + attn_output += x + residual = attn_output + attn_output = self.pre_ffw_norm(attn_output) + outputs = self.mlp(attn_output) + outputs = self.drop(outputs, deterministic) + outputs = residual + outputs + return outputs, unused_scan_arg + + +class Model(nn.Module): + """gemma model.""" + + variant: str + + width: int + depth: int + mlp_dim: int + num_heads: int + num_kv_heads: int + head_dim: int + norm_eps: float + vocab_size: int + + dropout: float = 0.0 + dropout_bdims: tuple[int, ...] = () # Every float is dropped independently. + cache_dtype: str | None = None + + # TODO: Wire this in all places needed so that the model can be + # run with different activation dtype. For now only float32 runs. + embed_dtype: str = "float32" + + scan: bool = False + remat_policy: str = "none" + + @nn.compact + def __call__( + self, tokens, *, + embedded_prefix=None, + embed_only=False, + pre_logits=None, + positions=None, mask=None, + decode=False, deterministic=True, + ): + """Embed only, or complete forward pass. + + Args: + tokens: Embedded, then and appended to `embedded_prefix`. Can be None. + embedded_prefix: Optional prefix that is already embedded. + embed_only: Whether to compute embeddings only. + pre_logits: If present computes logits from pre_logits and returns. + positions: Optional `[B, T]` allows to specify the absolute position of + the tokens. + mask: Optional attention mask `[B, T, S]`. + decode: Whether to use kv-cache. Caller must pass masks and positions. + deterministic: Forwarded to all dropout layers. + + Returns: + If `embed_only=False`, then `(logits, out)` will be returned. + If `embed_only=True`, then the embeddings will be returned. + """ + out = {} + + embedder = Embedder( + vocab_size=self.vocab_size, + embed_dim=self.width, + name="embedder") + + if pre_logits is not None: + x = out["pre_logits"] = pre_logits + logits = out["logits"] = embedder.decode(x) + return logits, out + + x = [] + if embedded_prefix is not None: + x.append(embedded_prefix) + if tokens is not None: + x.append(embedder.encode(tokens)) + + x = jnp.concatenate(x, axis=-2) + x = x.astype(self.embed_dtype) + batch_size, seq_len, width = x.shape + + if embed_only: + return x + + if decode: + assert positions is not None and mask is not None, ( + "Must explicitly pass positions and mask for decoding.") + + if positions is None: + positions = jnp.arange(seq_len).astype(jnp.int32)[None, :] + assert positions.shape[1] == x.shape[1], (positions.shape, x.shape) + + if mask is None: + mask = nn.attention.make_causal_mask(jnp.ones([batch_size, seq_len])) + if mask.ndim == 3: + mask = mask[:, None, :, :] + cache_size = max(seq_len, mask.shape[-1]) + assert mask.shape == (batch_size, 1, seq_len, cache_size), mask.shape + + if self.remat_policy == "none": + block_cls = Block + else: + block_cls = nn.remat( + Block, + prevent_cse=not self.scan, + static_argnums=(5, 6), # 0=self, 5=decode, 6=deterministic + policy=getattr(jax.checkpoint_policies, self.remat_policy), + ) + + block_kw = dict( + num_heads=self.num_heads, + head_dim=self.head_dim, + num_kv_heads=self.num_kv_heads, + embed_dim=width, + hidden_dim=self.mlp_dim, + dropout=self.dropout, + dropout_bdims=self.dropout_bdims, + cache_dtype=self.cache_dtype, + ) + layers = self.scope.push("layers") + if self.scan: + blocks = [nn.scan( + block_cls, + # cache has axis 1 since we want leading dimension to be batch size. + variable_axes={"params": 0, "cache": 1}, + split_rngs={"params": True, "dropout": True}, + in_axes=nn.broadcast, + length=self.depth, + )( + parent=layers, **block_kw + )] + else: + blocks = [ + block_cls( + parent=layers.push(str(layer)), + **block_kw, + ) + for layer in range(self.depth) + ] + unused_scan_arg = () + for block in blocks: + x, unused_scan_arg = block( + x, unused_scan_arg, positions, mask, decode, deterministic) + + assert x.dtype == jnp.dtype(self.embed_dtype) # Sanity check. + out["encoded"] = x + + x = RMSNorm(name="final_norm")(x) + out["pre_logits"] = x + + x = embedder.decode(x) + out["logits"] = x + + return x, out + + +_ORBAX_INITS = {} +_BV_INITS = {} + + +def _load_orbax(path): + """Loads and coverts Orbax gemma checkpoint.""" + checkpointer = orbax.checkpoint.PyTreeCheckpointer() + params = checkpointer.restore(path) + params = flax.traverse_util.unflatten_dict(params, sep="/")["transformer"] + n = sum(1 for k in params if k.startswith("layer_")) + params["layers"] = jax.tree.map( + lambda *xs: np.stack(xs), *(params.pop(f"layer_{i}") for i in range(n)) + ) + mlp = params["layers"]["mlp"] + mlp["gating_einsum"] = mlp["gating_einsum"].pop("w") + mlp["linear"] = mlp["linear"].pop("w") + return params + + +def _del_pad_rows(params): + """Some checkpoints have 128 unused padding tokens.""" + emb = params["embedder"]["input_embedding"] + assert emb.shape[0] == 256_128 + params["embedder"]["input_embedding"] = np.asarray(emb)[:256_000] + return params + + +def load(init_params, init_file, model_cfg=None, dont_load=()): + """Loads existing weights.""" + model_cfg = model_cfg or {} + variant = model_cfg.get("variant", "gemma_2b") + init_variant = f"{init_file} {variant}" + if init_variant in _ORBAX_INITS: + params = _del_pad_rows(_load_orbax(_ORBAX_INITS[init_variant])) + elif init_variant in _BV_INITS: + params = _del_pad_rows(u.load_params(_BV_INITS[init_variant])) + else: + params = u.load_params(init_file) + + def extend_rows(emb1, target_rows): + if (missing_rows := target_rows - emb1.shape[0]) == 0: + return emb1 + assert missing_rows > 0, "You're asking to shrink vocab?!" + new_rows = np.random.randn(missing_rows, emb1.shape[1]) + new_rows = (new_rows * 0.02).astype(emb1.dtype) + return np.r_[np.asarray(emb1), new_rows] + + if "vocab_size" in model_cfg: + params["embedder"]["input_embedding"] = extend_rows( + params["embedder"]["input_embedding"], + model_cfg["vocab_size"], + ) + + return common.merge_params(params, init_params, dont_load) diff --git a/big_vision/models/proj/cappa/cappa.py b/big_vision/models/proj/cappa/cappa.py new file mode 100644 index 0000000000000000000000000000000000000000..8c20b1b78f06d3eece7f2e5e8707eef995466529 --- /dev/null +++ b/big_vision/models/proj/cappa/cappa.py @@ -0,0 +1,428 @@ +# Copyright 2023 Big Vision 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. + +"""Model definitions for CapPa (https://arxiv.org/abs/2306.07915). + +Used abbreviations for dimension annotations: + B: batch size. + H: image height. + W: image width. + P: number of patches (PH/PW: number of patches in height/width dimensions). + E: embedding size. + L: sequence length of text tokens. + V: vocab size. +""" + +from collections.abc import Sequence + +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import flax +import flax.linen as nn +from flax.linen import partitioning +import jax +import jax.numpy as jnp + + +def shift_right(x, axis=1, constant_values=0): + """Shift to the right on given axis with padding value 0.""" + pad_widths = [(0, 0)] * len(x.shape) + pad_widths[axis] = (1, 0) + padded = jnp.pad(x, pad_widths, constant_values=constant_values) + # Cuts off the rightmost slice of size along the `axis` dimension. + # Note that `list[:-1]`` is the same as `list[slice(-1)]`. + return padded[tuple(slice(-1 if i == axis else None) for i in range(x.ndim))] + + +class MlpBlock(nn.Module): + """Transformer MLP / feed-forward block with option to deactivate bias.""" + mlp_dim: int | None = None # Defaults to 4x input dim + dropout: float = 0.0 + use_bias: bool = True + + @nn.compact + def __call__(self, x, deterministic=True): + """Applies Transformer MlpBlock module.""" + inits = dict( + kernel_init=nn.initializers.xavier_uniform(), + bias_init=nn.initializers.normal(stddev=1e-6), + ) + + n, l, d = x.shape # pylint: disable=unused-variable + x = nn.Dense(self.mlp_dim or 4 * d, use_bias=self.use_bias, **inits)(x) + x = nn.gelu(x) + x = nn.Dropout(rate=self.dropout)(x, deterministic) + x = nn.Dense(d, use_bias=self.use_bias, **inits)(x) + return x + + +class EncoderDecoderBlock(nn.Module): + """Transformer encoder-decoder layer.""" + mlp_dim: int + num_heads: int + dropout_rate: float = 0. + decode: bool = False + use_bias: bool = True + + @nn.compact + def __call__(self, targets, encoded, decoder_mask=None, deterministic=True): + """Applies EncoderDecoder1DBlock module. + + Args: + targets: target text embeddings [B, L, E]. + encoded: encoded image patches from encoder [B, P, E]. + decoder_mask: decoder self-attention mask. + deterministic: bool, deterministic or not (to apply dropout). + + Returns: + output after transformer encoder-decoder block [B, L, E]. + """ + def wlc(f): + dim_names = ("act_batch", "act_len", "act_emb") + return nn.with_logical_constraint(f, dim_names) + + # Decoder block. + x = wlc(nn.LayerNorm(name="LayerNorm1", use_bias=self.use_bias)(targets)) + x = wlc(nn.SelfAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, decode=self.decode, name="SelfAttn")( + x, decoder_mask, deterministic=deterministic)) + x = wlc(nn.Dropout(rate=self.dropout_rate)(x, deterministic=deterministic)) + x = wlc(x + targets) + + if encoded is not None: + # Encoder-Decoder block. + y = wlc(nn.LayerNorm(name="LayerNorm2", use_bias=self.use_bias)(x)) + y = wlc(nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, name="CrossAttn")( + y, encoded, deterministic=deterministic)) + y = wlc( + nn.Dropout(rate=self.dropout_rate)(y, deterministic=deterministic)) + y = wlc(y + x) + else: + y = x + + # MLP block. + z = wlc(nn.LayerNorm(name="LayerNorm3", use_bias=self.use_bias)(y)) + z = wlc(MlpBlock( + mlp_dim=self.mlp_dim, dropout=self.dropout_rate, use_bias=self.use_bias, + name="MLP")(z, deterministic=deterministic)) + + return wlc(y + z), None + + +class Decoder(nn.Module): + """Transformer decoder with parallel prediction.""" + emb_dim: int + mlp_dim: int + num_heads: int + num_layers: int + dropout_rate: float = 0. + output_vocab_size: int = 32_000 + + # Masked prediction training mode + masked_pred_prob: float = 0. + masking_ratio: float = 0. + + # Whether to use bias in MLP blocks and LN + use_bias: bool = True + + scan: bool = False + remat_policy: str = "nothing_saveable" + + @nn.compact + def __call__(self, + encoded, + targets, + pos_emb, + decoder_mask=None, + decode=False, + deterministic=True, + max_decode_length=None): + """Applies Transformer model on the inputs. + + Args: + encoded: encoded image patches from encoder [B, P, E]. + targets: target text tokens [B, L]. + pos_emb: positional embeddings. + decoder_mask: decoder self-attention mask. + decode: bool, whether to perform fast autoregressive decoding with cache. + deterministic: bool, deterministic or not (to apply dropout). + max_decode_length: optional max length for positional embeddings. + + Returns: + output of a transformer decoder [B, L, V]. + """ + y = targets.astype("int32") + if not decode: + if self.masked_pred_prob > 0.0 and not deterministic: + # Binary random variable indicating whether to do masked prediction + + def _add_random_masks(a): + # Generate random mask + n_masked = int(self.masking_ratio * a.shape[1]) + mask_locations = jnp.zeros(a.shape[:2], dtype=jnp.int32) + mask_locations = mask_locations.at[:, :n_masked].set(1) + mask_locations = jax.random.permutation( + self.make_rng("dropout"), mask_locations, axis=1, independent=True + ) + # Replace mask locations with mask token index (=vocab_size) + a_masked = jnp.where(mask_locations, self.output_vocab_size, a) + return a_masked + + def where(mask, x, y): + mask = mask.reshape((-1,) + (1,) * (x.ndim - 1)) + return jnp.where(mask, x, y) + + do_masked_pred = ( + jax.random.uniform(self.make_rng("dropout"), (len(y),)) + < self.masked_pred_prob + ) + y = where(do_masked_pred, _add_random_masks(y), shift_right(y)) + decoder_mask = where( + do_masked_pred, jnp.ones_like(decoder_mask), decoder_mask + ) + + else: + y = shift_right(y) + + embed = nn.Embed( + self.output_vocab_size + (1 if self.masked_pred_prob > 0.0 else 0), + self.emb_dim, + name="EmbedTargets", + embedding_init=nn.initializers.normal(stddev=1.0), + ) + y = embed(y) + + y = common.AddPositionEmbs( + decode=decode, name="PosEmbedTargets")(y, pos_emb) + # NOTE: One could apply dropout on the decoder's inputs here. Whether to do + # it or not, and if so, what is the best/common way, is to be determined. + # y = nn.Dropout(rate=self.dropout_rate)(y, deterministic=deterministic) + + if self.scan: + # Mostly followed + # https://github.com/google/maxtext/blob/4d99e30b3e0e0cb1d1aa11c7db7fffe18e301498/MaxText/layers.py#L1126 + # for the scanned version. + # 1. remat + enc_dec_block_remat = nn.remat( + EncoderDecoderBlock, + prevent_cse=False, + static_argnums=(-1,), + policy=getattr(jax.checkpoint_policies, self.remat_policy, None)) + # 2. scan + initializing = self.is_mutable_collection("params") + param_scan_axis = 1 + params_spec = (param_scan_axis if initializing + else partitioning.ScanIn(param_scan_axis)) + dec_scanned = nn.scan(enc_dec_block_remat, + variable_axes={ + "params": params_spec, + "cache": 0, + }, + split_rngs={"params": True, "dropout": True}, + in_axes=nn.broadcast, + length=self.num_layers) + # 3. fprop + y, _ = dec_scanned(num_heads=self.num_heads, mlp_dim=self.mlp_dim, + dropout_rate=self.dropout_rate, decode=decode, + use_bias=self.use_bias, name="EncDecBlock")( + y, encoded, decoder_mask, deterministic) + else: + for lyr in range(self.num_layers): + y, _ = EncoderDecoderBlock( + num_heads=self.num_heads, mlp_dim=self.mlp_dim, + dropout_rate=self.dropout_rate, decode=decode, + use_bias=self.use_bias, name=f"EncDecBlock{lyr}")( + y, encoded, decoder_mask=decoder_mask, + deterministic=deterministic) + + y = nn.LayerNorm(name="LayerNorm")(y) + + logits = nn.Dense( + self.output_vocab_size, + kernel_init=nn.initializers.zeros, + name="LogitsDense", + )(y) + return logits + + +class Model(nn.Module): + """Transformer Model for sequence to sequence translation.""" + # Encoder/decoder: + num_heads: int = 8 + num_layers: int = 6 + mlp_dim: int = 2048 + emb_dim: int = 512 + enc_dropout_rate: float = 0. + vocab_size: int = 32_000 + seq_len: int = 256 + + # Encoder: + patches: Sequence[int] = (16, 16) + input_seq_len: int = 768 + posemb_type: str = "learn" + patch_dropout: float = 0. + + # Decoder: + decoder_num_heads: int = 0 + decoder_num_layers: int = 0 + decoder_mlp_dim: int = 0 + decoder_emb_dim: int = 0 + dec_dropout_rate: float = 0. + # Probability of masked prediction rather than autoregressive prediciton. + masked_pred_prob: float = 0. + # Masking ratio for masked prediction. + masking_ratio: float = 0. + # Whether to use bias in decoder MLP blocks and LN. + decoder_bias: bool = True + + scan: bool = False + remat_policy: str = "nothing_saveable" + + def setup(self): + + self.encoder = vit.Model( + patch_size=self.patches, + width=self.emb_dim, + depth=self.num_layers, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.enc_dropout_rate, + posemb=self.posemb_type, + scan=self.scan, + remat_policy=self.remat_policy, + ) + + self.pos_emb_for_decoder = vit.get_posemb( + self, + self.posemb_type, + (1, self.seq_len), + self.decoder_emb_dim or self.emb_dim, + "pos_embedding_decoder", + ) + self.decoder = Decoder( + num_layers=self.decoder_num_layers or self.num_layers, + mlp_dim=self.decoder_mlp_dim or self.mlp_dim, + num_heads=self.decoder_num_heads or self.num_heads, + dropout_rate=self.dec_dropout_rate, + emb_dim=self.decoder_emb_dim or self.emb_dim, + output_vocab_size=self.vocab_size, + masked_pred_prob=self.masked_pred_prob, + masking_ratio=self.masking_ratio, + use_bias=self.decoder_bias, + scan=self.scan, + remat_policy=self.remat_policy, + ) + + def encode(self, image, train=False, return_enc_features=False): + """Encodes input image or embeddings.""" + + _, out = self.encoder(image, train=train) + encoded = out["encoded"] + + # Return intermediate features if required + if return_enc_features: + return encoded, out + + return encoded + + def decode(self, encoded, targets, decode=False, train=False, + max_decode_length=None): + """Applies Transformer decoder-branch on encoded-input and target. + + Args: + encoded: encoded image patches from encoder [B, P, E]. + targets: target text tokens [B, L]. + decode: whether to prepare and use an autoregressive cache. + train: whether it is training. + max_decode_length: optional max length for positional embeddings. + + Returns: + logits array from transformer decoder [B, L, V]. + """ + decoder_mask = None if decode else nn.make_causal_mask(targets) + logits = self.decoder( + encoded, + targets, + pos_emb=self.pos_emb_for_decoder, + decoder_mask=decoder_mask, + decode=decode, + deterministic=not train, + max_decode_length=max_decode_length) + return logits + + def __call__(self, image, text, *, decode=False, + train=False, return_enc_features=False): + """Applies Transformer model on the inputs. + + Args: + image: batch of images [B, H, W, 3]. + text: batch of tokenized texts [B, L]. + decode: whether to prepare and use an autoregressive cache. + train: whether it is training. + return_enc_features: whether to return the encoder features. + + Returns: + logits array from full transformer [B, L, V]. + """ + if return_enc_features: + encoded, out = self.encode(image, train=train, return_enc_features=True) + return encoded, out + + encoded = self.encode(image, train=train) + + decoded = self.decode(encoded, text, decode=decode, train=train) + return decoded + + +def load(init_params, init_files, model_params=None, + dont_load=("head/kernel", "head/bias", "cls")): + """Loads params from init checkpoint and merges into init_params.""" + + if isinstance(init_files, str): + # A shortcut for a single file checkpoint of a vtt model. + ckpt_params = utils.load_params(init_files) + ckpt_params = flax.training.checkpoints.convert_pre_linen(ckpt_params) + ckpt_params = common.merge_params(ckpt_params, init_params, dont_load) + + # Detect attempts to load non-scan checkpoint into scan model if possible. + if (model_params.get("scan") and + "encoderblock" not in ckpt_params["encoder"]["Transformer"]): + raise NotImplementedError("Loading a non-scan checkpoint into a " + "scan model is not supported yet!") + if (not model_params.get("scan") + and "encoderblock" in ckpt_params["encoder"]["Transformer"]): + assert "decoder.*" in dont_load or "decoder/.*" in dont_load, ( + "Converting scan decoder to a non-scan one is not supported yet!") + ckpt_params["encoder"] = utils.jit_cpu()( + vit.scan_to_pyloop)(ckpt_params["encoder"]) + + else: + assert set(init_files) == {"encoder"}, "Only encoder init supported" + enc_init = init_files["encoder"] + ckpt_params = flax.core.freeze(init_params).unfreeze() + vit_params = ckpt_params["encoder"] + encoder_params = vit.load( + vit_params, enc_init, model_cfg={}, + dont_load=dont_load) + ckpt_params["encoder"] = encoder_params + + ckpt_params["encoder"]["pos_embedding"] = vit.resample_posemb( + old=ckpt_params["encoder"]["pos_embedding"], + new=init_params["encoder"]["pos_embedding"]) + + return ckpt_params diff --git a/big_vision/models/proj/clippo/one_tower.py b/big_vision/models/proj/clippo/one_tower.py new file mode 100644 index 0000000000000000000000000000000000000000..c7ce5abd03174b91328e74302d1f15dadd98e0dc --- /dev/null +++ b/big_vision/models/proj/clippo/one_tower.py @@ -0,0 +1,96 @@ +# Copyright 2022 Big Vision 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. + +"""Model definition to train a single ViT model with the contrastive trainer.""" + +import importlib +from typing import Optional, Any + +from big_vision import utils +import flax.linen as nn +import jax.numpy as jnp + +ConfigDict = Any + + +class Model(nn.Module): + """Single ViT to encode regular images and text images.""" + image: Optional[ConfigDict] = None + image_model: str = "vit" + out_dim: int = 768 + temperature_init: float = 10.0 + + @nn.compact + def __call__(self, image, text=None, **kw): + """Returns (B, C) image and (B, C) text representations, and some extras.""" + ztxt, zimg = None, None + kw = kw or {} + + image_model = importlib.import_module( + f"big_vision.models.{self.image_model}" + ).Model(**{"num_classes": self.out_dim, **(self.image or {})}, name="img") # pylint: disable=not-a-mapping + + def _compute_embedding(input_image, prefix): + zemb, out_emb = image_model(input_image, **kw) + out = {f"{prefix}/{k}": v for k, v in out_emb.items()} + + # Normalize the embeddings. + out[f"{prefix}/norm"] = jnp.linalg.norm(zemb, axis=1, keepdims=True) + out[f"{prefix}/normalized"] = zemb = zemb / (out[f"{prefix}/norm"] + 1e-8) + return zemb, out + + out = {} + if image is not None: + zimg, out_img = _compute_embedding(image, "img") + out.update(out_img) + + if text is not None: + ztxt, out_txt = _compute_embedding(text, "txt") + out.update(out_txt) + + temp_init = jnp.log(self.temperature_init) + t = self.param("t", + lambda key, shape, dtype: temp_init*jnp.ones(shape, dtype), + (1,), jnp.float32) + out["t"] = jnp.exp(t) + out["t/parameter"] = t + + return zimg, ztxt, out + + +def load(init_params, init_files, model_cfg, img_load_kw={}): # pylint: disable=dangerous-default-value + """Loads the ViT parameters - adapted from proj/image_text/two_towers.py.""" + if isinstance(init_files, str): + # A shortcut for a single file checkpoint of a two_towers model. + init_files = {k: f"{init_files}:{k}" for k in ("img", "t")} + else: + init_files = {**init_files} # Shallow copy because we'll pop stuff off. + + restored_params = {**init_params} + + img_init = init_files.pop("image", init_files.pop("img", None)) + if img_init: + restored_params["img"] = importlib.import_module( + f"big_vision.models.{model_cfg.image_model}" + ).load(init_params["img"], img_init, model_cfg.image, **img_load_kw) + + t_init = init_files.pop("temperature", init_files.pop("t", None)) + if t_init: + restored_params["t"] = utils.load_params(None, t_init) + + assert not init_files, ( + f"There's something unused left in `config.model_init`. You probably got " + f"a typo. Here it is: {init_files}") + + return restored_params diff --git a/big_vision/models/proj/flaxformer/bert.py b/big_vision/models/proj/flaxformer/bert.py new file mode 100644 index 0000000000000000000000000000000000000000..4bad30be3dbdb736586fa04109ff756b3b0975c5 --- /dev/null +++ b/big_vision/models/proj/flaxformer/bert.py @@ -0,0 +1,94 @@ +# Copyright 2024 Big Vision 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. + +"""BERT encoder, optionally loading pre-trained checkpoints.""" + +import dataclasses +from typing import Optional + +from absl import logging +from big_vision import utils +from big_vision.models import common +import flax +import flax.linen as nn +import jax.numpy as jnp +from tensorflow.io import gfile + +from flaxformer.architectures.bert import bert +from flaxformer.architectures.bert import bert_checkpoint_converter +from flaxformer.architectures.bert import configs + + +class Model(nn.Module): + """BERT encoder with linear projection on last layer CLS token.""" + + config: str + num_classes: Optional[int] = None + head_zeroinit: bool = True + + @nn.compact + def __call__(self, text, *, train=False): + out = {} + + batch_size, max_len = text.shape + bert_model = bert.BertEncoder(**dataclasses.asdict({ + "base": configs.BertBaseConfig(), + "large": configs.BertLargeConfig(), + }[self.config])) + x = out["transformed"] = bert_model( + token_ids=text, + position_ids=jnp.tile( + jnp.arange(0, max_len, dtype=jnp.int32), [batch_size, 1]), + segment_ids=jnp.zeros([batch_size, max_len], dtype=jnp.int32), + input_mask=text.astype(jnp.bool_).astype(jnp.int32), + enable_dropout=train, + ) + + x = out["pre_logits"] = x[:, 0] # CLS token + + if self.num_classes: + kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {} + x = out["logits"] = nn.Dense(self.num_classes, name="head", **kw)(x) + + return x, out + + +def load(params, path, model_cfg=None, dont_load=()): + """Returns `params` with BERT weights replaced from checkpoint at `path`.""" + del model_cfg + + checkpoint_path = f"{path}/bert_model.ckpt" + if gfile.exists(f"{checkpoint_path}.index"): + logging.info("Loading original BERT checkpoint from '%s'", checkpoint_path) + params = flax.core.FrozenDict(params).unfreeze() # Recursive copy. + max_len = ( + params["BertEncoder_0"]["embedder"]["embedders_position_ids"] + ["embedding"].shape[0]) + bert_params, pooler_params = ( + bert_checkpoint_converter.load_params_from_tf_checkpoint( + checkpoint_path=f"{path}/bert_model.ckpt")) + del pooler_params + if isinstance(bert_params, flax.core.FrozenDict): + bert_params = bert_params.unfreeze() + bert_params["embedder"]["embedders_position_ids"]["embedding"] = ( + bert_params["embedder"]["embedders_position_ids"]["embedding"][:max_len] + ) + return common.merge_params( + {"BertEncoder_0": bert_params}, params, dont_load) + + logging.info( + "Could not find original BERT checkpoint path '%s', " + "loading big_vision checkpoint '%s'", checkpoint_path, path) + restored_params = utils.load_params(path) + return common.merge_params(restored_params, params, dont_load) diff --git a/big_vision/models/proj/flaxformer/bert_test.py b/big_vision/models/proj/flaxformer/bert_test.py new file mode 100644 index 0000000000000000000000000000000000000000..90405c2ec70a45b3a8ae90aa2dd39aaea2fee240 --- /dev/null +++ b/big_vision/models/proj/flaxformer/bert_test.py @@ -0,0 +1,77 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for bert.""" + +import tempfile + +from big_vision import input_pipeline +from big_vision.models.proj.flaxformer import bert +from big_vision.models.proj.flaxformer import bert_test_util +import big_vision.pp.builder as pp_builder +import big_vision.pp.ops_general # pylint: disable=unused-import +import big_vision.pp.proj.flaxformer.bert_ops # pylint: disable=unused-import +import flax +import jax +import jax.numpy as jnp +import tensorflow as tf + + +# BERT vocabulary for testing. +_BERT_VOCAB = [ + "[PAD]", + "[UNK]", + "this", + "is", + "a", + "test", + "[CLS]", + "[SEP]", +] +_TOKEN_LEN = 16 + + +class BertTest(tf.test.TestCase): + + def test_load_apply(self): + inkey = "text" + vocab_path = f"{tempfile.mkdtemp()}/vocab.txt" + with open(vocab_path, "w") as f: + f.write("\n".join(_BERT_VOCAB)) + ds2, _ = input_pipeline.make_for_inference( + tf.data.Dataset.from_tensor_slices( + {inkey: tf.ragged.constant([["this is a test"]])}), + num_ex_per_process=[1], + preprocess_fn=pp_builder.get_preprocess_fn( + f"bert_tokenize(inkey='{inkey}', vocab_path='{vocab_path}', " + f"max_len={_TOKEN_LEN})" + "|keep('labels')"), + batch_size=1, + ) + text = jnp.array(next(iter(ds2))["labels"]) + model = bert.Model(config="base") + variables = model.init(jax.random.PRNGKey(0), text) + params = bert.load(flax.core.unfreeze(variables)["params"], + bert_test_util.create_base_checkpoint()) + x, out = model.apply({"params": params}, text) + self.assertAllEqual(jax.tree_map(jnp.shape, x), (1, 768)) + self.assertAllEqual( + jax.tree_map(jnp.shape, out), { + "transformed": (1, 16, 768), + "pre_logits": (1, 768), + }) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/models/proj/flaxformer/bert_test_util.py b/big_vision/models/proj/flaxformer/bert_test_util.py new file mode 100644 index 0000000000000000000000000000000000000000..11a8a0105dec13490e8438d4162040b2fa4a06fe --- /dev/null +++ b/big_vision/models/proj/flaxformer/bert_test_util.py @@ -0,0 +1,261 @@ +# Copyright 2024 Big Vision 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. + +"""Utilities for fake BERT checkpoint.""" + +import tempfile + +import tensorflow.compat.v1 as tf + +# Checkpoint structure was extracted with the following (Colab) snippet: +# +# !wget https://storage.googleapis.com/bert_models/2020_02_20/uncased_L-12_H-768_A-12.zip # pylint: disable=line-too-long +# !unzip uncased_L-12_H-768_A-12.zip +# +# import tensorflow.compat.v1 as tf +# +# ckpt_reader = tf.train.load_checkpoint('bert_model.ckpt') +# tf_params = { +# tf_name: ckpt_reader.get_tensor(tf_name) +# for tf_name in ckpt_reader.get_variable_to_dtype_map() +# } +# +# 'shapes_dtypes = {\n%s\n}' % '\n'.join( +# f' "{k}": ({v.shape}, "{v.dtype}"),' +# for k, v, in tf_params.items() +# ) + +# pylint: disable=line-too-long +_BASE_SHAPES_DTYPES = { + "cls/seq_relationship/output_bias": ((2,), "float32"), + "cls/predictions/transform/LayerNorm/gamma": ((768,), "float32"), + "cls/predictions/transform/LayerNorm/beta": ((768,), "float32"), + "bert/pooler/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_9/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_9/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_3/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_7/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_9/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_7/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_9/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_9/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_9/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_9/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_9/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_8/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_4/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_8/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_8/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_11/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_11/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_8/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_8/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_8/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_1/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_8/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_3/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_8/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_8/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_8/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_7/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_7/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_8/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_7/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_7/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_9/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_7/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_7/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_6/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_6/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_6/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_0/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_6/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_7/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_4/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_2/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_5/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_5/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_9/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_3/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_8/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_5/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_5/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_5/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_4/output/dense/bias": ((768,), "float32"), + "bert/embeddings/token_type_embeddings": ((2, 768), "float32"), + "bert/encoder/layer_4/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_4/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_7/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_4/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_9/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_10/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_6/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_4/attention/self/query/bias": ((768,), "float32"), + "cls/seq_relationship/output_weights": ((2, 768), "float32"), + "bert/encoder/layer_7/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_4/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_4/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_4/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_3/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_1/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_2/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_8/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_4/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_3/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_4/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_3/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_1/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_3/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_10/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_3/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_1/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_0/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_10/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_3/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_3/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_1/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_3/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_1/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_3/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_2/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_6/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_11/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_2/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_2/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_6/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_11/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_6/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_11/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_11/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_11/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_10/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_11/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_6/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_6/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_11/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_10/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_4/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_11/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_10/attention/self/query/bias": ((768,), "float32"), + "bert/embeddings/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_2/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_11/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_11/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_5/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_3/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_10/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_10/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/embeddings/word_embeddings": ((30522, 768), "float32"), + "bert/encoder/layer_9/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_9/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_6/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_10/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_6/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_1/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_5/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_2/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_0/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_3/intermediate/dense/kernel": ((768, 3072), "float32"), + "cls/predictions/output_bias": ((30522,), "float32"), + "bert/encoder/layer_0/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_6/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_0/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_10/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_5/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_4/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_0/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_0/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_10/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_7/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_3/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_2/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_8/output/dense/kernel": ((3072, 768), "float32"), + "bert/embeddings/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_1/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_10/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_6/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_2/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_11/attention/self/value/bias": ((768,), "float32"), + "bert/encoder/layer_9/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_0/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_10/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_10/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_1/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_8/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_0/intermediate/dense/bias": ((3072,), "float32"), + "bert/encoder/layer_1/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_1/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_7/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_8/attention/output/dense/bias": ((768,), "float32"), + "cls/predictions/transform/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_6/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_5/attention/self/key/kernel": ((768, 768), "float32"), + "bert/encoder/layer_0/attention/self/value/kernel": ((768, 768), "float32"), + "bert/encoder/layer_7/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_7/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_1/output/dense/kernel": ((3072, 768), "float32"), + "bert/encoder/layer_11/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_4/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_1/attention/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_9/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_2/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_0/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_10/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_1/attention/self/query/bias": ((768,), "float32"), + "bert/encoder/layer_3/output/LayerNorm/beta": ((768,), "float32"), + "bert/encoder/layer_6/attention/output/dense/kernel": ((768, 768), "float32"), + "bert/encoder/layer_1/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_11/output/dense/bias": ((768,), "float32"), + "cls/predictions/transform/dense/bias": ((768,), "float32"), + "bert/encoder/layer_0/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_11/attention/self/query/kernel": ((768, 768), "float32"), + "bert/encoder/layer_0/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_0/attention/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_7/attention/output/LayerNorm/gamma": ((768,), "float32"), + "bert/encoder/layer_4/attention/self/key/bias": ((768,), "float32"), + "bert/encoder/layer_10/attention/self/key/kernel": ((768, 768), "float32"), + "bert/embeddings/position_embeddings": ((512, 768), "float32"), + "bert/encoder/layer_1/output/dense/bias": ((768,), "float32"), + "bert/encoder/layer_9/intermediate/dense/kernel": ((768, 3072), "float32"), + "bert/encoder/layer_0/output/LayerNorm/beta": ((768,), "float32"), + "bert/pooler/dense/bias": ((768,), "float32"), + "bert/encoder/layer_0/attention/output/LayerNorm/beta": ((768,), "float32"), +} +# pylint: enable=line-too-long + + +def create_base_checkpoint(): + """Returns path to fake Bert "base" checkpoint directory (zero init).""" + directory = tempfile.mkdtemp() + path = f"{directory}/bert_model.ckpt" + with tf.Session() as sess: + for name, (shape, dtype) in _BASE_SHAPES_DTYPES.items(): + tf.Variable(tf.zeros(shape, dtype), name=name) + saver = tf.train.Saver() + sess.run(tf.global_variables_initializer()) + saver.save(sess, path) + return directory diff --git a/big_vision/models/proj/flexi/vit.py b/big_vision/models/proj/flexi/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..3a0af0e95045016fa6adb0dd7f49eee70d4005bf --- /dev/null +++ b/big_vision/models/proj/flexi/vit.py @@ -0,0 +1,226 @@ +# Copyright 2024 Big Vision 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. + +"""A version of ViT with flexible seqlen ((internal link)).""" + +from typing import Optional, Sequence + +from absl import logging +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np +import tensorflow as tf + + +def resample_patchemb(old, new_hw): + """Resample the weights of the patch embedding kernel to target resolution. + + We resample the patch embedding kernel by approximately inverting the effect + of patch resizing. Colab with detailed explanation: + (internal link) + With this resizing, we can for example load a B/8 filter into a B/16 model + and, on 2x larger input image, the result will match. + See (internal link) + Args: + old: original parameter to be resized. + new_hw: target shape (height, width)-only. + Returns: + Resized patch embedding kernel. + """ + assert len(old.shape) == 4, "Four dimensions expected" + assert len(new_hw) == 2, "New shape should only be hw" + if tuple(old.shape[:2]) == tuple(new_hw): + return old + + logging.info("FlexiViT: resize embedding %s to %s", old.shape, new_hw) + + def resize(x_np, new_shape): + x_tf = tf.constant(x_np)[None, ..., None] + # NOTE: we are using tf.image.resize here to match the resize operations in + # the data preprocessing pipeline. + x_upsampled = tf.image.resize( + x_tf, new_shape, method="bilinear")[0, ..., 0].numpy() + return x_upsampled + + def get_resize_mat(old_shape, new_shape): + mat = [] + for i in range(np.prod(old_shape)): + basis_vec = np.zeros(old_shape) + basis_vec[np.unravel_index(i, old_shape)] = 1. + mat.append(resize(basis_vec, new_shape).reshape(-1)) + return np.stack(mat).T + + resize_mat = get_resize_mat(old.shape[:2], new_hw) + resize_mat_pinv = np.linalg.pinv(resize_mat.T) + + def resample_kernel(kernel): + resampled_kernel = resize_mat_pinv @ kernel.reshape(-1) + return resampled_kernel.reshape(new_hw) + v_resample_kernel = jax.vmap(jax.vmap(resample_kernel, 2, 2), 3, 3) + return v_resample_kernel(old) + + +class Patchify(nn.Module): + """As a class just to match param names with original ViT.""" + + patch_size: Sequence[int] = (32, 32) + width: int = 768 + seqhw: Optional[int] = None + + @nn.compact + def __call__(self, image, seqhw=None): + n, h, w, c = image.shape # pylint: disable=unused-variable + + w_emb = self.param( + "kernel", nn.initializers.normal(stddev=1/np.sqrt(self.width)), + (*self.patch_size, c, self.width), image.dtype) + b_emb = self.param("bias", nn.initializers.zeros, self.width, image.dtype) + + # Compute required patch-size to reach `seqhw` given `image` size. + seqhw = seqhw or self.seqhw + if seqhw is None and self.is_initializing(): + patch_size = self.patch_size + else: + patch_size = tuple(np.array((h, w)) // np.array((seqhw, seqhw))) + + if patch_size != self.patch_size: + w_emb = resample_patchemb(old=w_emb, new_hw=patch_size) + + x = jax.lax.conv_general_dilated( + image, w_emb, window_strides=patch_size, padding="VALID", + dimension_numbers=("NHWC", "HWIO", "NHWC")) + return x + b_emb + + +class _Model(nn.Module): + """ViT model.""" + + num_classes: int + patch_size: Sequence[int] = (32, 32) + posemb_size: Sequence[int] = (7, 7) + width: int = 768 + depth: int = 12 + mlp_dim: Optional[int] = None # Defaults to 4x input dim + num_heads: int = 12 + posemb: str = "learn" # Can also be "sincos2d" + pool_type: str = "gap" # Can also be "map" or "tok" + head_zeroinit: bool = True + + seqhw: Optional[int] = None + + @nn.compact + def __call__(self, image, *, seqhw=None, train=False): + out = {} + + x = out["stem"] = Patchify( + self.patch_size, self.width, self.seqhw, name="embedding")(image, seqhw) + + # == Flattening + posemb + n, h, w, c = x.shape + x = jnp.reshape(x, [n, h * w, c]) + + pos_emb = vit.get_posemb( + self, self.posemb, self.posemb_size, c, "pos_embedding", x.dtype) + if pos_emb.shape[1] != h * w: + pos_emb = jnp.reshape(pos_emb, (1, *self.posemb_size, c)) + pos_emb = jax.image.resize(pos_emb, (1, h, w, c), "linear") + pos_emb = jnp.reshape(pos_emb, (1, h * w, c)) + + x = out["with_posemb"] = x + pos_emb + + # == Optional [cls] token + if self.pool_type == "tok": + cls = self.param("cls", nn.initializers.zeros, (1, 1, c), x.dtype) + x = jnp.concatenate([jnp.tile(cls, [n, 1, 1]), x], axis=1) + + # == Encoder + n, l, c = x.shape # pylint: disable=unused-variable + + x, out["encoder"] = vit.Encoder( + depth=self.depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + name="Transformer")(x) + encoded = out["encoded"] = x + + if self.pool_type == "map": + x = out["head_input"] = vit.MAPHead( + num_heads=self.num_heads, mlp_dim=self.mlp_dim)(x) + elif self.pool_type == "gap": + x = out["head_input"] = jnp.mean(x, axis=1) + elif self.pool_type == "tok": + x = out["head_input"] = x[:, 0] + encoded = encoded[:, 1:] + else: + raise ValueError(f"Unknown pool type: '{self.pool_type}'") + + x_2d = jnp.reshape(encoded, [n, h, w, -1]) + + out["pre_logits_2d"] = x_2d + out["pre_logits"] = x + + if self.num_classes: + kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {} + head = nn.Dense(self.num_classes, name="head", **kw) + x_2d = out["logits_2d"] = head(x_2d) + x = out["logits"] = head(x) + + return x, out + + +def Model(num_classes, *, variant=None, **kw): # pylint: disable=invalid-name + """Factory function, because linen really don't like what I'm doing!""" + return _Model(num_classes, **{**vit.decode_variant(variant), **kw}) + + +def load(init_params, init_file, model_cfg, dont_load=()): # pylint: disable=invalid-name because we had to CamelCase above. + """Load init from checkpoint, both old model and this one. +Hi-res posemb.""" + init_file = {**vit.VANITY_NAMES, **VANITY_NAMES}.get(init_file, init_file) + restored_params = utils.load_params(init_file) + + restored_params = vit.fix_old_checkpoints(restored_params) + + # Potentially resize the position embedings if seqlen differs. + restored_params["pos_embedding"] = vit.resample_posemb( + old=restored_params["pos_embedding"], + new=init_params["pos_embedding"]) + + # Potentially resize the patch embedding kernel. + old_patchemb = restored_params["embedding"]["kernel"] + restored_params["embedding"]["kernel"] = resample_patchemb( + old=old_patchemb, new_hw=model_cfg.patch_size) + + # possibly use the random init for some of the params (such as, the head). + restored_params = common.merge_params(restored_params, init_params, dont_load) + + return restored_params + + +# Shortcut names for some canonical paper checkpoints: +VANITY_NAMES = { + # pylint: disable=line-too-long + "FlexiViT-L i1k": "gs://big_vision/flexivit/flexivit_l_i1k.npz", + "FlexiViT-B i1k": "gs://big_vision/flexivit/flexivit_b_i1k.npz", + "FlexiViT-S i1k": "gs://big_vision/flexivit/flexivit_s_i1k.npz", + "FlexiViT-B i21k 90ep": "gs://big_vision/flexivit/flexivit_b_i21k_90ep.npz", + "FlexiViT-B i21k 300ep": "gs://big_vision/flexivit/flexivit_b_i21k_300ep.npz", + "FlexiViT-B i21k 1000ep": "gs://big_vision/flexivit/flexivit_b_i21k_1000ep.npz", + "ViT-B/16 i21k": "gs://big_vision/flexivit/vit_b16_i21k_300ep.npz", + "ViT-B/30 i21k": "gs://big_vision/flexivit/vit_b30_i21k_300ep.npz", + # pylint: enable=line-too-long +} diff --git a/big_vision/models/proj/flexi/vit_test.py b/big_vision/models/proj/flexi/vit_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f7dd588e3a4231ece5ac0a92771fddcac803b66d --- /dev/null +++ b/big_vision/models/proj/flexi/vit_test.py @@ -0,0 +1,127 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for the FlexiViT model.""" + +from absl.testing import absltest +from big_vision.models.proj.flexi import vit +import jax +from jax import config +from jax import numpy as jnp +import numpy as np +import tensorflow as tf + +config.update("jax_enable_x64", True) + + +class PatchEmbTest(absltest.TestCase): + + def _test_patch_emb_resize(self, old_shape, new_shape, n_patches=100): + # This test verifies that if we resize the input image patch and resample + # the patch embedding accordingly, the output does not change. + # NOTE: if the image contains more than one patch, then the embeddings will + # change due to patch interaction during the resizing. + patch_shape = old_shape[:-2] + resized_patch_shape = new_shape[:-2] + patches = np.random.randn(n_patches, *old_shape[:-1]) + w_emb = jnp.asarray(np.random.randn(*old_shape)) + + old_embeddings = jax.lax.conv_general_dilated( + patches, w_emb, window_strides=patch_shape, padding="VALID", + dimension_numbers=("NHWC", "HWIO", "NHWC"), precision="highest") + + patch_resized = tf.image.resize( + tf.constant(patches), resized_patch_shape, method="bilinear").numpy() + patch_resized = jnp.asarray(patch_resized).astype(jnp.float64) + w_emb_resampled = vit.resample_patchemb(w_emb, resized_patch_shape) + self.assertEqual(w_emb_resampled.shape, new_shape) + + new_embeddings = jax.lax.conv_general_dilated( + patch_resized, w_emb_resampled, window_strides=resized_patch_shape, + padding="VALID", dimension_numbers=("NHWC", "HWIO", "NHWC"), + precision="highest") + + self.assertEqual(old_embeddings.shape, new_embeddings.shape) + np.testing.assert_allclose( + old_embeddings, new_embeddings, rtol=1e-1, atol=1e-4) + + def test_resize_square(self): + out_channels = 256 + patch_sizes = [48, 40, 30, 24, 20, 16, 15, 12, 10, 8, 6, 5] + for s in patch_sizes: + old_shape = (s, s, 3, out_channels) + for t in patch_sizes: + new_shape = (t, t, 3, out_channels) + if s <= t: + self._test_patch_emb_resize(old_shape, new_shape) + + def test_resize_rectangular(self): + out_channels = 256 + old_shape = (8, 10, 3, out_channels) + new_shape = (10, 12, 3, out_channels) + self._test_patch_emb_resize(old_shape, new_shape) + + old_shape = (8, 6, 3, out_channels) + new_shape = (9, 15, 3, out_channels) + self._test_patch_emb_resize(old_shape, new_shape) + + old_shape = (8, 6, 3, out_channels) + new_shape = (15, 9, 3, out_channels) + self._test_patch_emb_resize(old_shape, new_shape) + + def test_input_channels(self): + out_channels = 256 + for c in [1, 3, 10]: + old_shape = (8, 10, c, out_channels) + new_shape = (10, 12, c, out_channels) + self._test_patch_emb_resize(old_shape, new_shape) + + def _test_works(self, old_shape, new_shape): + old = jnp.asarray(np.random.randn(*old_shape)) + resampled = vit.resample_patchemb(old, new_shape[:2]) + self.assertEqual(resampled.shape, new_shape) + self.assertEqual(resampled.dtype, old.dtype) + + def test_downsampling(self): + # NOTE: for downsampling we cannot guarantee that the outputs would match + # before and after downsampling. So, we simply test that the code runs and + # produces an output of the correct shape and type. + out_channels = 256 + for t in [4, 5, 6, 7]: + for c in [1, 3, 5]: + old_shape = (8, 8, c, out_channels) + new_shape = (t, t, c, out_channels) + self._test_works(old_shape, new_shape) + + def _test_raises(self, old_shape, new_shape): + old = jnp.asarray(np.random.randn(*old_shape)) + with self.assertRaises(AssertionError): + vit.resample_patchemb(old, new_shape) + + def test_raises_incorrect_dims(self): + old_shape = (8, 10, 3, 256) + new_shape = (10, 12, 1, 256) + self._test_raises(old_shape, new_shape) + + old_shape = (8, 10, 1, 256) + new_shape = (10, 12, 3, 256) + self._test_raises(old_shape, new_shape) + + old_shape = (8, 10, 3, 128) + new_shape = (10, 12, 3, 256) + self._test_raises(old_shape, new_shape) + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/models/proj/givt/adaptor.py b/big_vision/models/proj/givt/adaptor.py new file mode 100644 index 0000000000000000000000000000000000000000..6b76c7704beee10a5d57f822577f490cb1ad1d59 --- /dev/null +++ b/big_vision/models/proj/givt/adaptor.py @@ -0,0 +1,174 @@ +# Copyright 2024 Big Vision 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. + +"""Invertible adaptor based on iRevNet. + +Based on the PyTorch version from: +https://github.com/jhjacobsen/pytorch-i-revnet/blob/master/models/iRevNet.py +""" + +from typing import Any, Optional, Sequence + +from big_vision import utils +from big_vision.models import common +from big_vision.models.proj.givt import cnn +import einops +import flax.core +import flax.linen as nn +import jax +import jax.numpy as jnp + + +def _split(x: jax.Array) -> tuple[jax.Array, jax.Array]: + n = x.shape[-1] // 2 + x1 = x[:, :, :, :n] + x2 = x[:, :, :, n:] + return x1, x2 + + +def _merge(x1: jax.Array, x2: jax.Array) -> jax.Array: + return jnp.concatenate((x1, x2), axis=-1) + + +class IRevNetBlock(nn.Module): + """iRevNet Block.""" + first: int = False + dropout_rate: float = 0. + num_channels: int = 2 + num_channels_bottleneck: Optional[int] = None + num_grps_norm: int = 32 + + @nn.compact + def _fx2(self, x: jax.Array, train: bool = True) -> jax.Array: + if not self.first: + y = nn.GroupNorm(num_groups=self.num_grps_norm, name="gn_0")(x) + y = nn.relu(y) + else: + y = x + + ks = (3, 3) # hardcode kernel-size 3 for now + y = nn.Conv(self.num_channels_bottleneck or self.num_channels, + kernel_size=ks, padding=1, use_bias=False)(y) + y = nn.GroupNorm(num_groups=self.num_grps_norm, name="gn_1")(y) + y = nn.relu(y) + + y = nn.Conv(self.num_channels_bottleneck or self.num_channels, + kernel_size=ks, padding=1, use_bias=False)(y) + y = nn.Dropout(rate=self.dropout_rate, deterministic=(not train))(y) + y = nn.GroupNorm(num_groups=self.num_grps_norm, name="gn_2")(y) + y = nn.relu(y) + + y = nn.Conv(self.num_channels, kernel_size=ks, padding=1, use_bias=False)(y) + + return y + + def forward( + self, + x: tuple[jax.Array, jax.Array], + train: bool = True, + ) -> tuple[jax.Array, jax.Array]: + """Bijective block forward.""" + x1, x2 = x[0], x[1] + fx2 = self._fx2(x2, train=train) + y1 = fx2 + x1 + return (x2, y1) + + def inverse(self, + x: tuple[jax.Array, jax.Array], + train: bool = True + ) -> tuple[jax.Array, jax.Array]: + """Bijective block inverse.""" + x2, y1 = x[0], x[1] + fx2 = -self._fx2(x2, train=train) + x1 = fx2 + y1 + return (x1, x2) + + +class IRevNet(nn.Module): + """iRevNet.""" + num_blocks: int = 4 + num_channels: int = 4 + num_channels_bottleneck: Optional[int] = None + dropout_rate: float = 0.0 + + def setup(self) -> None: + num_grps_norm = min(32, self.num_channels // 2) + self.modules = [ + IRevNetBlock( + first=(i == 0), + num_channels=self.num_channels // 2, + num_channels_bottleneck=( + self.num_channels_bottleneck or self.num_channels) // 2, + num_grps_norm=num_grps_norm, + dropout_rate=self.dropout_rate, + ) + for i in range(self.num_blocks) + ] + + def forward(self, x: jax.Array, train: bool = True) -> jax.Array: + out = _split(x) + for m in self.modules: + out = m.forward(out, train=train) + out_bij = _merge(out[0], out[1]) + return out_bij + + def inverse(self, out_bij: jax.Array, train: bool = True) -> jax.Array: + out = _split(out_bij) + for m in reversed(self.modules): + out = m.inverse(out, train=train) + out = _merge(out[0], out[1]) + return out + + def __call__(self, x: jax.Array, train: bool = True) -> jax.Array: + return self.forward(x, train=train) + + +class Model(IRevNet): + """Wrapper for IRevNet to function as an adaptor in our setup.""" + + pixel_shuffle_patch_size: tuple[int, int] = (1, 1) + + def forward(self, x: jax.Array, train: bool = True) -> jax.Array: + # (b, code_len, ch) --> (b, h, w, ch) --> (b, code_len, ch) + # h, w are the spatial dimensions after space-to-depth transformation + h, w = cnn.get_h_w_pixelshuffle(x.shape[1], self.pixel_shuffle_patch_size) + x = einops.rearrange(x, "b (h w) c -> b h w c", h=h, w=w) + x = super().forward(x, train) + x = einops.rearrange(x, "b h w c -> b (h w) c") # (b, codelen, codeword_d) + + return x + + def inverse(self, out_bij: jax.Array, train: bool = True) -> jax.Array: + # (b, code_len, ch) --> (b, h, w, ch) --> (b, code_len, ch) + h, w = cnn.get_h_w_pixelshuffle( + out_bij.shape[1], self.pixel_shuffle_patch_size) + out_bij = einops.rearrange(out_bij, "b (h w) c -> b h w c", h=h, w=w) + out_bij = super().inverse(out_bij, train) + out_bij = einops.rearrange(out_bij, "b h w c -> b (h w) c") + + return out_bij + + +def load( + init_params: Any, + init_file: str, + model_params: Any = None, + dont_load: Sequence[str] = (), +) -> Any: + """Loads params from init checkpoint and merges into init_params.""" + del model_params + ckpt_params = flax.core.unfreeze(utils.load_params(init_file)) + if init_params is not None: + ckpt_params = common.merge_params(ckpt_params, init_params, dont_load) + return ckpt_params diff --git a/big_vision/models/proj/givt/adaptor_test.py b/big_vision/models/proj/givt/adaptor_test.py new file mode 100644 index 0000000000000000000000000000000000000000..e197d1d2ba6c5e3247bb14cd533e2b916f373779 --- /dev/null +++ b/big_vision/models/proj/givt/adaptor_test.py @@ -0,0 +1,50 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for the IRevNet adaptor.""" + +from big_vision.models.proj.givt import adaptor +import jax +from jax import random +import jax.numpy as jnp + +from absl.testing import absltest + + +class AdaptorTest(googletest.TestCase): + + def test_inversion(self): + num_channels = 8 + input_shape = (1, 24, 24, num_channels) + + rng = random.PRNGKey(758493) + _, inp_rng, init_rng, data_rng = jax.random.split(rng, 4) + + dummy_x = random.normal(inp_rng, shape=input_shape) + real_x = jax.random.normal(data_rng, shape=input_shape) + + model = adaptor.IRevNet( + num_blocks=4, + num_channels=num_channels, + dropout_rate=0.0, + ) + params = model.init(init_rng, dummy_x) + + real_y = model.apply(params, real_x, method=model.forward) + real_x_ = model.apply(params, real_y, method=model.inverse) + self.assertTrue(jnp.allclose(real_x, real_x_, atol=1e-5)) + + +if __name__ == "__main__": + googletest.main() diff --git a/big_vision/models/proj/givt/cnn.py b/big_vision/models/proj/givt/cnn.py new file mode 100644 index 0000000000000000000000000000000000000000..27896b3d365c75aa7ec62edd0f872a98ad6afe3f --- /dev/null +++ b/big_vision/models/proj/givt/cnn.py @@ -0,0 +1,376 @@ +# Copyright 2024 Big Vision 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. + +"""CNN encoder/decoder architecture based on the VQ-GAN and MaskGIT papers. + +Adapted from https://github.com/google-research/maskgit/blob/main/maskgit/nets/vqgan_tokenizer.py. # pylint: disable=line-too-long +""" + +import dataclasses +import functools +import math +from typing import Any, Sequence + +from big_vision import utils +from big_vision.models import common +from big_vision.models.proj.givt import vae + +import einops +import flax.linen as nn +import flax.training.checkpoints + +import jax +import jax.numpy as jnp + + +def _get_norm_layer(train, dtype, norm_type="BN"): + """Create normalization layers. + + Args: + train: Whether to use the layer in training or inference mode. + dtype: Layer output type. + norm_type: Which normalization to use "BN", "LN", or "GN". + + Returns: + An instance of the the layer. + """ + if norm_type == "BN": + return functools.partial( + nn.BatchNorm, + use_running_average=not train, + momentum=0.9, + epsilon=1e-5, + axis_name=None, + axis_index_groups=None, + dtype=jnp.float32, + use_fast_variance=False) + elif norm_type == "LN": + return functools.partial(nn.LayerNorm, dtype=dtype, use_fast_variance=False) + elif norm_type == "GN": + return functools.partial(nn.GroupNorm, dtype=dtype, use_fast_variance=False) + else: + raise NotImplementedError + + +def _tensorflow_style_avg_pooling(x, window_shape, strides, padding: str): + """Avg pooling as done by TF (Flax layer gives different results). + + To be specific, Flax includes padding cells when taking the average, + while TF does not. + + Args: + x: Input tensor + window_shape: Shape of pooling window; if 1-dim tuple is just 1d pooling, if + 2-dim tuple one gets 2d pooling. + strides: Must have the same dimension as the window_shape. + padding: Either 'SAME' or 'VALID' to indicate pooling method. + + Returns: + pooled: Tensor after applying pooling. + """ + pool_sum = jax.lax.reduce_window(x, 0.0, jax.lax.add, + (1,) + window_shape + (1,), + (1,) + strides + (1,), padding) + pool_denom = jax.lax.reduce_window( + jnp.ones_like(x), 0.0, jax.lax.add, (1,) + window_shape + (1,), + (1,) + strides + (1,), padding) + return pool_sum / pool_denom + + +def _upsample(x, factor=2, method="nearest"): + n, h, w, c = x.shape + x = jax.image.resize(x, (n, h * factor, w * factor, c), method=method) + return x + + +def _dsample(x): + return _tensorflow_style_avg_pooling( + x, (2, 2), strides=(2, 2), padding="same") + + +def get_h_w_pixelshuffle(hw, pixel_shuffle_patch_size): + # Compute h, w after space-to-depth transformation and before flattening, + # assuming the imge before space-to-depth transformation was square. + ph, pw = pixel_shuffle_patch_size + s = int(math.sqrt(hw * ph * pw)) + h, w = s // ph, s // pw + assert h * w == hw, f"Length {hw} incompatible with pixelshuffle ({ph}, {pw})" + return h, w + + +class ResBlock(nn.Module): + """Basic Residual Block.""" + filters: int + norm_fn: Any + conv_fn: Any + dtype: int = jnp.float32 + activation_fn: Any = nn.relu + use_conv_shortcut: bool = False + + @nn.compact + def __call__(self, x: jax.Array) -> jax.Array: + input_dim = x.shape[-1] + residual = x + x = self.norm_fn()(x) + x = self.activation_fn(x) + x = self.conv_fn(self.filters, kernel_size=(3, 3), use_bias=False)(x) + x = self.norm_fn()(x) + x = self.activation_fn(x) + x = self.conv_fn(self.filters, kernel_size=(3, 3), use_bias=False)(x) + if input_dim != self.filters: + if self.use_conv_shortcut: + residual = self.conv_fn( + self.filters, kernel_size=(3, 3), use_bias=False)( + x) + else: + residual = self.conv_fn( + self.filters, kernel_size=(1, 1), use_bias=False)( + x) + return x + residual + + +class Encoder(nn.Module): + """Encoder Blocks.""" + + filters: int + num_res_blocks: int + channel_multipliers: list[int] + embedding_dim: int + conv_downsample: bool = False + norm_type: str = "GN" + activation_fn_str: str = "swish" + dtype: int = jnp.float32 + + def setup(self) -> None: + if self.activation_fn_str == "relu": + self.activation_fn = nn.relu + elif self.activation_fn_str == "swish": + self.activation_fn = nn.swish + else: + raise NotImplementedError + + @nn.compact + def __call__(self, x: jax.Array, train: bool = False) -> jax.Array: + conv_fn = nn.Conv + norm_fn = _get_norm_layer( + train=train, dtype=self.dtype, norm_type=self.norm_type) + block_args = dict( + norm_fn=norm_fn, + conv_fn=conv_fn, + dtype=self.dtype, + activation_fn=self.activation_fn, + use_conv_shortcut=False, + ) + x = conv_fn(self.filters, kernel_size=(3, 3), use_bias=False)(x) + num_blocks = len(self.channel_multipliers) + for i in range(num_blocks): + filters = self.filters * self.channel_multipliers[i] + for _ in range(self.num_res_blocks): + x = ResBlock(filters, **block_args)(x) + if i < num_blocks - 1: + if self.conv_downsample: + x = conv_fn(filters, kernel_size=(4, 4), strides=(2, 2))(x) + else: + x = _dsample(x) + for _ in range(self.num_res_blocks): + x = ResBlock(filters, **block_args)(x) + x = norm_fn()(x) + x = self.activation_fn(x) + x = conv_fn(self.embedding_dim, kernel_size=(1, 1))(x) + return x + + +class Decoder(nn.Module): + """Decoder Blocks.""" + + filters: int + num_res_blocks: int + channel_multipliers: list[int] + norm_type: str = "GN" + activation_fn_str: str = "swish" + output_dim: int = 3 + dtype: Any = jnp.float32 + + def setup(self) -> None: + if self.activation_fn_str == "relu": + self.activation_fn = nn.relu + elif self.activation_fn_str == "swish": + self.activation_fn = nn.swish + else: + raise NotImplementedError + + @nn.compact + def __call__(self, x: jax.Array, train: bool = False) -> jax.Array: + conv_fn = nn.Conv + norm_fn = _get_norm_layer( + train=train, dtype=self.dtype, norm_type=self.norm_type) + block_args = dict( + norm_fn=norm_fn, + conv_fn=conv_fn, + dtype=self.dtype, + activation_fn=self.activation_fn, + use_conv_shortcut=False, + ) + num_blocks = len(self.channel_multipliers) + filters = self.filters * self.channel_multipliers[-1] + x = conv_fn(filters, kernel_size=(3, 3), use_bias=True)(x) + for _ in range(self.num_res_blocks): + x = ResBlock(filters, **block_args)(x) + for i in reversed(range(num_blocks)): + filters = self.filters * self.channel_multipliers[i] + for _ in range(self.num_res_blocks): + x = ResBlock(filters, **block_args)(x) + if i > 0: + x = _upsample(x, 2) + x = conv_fn(filters, kernel_size=(3, 3))(x) + x = norm_fn()(x) + x = self.activation_fn(x) + x = conv_fn(self.output_dim, kernel_size=(3, 3))(x) + return x + + +class Model(vae.Model): + """CNN Model.""" + + filters: int = 128 + num_res_blocks: int = 2 + channel_multipliers: list[int] = dataclasses.field(default_factory=list) + conv_downsample: bool = False + activation_fn: str = "swish" + norm_type: str = "GN" + output_dim: int = 3 + dtype: Any = jnp.float32 + # If True, rescale the input [-1, 1] -> [0, 1] and clip logvar to [-30, 20] + malib_ckpt: bool = False + pixel_shuffle_patch_size: tuple[int, int] = (1, 1) + + def setup(self) -> None: + # Encoder and decoder + self.encoder = Encoder( + filters=self.filters, + num_res_blocks=self.num_res_blocks, + channel_multipliers=self.channel_multipliers, + norm_type=self.norm_type, + activation_fn_str=self.activation_fn, + embedding_dim=2 * self.codeword_dim, + conv_downsample=self.conv_downsample, + dtype=self.dtype, + name="cnn_encoder", + ) + self.decoder = Decoder( + filters=self.filters, + num_res_blocks=self.num_res_blocks, + channel_multipliers=self.channel_multipliers, + norm_type=self.norm_type, + activation_fn_str=self.activation_fn, + output_dim=self.output_dim, + dtype=self.dtype, + name="cnn_decoder", + ) + + def _maybe_rescale_input(self, x): + return (x + 1.0) / 2.0 if self.malib_ckpt else x + + def _maybe_rescale_output(self, x): + return 2.0 * x - 1.0 if self.malib_ckpt else x + + def _maybe_clip_logvar(self, logvar): + return jnp.clip(logvar, -30.0, 20.0) if self.malib_ckpt else logvar + + def encode( + self, + x: jax.Array, + *, + train: bool = False, + ) -> tuple[jax.Array, jax.Array]: + x = self._maybe_rescale_input(x) + x = self.encoder(x, train=train) # (2, 16, 16, 64) + assert x.shape[1] == x.shape[2], f"Square spatial dims. required: {x.shape}" + mu, logvar = jnp.split(x, 2, axis=-1) # (2, 16, 16, 32) x 2 + logvar = self._maybe_clip_logvar(logvar) + + def _space_to_depth(z): + ph, pw = self.pixel_shuffle_patch_size + return einops.rearrange( + z, "b (h ph) (w pw) c -> b (h w) (c ph pw)", + ph=ph, pw=pw + ) # (2, 256 // (ph * pw), 64 * ph * pw) + + mu, logvar = _space_to_depth(mu), _space_to_depth(logvar) + + return mu, logvar + + def decode(self, x: jax.Array, train: bool = False) -> jax.Array: + # Decode + ph, pw = self.pixel_shuffle_patch_size + h, w = get_h_w_pixelshuffle(x.shape[1], (ph, pw)) + + x = einops.rearrange( + x, "b (h w) (c ph pw) -> b (h ph) (w pw) c", + h=h, w=w, + ph=ph, pw=pw + ) # (2, 16, 16, 32) + x = self.decoder(x, train=train) # (2, 256, 256, 3) + x = self._maybe_rescale_output(x) + x = jnp.clip(x, -1.0, 1.0) + + return x + + +def load( + init_params: Any, + init_file: str, + model_params: Any = None, + dont_load: Sequence[str] = (), + malib_ckpt: bool = False, + use_ema_params: bool = False, +) -> Any: + """Loads params from init checkpoint and merges into init_params. + + Args: + init_params: pytree with (previously initialized) model parameters. + init_file: Path of the checkpoint to load. + model_params: Dict containing the model config. + dont_load: Sequence of (flattened) parameter names which should not be + loaded. + malib_ckpt: Whether the given init_file is a malib checkpoint. + use_ema_params: Whether to load the EMA params (for malib checkpoints). + + Returns: + pytree containing the loaded model parameters. + """ + # `model_params` is unused here, but we still include it to conform with the + # general big_vision interface, cf. the core models in big_vision/models/. + del model_params + + assert malib_ckpt or (not use_ema_params), ( + "Loading EMA parameters is only supported for malib checkpoints.") + + if malib_ckpt: + # Locally disable transfer guard since restore_checkpoint does not allow for + # fine-grained sharding control. + with jax.transfer_guard("allow"): + vaegan_params = flax.training.checkpoints.restore_checkpoint( + init_file, None) + vaegan_params_flat = utils.tree_flatten_with_names(vaegan_params)[0] + prefix_old = "ema_params/" if use_ema_params else "g_params/" + vaegan_params_flat = [(k.replace(prefix_old, "cnn_"), v) + for k, v in vaegan_params_flat if prefix_old in k] + params = utils.tree_unflatten(vaegan_params_flat) + else: + params = flax.core.unfreeze(utils.load_params(init_file)) + + if init_params is not None: + params = common.merge_params(params, init_params, dont_load) + return params diff --git a/big_vision/models/proj/givt/decode.py b/big_vision/models/proj/givt/decode.py new file mode 100644 index 0000000000000000000000000000000000000000..14f685185ff51fa0e47e517c538c68ebc82df8a3 --- /dev/null +++ b/big_vision/models/proj/givt/decode.py @@ -0,0 +1,386 @@ +# Copyright 2024 Big Vision 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. + +"""Autorgregressive sampler for GIVT.""" + +import functools +from typing import Any, Optional + +from big_vision.models.proj.givt import parallel_decode +import flax +from flax import linen as nn +import jax +from jax import lax +from jax import numpy as jnp +import ml_collections + + +def _sample_gmm( + gmm_pdf, + *, + rng, + cfg_inference_weight=None, + gmm_pdf_uncond=None, +): + """Draw a single sample from a GMM.""" + if cfg_inference_weight is not None: + assert gmm_pdf_uncond is not None + gmm_pdf = parallel_decode.CFGDensity( + gmm_pdf, gmm_pdf_uncond, w=cfg_inference_weight, rng=rng + ) + samples = gmm_pdf.sample(seed=rng) + logprobs = gmm_pdf.log_prob(samples) + if logprobs.ndim == 2: + logprobs = logprobs[..., None] + return samples, logprobs + + +# Beam search reshaping utils +def _flatten_samples_dim(x): + """Flattens samples dimension into batch dimension.""" + if x.ndim == 0: # ignore scalars (e.g. cache index) + return x + return x.reshape((x.shape[0] * x.shape[1],) + x.shape[2:]) + + +def _unflatten_samples_dim(x, batch_size, num_samples): + """Unflattens first dimension into batch and samples dimensions.""" + if x.ndim == 0: # ignore scalars (e.g. cache index) + return x + assert batch_size * num_samples == x.shape[0] + return x.reshape((batch_size, num_samples) + x.shape[1:]) + + +def _cache_map(fn, cache, scan=False): + """Maps function over cache.""" + if scan: + # Assuming the chache is scanned over the first dimension, we apply a map + # function over this dimension for scanned models + fn_mod = lambda x: jax.lax.map(fn, x) if x.ndim > 0 else fn(x) + else: + fn_mod = fn + + frozen = isinstance(cache, flax.core.FrozenDict) + if frozen: + cache = flax.core.unfreeze(cache) + flat_cache = flax.traverse_util.flatten_dict(cache) + # Exclude cached relative position bias from beam expansion, etc. + keyvals = {k: v for k, v in flat_cache.items() if k[-1] != "cached_bias"} + keyvals = jax.tree_map(fn_mod, keyvals) + flat_cache.update(keyvals) + new_cache = flax.traverse_util.unflatten_dict(flat_cache) + if frozen: + new_cache = flax.core.freeze(new_cache) + return new_cache + + +@flax.struct.dataclass +class LoopState: + """Internal state of the sampling loop.""" + # Terminology + # b: batch size + # nb: number of beams + # nf: number of fans + # s: seaquence length + # d: feature dimension + rng: jnp.ndarray # PRNGKey of the loop state. + cache: Any # Cache for fast auto-regressive decoding. + sequences: jnp.ndarray # (b * nb, s, d) + logprobs: jnp.ndarray # (b * nb, s, d) + cache_u: Any # Uncond cache if cfg, otherwise None + + +def _create_cache( + labels, + model, + init_sequence, + params, + encoded, + uncond=False, +): + """Creates the cache and returns initial logits.""" + if uncond: + assert labels is not None # Need labels for CFG! + drop_labels = jnp.ones((labels.shape[0],), dtype=jnp.bool_) + else: + drop_labels = None + + def init_cache(model): + return model.decode( + init_sequence, labels, encoded, decode=True, drop_labels=drop_labels + ) + + cache = nn.apply(init_cache, model, mutable=True)(params)[1]["cache"] + + def prefill_cache(model): + return model.prefill( + labels, init_sequence.shape[0], encoded, drop_labels=drop_labels + ) + + # prefill class label or BOS token + prefill_logits, aux = nn.apply(prefill_cache, model, mutable=True)( + {"params": params["params"], "cache": cache}) + cache = aux["cache"] + return cache, prefill_logits + + +def generate( + params: Any, + seed: jax.Array, + *, + model: nn.Module, + seq_len: int, + feature_dim: int, + labels: Optional[jnp.ndarray] = None, + cond_image: Optional[jnp.ndarray] = None, + batch_size: Optional[int] = None, + config: Optional[ml_collections.ConfigDict] = None, +) -> tuple[jax.Array, jax.Array]: + """Sampling loop for GIVT.""" + if model.style != "ar": # pytype: disable=wrong-arg-types + raise ValueError(f"Invalid style: {model.style}") + if model.has_encoder != (cond_image is not None): + raise ValueError("Need cond_image if and only if the model has an encoder!") + + assert labels is not None or batch_size, ( + "Please provide either labels or batch_size.") + + config = config or {} + config = dict(config) # copy + + # For sampling, we support keep_gt (a bool mask), and gt (ground truth) + # tokens to use instead of samples. + keep_gt = config.pop("keep_gt", None) + gt = config.pop("gt", None) + + if isinstance(seed, int): + seed = jax.random.PRNGKey(seed) + + beam_size = config.pop("beam_size", 1) + fan_size = config.pop("fan_size", 1) + + if labels is not None: + batch_size = labels.shape[0] + # fold beams into batch dimension + labels = labels.repeat(beam_size, axis=0) + + # initialize sequence and logprobs (we track per feature dim logprobs) + init_sequence = jnp.zeros((batch_size * beam_size, seq_len, feature_dim)) + init_logprobs = jnp.zeros_like(init_sequence) + + if cond_image is not None: + # embed conditioning image if provided + def encode_cond_img(model, cond_img): + return model.encode(cond_img) + encoded = nn.apply(encode_cond_img, model)(params, cond_image) + encoded = jnp.repeat(encoded, beam_size, axis=0) + else: + encoded = None + + cache, prefill_logits = _create_cache( + labels, model, init_sequence, params, encoded + ) + + cfg_inference_weight = config.pop("cfg_inference_weight", None) + if cfg_inference_weight == 0.0: + cfg_inference_weight = None + cfg = cfg_inference_weight is not None + + get_pdf = functools.partial( + model.get_pdf, + temperature_scales=config.pop("temp", None), + temperature_probs=config.pop("temp_probs", None), + ) + + # setup sampling function + sample = functools.partial( + _sample_gmm, cfg_inference_weight=cfg_inference_weight + ) + + # draw first output token + pdf_first = get_pdf(prefill_logits) + rng_first, rng = jax.random.split(seed) + + if cfg: + assert beam_size == 1 and fan_size == 1 # CFG + Beam not supported. + cache_u, prefill_logits_u = _create_cache( + labels, model, init_sequence, params, encoded, uncond=True + ) + pdf_first_u = get_pdf(prefill_logits_u) + else: + cache_u = None + pdf_first_u = None + + tokens_first, logprobs_first = sample( + pdf_first, rng=rng_first, gmm_pdf_uncond=pdf_first_u + ) + init_sequence = init_sequence.at[:, 0].set(tokens_first.squeeze(axis=1)) + init_logprobs = init_logprobs.at[:, 0].set(logprobs_first.squeeze(axis=1)) + + def tokens_to_logits(tokens, cache, uncond=False): + if uncond: + drop_labels = jnp.ones((labels.shape[0],), dtype=jnp.bool_) + else: + drop_labels = None + + def decode_step(model, tokens): + return model.decode(tokens, labels, encoded, + decode=True, drop_labels=drop_labels) + + logits, aux = nn.apply(decode_step, model, mutable=True)( + {"params": params["params"], "cache": cache}, tokens) + return logits, aux["cache"] + + init_state = LoopState( + cache=cache, + sequences=init_sequence, # (b * nb, s, d) + logprobs=init_logprobs, # (b * nb, s, d) + rng=rng, + cache_u=cache_u, + ) + + rand_top_k = config.pop("rand_top_k", False) + rand_top_k_temp = config.pop("rand_top_k_temp", 1.0) + + assert not config, f"Sampling config is expected to be empty: {config}" + + def sampling_iteration(i, state): + rng_sampling, rng_local = jax.random.split(state.rng) + cur_tokens = state.sequences[:, i][:, None] + # (b * nb, d) + cur_logits, cache = tokens_to_logits(cur_tokens, state.cache) + + # (b, nb, d) + cur_logits = _unflatten_samples_dim( + cur_logits, batch_size, beam_size).squeeze(axis=2) + + # (b, nb * nf, d) + cur_pdf = get_pdf(cur_logits.repeat(fan_size, axis=1)) + + if cfg: + cur_logits_u, cache_u = tokens_to_logits( + cur_tokens, state.cache_u, uncond=True + ) + cur_logits_u = _unflatten_samples_dim( + cur_logits_u, batch_size, beam_size).squeeze(axis=2) + cur_pdf_u = get_pdf(cur_logits_u.repeat(fan_size, axis=1)) + new_tokens, new_logprobs = sample( + cur_pdf, rng=rng_sampling, gmm_pdf_uncond=cur_pdf_u + ) + else: + new_tokens, new_logprobs = sample(cur_pdf, rng=rng_sampling) + cache_u = None + + if gt is not None: + assert keep_gt is not None + new_tokens = jnp.where(keep_gt[i], gt[:, i, :][:, None], new_tokens) + + # Skip beam search if not needed + if beam_size == fan_size == 1: + sampled_tokens = new_tokens.squeeze(axis=1) + sequences = state.sequences.at[:, i + 1].set(sampled_tokens) + return LoopState( + cache=cache, + rng=rng_local, + sequences=sequences, + logprobs=state.logprobs, + cache_u=cache_u, + ) + + # (b, nb, s, d) + logprobs = _unflatten_samples_dim(state.logprobs, batch_size, beam_size) + cur_logprobs = logprobs[:, :, i] # (b, nb, d) + # (b, nb * nf, d) + new_logprobs = new_logprobs + cur_logprobs.repeat(fan_size, axis=1) + beam_logprobs = new_logprobs.sum(axis=-1) # (b, nb * nf) + + if rand_top_k: + # randomize top-k sampling via sampling from a categorical distribution + def stoc_top_k(r, x, p): + return jax.random.choice(r, x, shape=(beam_size,), replace=False, p=p) + # construct index grid + index_grid = jnp.arange(beam_logprobs.shape[1], dtype=jnp.int32) + # (b, nb * nf) + index_grid = index_grid[None].repeat(beam_logprobs.shape[0], axis=0) + top_k_rng, rng_local = jax.random.split(rng_local) + top_k_rng = jax.random.split(top_k_rng, beam_logprobs.shape[0]) + # vmap categorical sampling + top_beam_fan_indices = jax.vmap(stoc_top_k, in_axes=(0, 0, 0))( + top_k_rng, + index_grid, + nn.softmax(beam_logprobs / rand_top_k_temp, axis=-1)) + else: + _, top_beam_fan_indices = lax.top_k(beam_logprobs, k=beam_size) # (b, nb) + + top_beam_indices = top_beam_fan_indices // fan_size + + def _gather_beams(x): + if x.ndim == 0: + return x + # checkify.check(jnp.all(top_beam_indices < x.shape[1]), + # f"`take_along_axis` out of bounds in `_gather_beams`: " + # f"{top_beam_indices.max()} vs. {x.shape[1]}") + # (b, nb, 1 ... 1) + expanded_indices = top_beam_indices.reshape( + top_beam_indices.shape + (1,) * (x.ndim - 2)) + return jnp.take_along_axis(x, expanded_indices, axis=1) + + def _gather_tokens(x): + # (b, nb * nf, d) -> (b, nb, d) + # checkify.check(jnp.all(top_beam_fan_indices < x.shape[1]), + # f"`take_along_axis` out of bounds in `_gather_tokens`: " + # f"{top_beam_fan_indices.max()} vs. {x.shape[1]}") + return jnp.take_along_axis(x, top_beam_fan_indices[..., None], axis=1) + # (b, nb, s, d) + sequences = _unflatten_samples_dim(state.sequences, batch_size, beam_size) + sequences = _gather_beams(sequences) # (b, nb, s, d) + sequences = sequences.at[:, :, i + 1].set(_gather_tokens(new_tokens)) + # (b, nb, s, d) + sequences = _flatten_samples_dim(sequences) + + logprobs = _gather_beams(logprobs) + logprobs = logprobs.at[:, :, i + 1].set(_gather_tokens(new_logprobs)) + logprobs = _flatten_samples_dim(logprobs) + + scanned_cache = getattr(model, "scan", False) + cache = _cache_map( + lambda x: _unflatten_samples_dim(x, batch_size, beam_size), + cache, scanned_cache) + cache = _cache_map(_gather_beams, cache, scanned_cache) + cache = _cache_map(_flatten_samples_dim, cache, scanned_cache) + + if cfg: + assert cache_u is not None + cache_u = _cache_map( + lambda x: _unflatten_samples_dim(x, batch_size, beam_size), + cache_u, scanned_cache + ) + cache_u = _cache_map(_gather_beams, cache_u, scanned_cache) + cache_u = _cache_map(_flatten_samples_dim, cache_u, scanned_cache) + else: + assert cache_u is None + + return LoopState( + cache=cache, + rng=rng_local, + sequences=sequences, + logprobs=logprobs, + cache_u=cache_u, + ) + + final_state = lax.fori_loop(0, seq_len, sampling_iteration, init_state) + final_logprobs = final_state.logprobs[::beam_size][:, -1].sum(axis=-1) + + # return top beams and corresponding log probs + return final_state.sequences[::beam_size], final_logprobs diff --git a/big_vision/models/proj/givt/decode_test.py b/big_vision/models/proj/givt/decode_test.py new file mode 100644 index 0000000000000000000000000000000000000000..53b41c09c9bc6a842ac4d565ae5de9a05562819d --- /dev/null +++ b/big_vision/models/proj/givt/decode_test.py @@ -0,0 +1,121 @@ +# Copyright 2024 Big Vision 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. + +from absl.testing import parameterized +from big_vision.models.proj.givt import decode +from big_vision.models.proj.givt import givt +import jax +import jax.numpy as jnp + +from absl.testing import absltest + + +_BATCH_SIZE = 2 +_OUT_DIM = 4 +_IMG_DIM = 8 +_PATCH_SIZE = 2 +_SEQ_LEN = _IMG_DIM // _PATCH_SIZE * _IMG_DIM // _PATCH_SIZE +_NUM_MIXTURES = 4 + + +def _make_test_model(**overwrites): + config = dict( + num_heads=2, + num_decoder_layers=1, + mlp_dim=64, + emb_dim=16, + patches=(_PATCH_SIZE, _PATCH_SIZE), + input_size=(_IMG_DIM, _IMG_DIM), + seq_len=_SEQ_LEN, + out_dim=_OUT_DIM, + num_mixtures=_NUM_MIXTURES, + style="ar", + ) + config.update(overwrites) + return givt.Model(**config) + + +class DecodeTest(parameterized.TestCase): + + def _make_model(self, **overwrites): + model = _make_test_model(**overwrites) + sequence = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM) + ) + labels = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE,), maxval=10 + ).astype(jnp.int32) + variables = model.init( + jax.random.PRNGKey(0), + sequence, + labels, + train=False, + image=jnp.zeros((_BATCH_SIZE, _IMG_DIM, _IMG_DIM, 3), dtype=jnp.float32) + if model.has_encoder + else None, + ) + return model, variables + + def _test_model(self, rng, model, variables, config): + labels = jnp.ones((_BATCH_SIZE,), dtype=jnp.int32) + if model.has_encoder: + cond_image = jnp.zeros( + (_BATCH_SIZE, _IMG_DIM, _IMG_DIM, 3), dtype=jnp.float32 + ) + else: + cond_image = None + result, logprobs = decode.generate( + params=variables, + seed=rng, + seq_len=_SEQ_LEN, + feature_dim=_OUT_DIM, + labels=labels, + model=model, + config=config, + cond_image=cond_image, + ) + # TODO: More expressive tests? Eg for causality, and caching. + self.assertEqual(result.shape, (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM)) + self.assertTrue(jnp.allclose(logprobs, jnp.zeros_like(logprobs), atol=1e-5)) + + @parameterized.product( + rng_seed=[1, 2], + encoder=[True, False], + ) + def test_simple(self, rng_seed, encoder): + rng = jax.random.PRNGKey(rng_seed) + model, variables = self._make_model( + num_layers=1 if encoder else 0 + ) + assert model.has_encoder == encoder + self._test_model(rng, model, variables, config={}) + + @parameterized.product( + rng_seed=[1, 2], + cfg_inference_weight=[0.0, 1.0, 3.0], + per_channel_mixtures=[True, False], + ) + def test_cfg(self, rng_seed, cfg_inference_weight, per_channel_mixtures): + rng = jax.random.PRNGKey(rng_seed) + model, variables = self._make_model( + num_mixtures=1 if per_channel_mixtures else 3, + drop_labels_probability=0.1, + per_channel_mixtures=per_channel_mixtures, + ) + config = {"cfg_inference_weight": cfg_inference_weight} + self._test_model(rng, model, variables, config) + + +if __name__ == "__main__": + googletest.main() diff --git a/big_vision/models/proj/givt/givt.py b/big_vision/models/proj/givt/givt.py new file mode 100644 index 0000000000000000000000000000000000000000..0bd4c84427fde5873c33dd7e76e6a289e163cb22 --- /dev/null +++ b/big_vision/models/proj/givt/givt.py @@ -0,0 +1,820 @@ +# Copyright 2024 Big Vision 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. + +"""Decoder-only and encoder-decoder GIVT model. + +Used abbreviations for dimension annotations: + B: batch size. + E: embedding size. + L: (soft) token sequence length. + D: soft token dimension. + P: number of patches (extracted by a ViT encoder in GIVT-based UViM) +""" + +import enum +import itertools +from typing import Literal, Optional, Sequence, Any, Mapping + +from absl import logging +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import distrax +import einops +import flax.linen as nn +from flax.linen import partitioning +import jax +import jax.numpy as jnp +import numpy as np + + +class _SpecialLabel(enum.Enum): + + MASK = "mask" + NOMASK = "nomask" + REPLACE = "replace" + NOLABEL = "nolabel" # For CFG + + +def _random_mask_with_ratios(rng, ratios: jax.Array, seq_len: int): + """Generates masks where a fraction of tokens is uncovered. + + Args: + rng: RNG. + ratios: Ratios, must be a 1D matrix of shape (B,). Values must be in + [0, 1], and indicate at ratios[i] how many of the i-th tokens are + uncovered (ie. equal to `True`). + seq_len: How many tokens this mask has to cover. + + Returns: + Mask of dtype bool, shape (B, L). + + Raises: + ValueError: Incorrect inputs. + """ + if ratios.ndim != 1: + raise ValueError("Ratios must have shape (B,)!") + ratios = jnp.clip(ratios, 0, 1) + indices = jnp.arange(seq_len, dtype=jnp.float32) # Shape: (L,) + ratios = ratios[:, jnp.newaxis] * seq_len # Shape: (B, 1) + # This is a binary array where the first ratios * seq_len positions are True + mask = (indices < ratios).astype(jnp.bool_) # Shape: (B, L) + # Shuffle to a actual mask. + return jax.random.shuffle(rng, mask, axis=-1) + + +def apply_mask_schedule(ratio: float | jax.Array, method: str) -> jax.Array: + """Generate a mask rate by scheduling mask functions R.""" + if method == "cosine": + mask_ratio = jax.lax.cos(jnp.pi / 2. * ratio) + elif "pow:" in method: + exponent = float(method.replace("pow:", "")) + mask_ratio = 1. - ratio**exponent + else: + raise NotImplementedError(method) + # Clamps mask into [epsilon, 1) + mask_ratio = jnp.clip(mask_ratio, 1e-6, 1.) + return mask_ratio + + +class EncoderDecoderBlock(nn.Module): + """Transformer encoder-decoder layer.""" + mlp_dim: int + num_heads: int + dropout_rate: float = 0. + decode: bool = False + + @nn.compact + def __call__( + self, + targets: jax.Array, + encoded: jax.Array | None = None, + decoder_mask: jax.Array | None = None, + deterministic: bool = True, + ) -> tuple[jax.Array, jax.Array]: + """Applies EncoderDecoderBlock module. + + Args: + targets: target text embeddings [B, L, D]. + encoded: encoded image patches from encoder [B, P, E]. + decoder_mask: decoder self-attention mask. + deterministic: bool, deterministic or not (to apply dropout). + + Returns: + output after transformer encoder-decoder block [B, L, E]. + """ + # Helper function for axis annotation. + def wlc(f): + dim_names = ("act_batch", "act_len", "act_emb") + return nn.with_logical_constraint(f, dim_names) + # Decoder block. + x = wlc(nn.LayerNorm(name="LayerNorm1", use_bias=False)(targets)) + x = wlc(nn.SelfAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, decode=self.decode, name="SelfAttn")( + x, decoder_mask, deterministic=deterministic)) + x = wlc(nn.Dropout(rate=self.dropout_rate)(x, deterministic=deterministic)) + x = wlc(x + targets) + + if encoded is None: + y = x + else: + # Encoder-Decoder block. + y = wlc(nn.LayerNorm(name="LayerNorm2", use_bias=False)(x)) + y = wlc(nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, name="CrossAttn")( + y, encoded, deterministic=deterministic)) + y = wlc( + nn.Dropout(rate=self.dropout_rate)(y, deterministic=deterministic)) + y = wlc(y + x) + + # MLP block. + z = wlc(nn.LayerNorm(name="LayerNorm3", use_bias=False)(y)) + z = wlc(vit.MlpBlock(mlp_dim=self.mlp_dim, dropout=self.dropout_rate, + name="MLP")(z, deterministic=deterministic)) + + # nn.scan requires a carry (second element in tuple) + out = wlc(y + z) + return out, out + + +class Decoder(nn.Module): + """Transformer decoder model with optional cross-attention.""" + emb_dim: int + mlp_dim: int + num_heads: int + num_layers: int + out_dim: int + seq_len: int + style: Literal["ar", "masked"] + dropout_rate: float = 0. + zero_embedding_init: bool = False + + scan: bool = False + remat_policy: str = "nothing_saveable" + + @nn.compact + def __call__( + self, + targets: jax.Array, + encoded: jax.Array | None = None, + decoder_mask: jax.Array | None = None, + decode: bool = False, + deterministic: bool = True, + return_reps: bool = False, + ) -> jax.Array | tuple[jax.Array, Mapping[str, jax.Array]]: + """Applies Transformer model on the inputs. + + Args: + targets: target text tokens [B, L]. + encoded: encoded sequence from an encoder [B, P, E]. + decoder_mask: decoder self-attention mask. + decode: bool, whether to perform fast autoregressive decoding with cache. + deterministic: bool, deterministic or not (to apply dropout). + return_reps: bool, whether to return intermediate representations. + + Returns: + output of a transformer decoder [B, L, out_dim], where out_dim is usually + a multiple of D. + """ + if self.style == "masked" and decode: + raise ValueError("Cannot run masked model in cached mode!") + + pos_emb = vit.get_posemb( + self, "learn", self.seq_len, self.emb_dim, + "pos_emb") + + y = common.AddPositionEmbs( + decode=decode, name="PosEmbedTargets")(targets, pos_emb) + + out = {} + if self.scan: + # Mostly followed + # https://github.com/google/maxtext/blob/4d99e30b3e0e0cb1d1aa11c7db7fffe18e301498/MaxText/layers.py#L1126 + # for the scanned version. + + # 1. remat + enc_dec_block_remat = nn.remat( + EncoderDecoderBlock, + prevent_cse=False, + static_argnums=(-1, -2), + policy=getattr(jax.checkpoint_policies, self.remat_policy, None)) + # 2. scan + initializing = self.is_mutable_collection("params") + param_scan_axis = 1 + params_spec = (param_scan_axis if initializing + else partitioning.ScanIn(param_scan_axis)) + dec_scanned = nn.scan(enc_dec_block_remat, + variable_axes={ + "params": params_spec, + "cache": 0, + }, + split_rngs={"params": True, "dropout": True}, + in_axes=nn.broadcast, + length=self.num_layers) + # 3. fprop + y, out = dec_scanned(num_heads=self.num_heads, mlp_dim=self.mlp_dim, + dropout_rate=self.dropout_rate, decode=decode, + name="EncDecBlock")( + y, encoded, decoder_mask, deterministic) + # Extracting the intermediate representation from the stacked activation + # tensor `out`, which is a [num_layers, B, L, E] tensor. Indexing along + # the first axis to extract individual layers, and then averaging across + # the second axis, which corresponds to the sequence dimension after + # indexing. + assert out.shape[0] == self.num_layers and ( + decode or out.shape[2] == self.seq_len), ( + (out.shape, self.num_layers, self.seq_len)) + out = {f"block{l}_rep": jnp.mean(out[l], axis=1) + for l in range(self.num_layers)} + else: + for lyr in range(self.num_layers): + y, _ = EncoderDecoderBlock( + num_heads=self.num_heads, mlp_dim=self.mlp_dim, + dropout_rate=self.dropout_rate, decode=decode, + name=f"EncDecBlock{lyr}")(y, encoded, decoder_mask=decoder_mask, + deterministic=deterministic) + out[f"block{lyr}_rep"] = jnp.mean(y, axis=1) + y = nn.LayerNorm(name="LayerNorm")(y) + out["pre_logits"] = jnp.mean(y, axis=1) + + logits = nn.Dense( + self.out_dim, + kernel_init=nn.initializers.zeros, + name="LogitsDense", + )(y) + out["logits"] = logits + if return_reps: + return logits, out + return logits + + +class Model(nn.Module): + """GIVT model supporting decoder-only and encoder-decoder applications.""" + num_heads: int = 8 + # num_layers = 0 means no encoder + num_layers: int = 0 + num_decoder_layers: int = 6 + mlp_dim: int = 2048 + enc_dropout_rate: float = 0. + dec_dropout_rate: float = 0. + # Decoder params: + emb_dim: int = 512 + num_labels: Optional[int] = 1000 + seq_len: int = 256 + # Encoder params: + patches: Sequence[int] = (16, 16) + input_size: Sequence[int] = (256, 256) + posemb_type: Literal["learn", "sincos2d"] = "learn" + zero_decoder_seq: bool = False + style: Literal["ar", "masked"] = "ar" + + zero_embedding_init: bool = False + + num_mixtures: int = 4 + multivariate: bool = False + out_dim: int = 32 + scale_tol: float = 1e-6 + + # Mask specific params. + mask_schedule_train: str = "cosine" + # Results in at least 40% masked tokens with cosine. + min_masking_rate_training: float = 0.3 + + # How to fuse mask at input: + # - replace: replace token[masked] with lookup(MASK) + # - concat: replace token[mask] with lookup(REPLACE) and concat either + # lookup(NOMASK) or lookup(MASK). + mask_style: str = "replace" + + # Set to >0 for CFG support. + drop_labels_probability: float = 0.0 + + fix_square_plus: bool = False + + # If True, and mixture >1, create a GMM per channel. Otherwise, create + # a GMM of `dim`-dimensional Gaussians. + per_channel_mixtures: bool = True + + scan: bool = False + remat_policy: str = "nothing_saveable" + + @property + def has_encoder(self) -> bool: + return self.num_layers > 0 + + @property + def num_logits(self) -> int: + if self.multivariate: + assert self.num_mixtures == 1 + # d**2 covariance, d means. + # Note: `round` makes pytype happy. + return round(self.out_dim ** 2) + self.out_dim + + elif self.per_channel_mixtures: + # One (mu, sigma, pi) per output dimension and mixture component. + # Note that we predict a distribution for each output dimensions in + # parallel. + return 3 * self.num_mixtures * self.out_dim + + else: + # Mixture weights plus mean/scale per mixture + return self.num_mixtures + 2 * self.num_mixtures * self.out_dim + + def setup(self) -> None: + assert self.posemb_type == "learn" + assert self.num_mixtures > 0 + + if self.multivariate and self.num_mixtures != 1: + raise ValueError("Cannot do multivariate GMM!") + + if self.num_layers > 0: + grid_size = np.array(self.input_size) // np.array(self.patches) + + self.pos_emb_for_encoder = vit.get_posemb( + self, self.posemb_type, grid_size, self.emb_dim, + "pos_embedding_encoder") + + self.conv = nn.Conv(self.emb_dim, self.patches, padding="VALID", + strides=self.patches, name="EmbedPatches") + + self.encoder = vit.Encoder( + depth=self.num_layers, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.enc_dropout_rate, + scan=self.scan, + remat_policy=self.remat_policy,) + else: + self.encoder = None + + # Iterator that will lead free label IDs. + next_label = itertools.count(self.num_labels or 0) + special_labels = {} + + if self.style == "ar": + pass + elif self.style == "masked": + if self.mask_style == "replace": + special_labels = {_SpecialLabel.MASK: next(next_label)} + elif self.mask_style == "concat": + special_labels = { + _SpecialLabel.MASK: next(next_label), + _SpecialLabel.NOMASK: next(next_label), + _SpecialLabel.REPLACE: next(next_label), + } + else: + raise NotImplementedError(self.mask_style) + else: + raise NotImplementedError(self.style) + + if self.drop_labels_probability > 0: + special_labels[_SpecialLabel.NOLABEL] = next(next_label) + + self.special_labels = special_labels + lookup_size = (self.num_labels or 1) + len(self.special_labels) + + self.labels_emb = nn.Embed( + lookup_size, + self.emb_dim, + name="EmbedLabels", + embedding_init=nn.initializers.zeros + if self.zero_embedding_init + else nn.initializers.normal(stddev=1.0), + ) + + self.targets_emb = nn.Dense(self.emb_dim, name="EmbedTargets") + + self.decoder = Decoder( + num_layers=self.num_decoder_layers or self.num_layers, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + out_dim=self.num_logits, + # In masked mode, we run with 1 more token at the input. + seq_len=self.seq_len + int(self.style == "masked"), + dropout_rate=self.dec_dropout_rate, + emb_dim=self.emb_dim, + zero_embedding_init=self.zero_embedding_init, + style=self.style, + scan=self.scan, + remat_policy=self.remat_policy, + ) + + def encode(self, image: jax.Array, train: bool = False) -> jax.Array: + """Encodes input image or embeddings.""" + emb = self.conv(image) + patch_embeddings = einops.rearrange(emb, "B PH PW E -> B (PH PW) E") + encoded, _ = self.encoder( + patch_embeddings + self.pos_emb_for_encoder, deterministic=not train) + return encoded + + def embed_labels( + self, + labels: jax.Array | None = None, + batch_size: int | None = None, + ) -> jax.Array: + if labels is not None: + # Embed class label, add a sequence dim (output shape (B, 1, E)) + return self.labels_emb(labels)[:, None, :] + + assert ((self.num_labels == 1 or self.num_labels is None) + and batch_size is not None) + # Create [BOS] token embedding + return self.labels_emb(jnp.zeros((batch_size,), jnp.int32))[:, None, :] + + def prefill( + self, labels=None, batch_size=None, encoded=None, drop_labels=None + ): + labels = self._drop_labels(drop_labels, labels) + labels_for_prefill = self.embed_labels(labels=labels, batch_size=batch_size) + return self.decoder( + labels_for_prefill, + encoded=encoded, + decode=True) + + def _decode_ar( + self, + targets: jax.Array, + labels: jax.Array | None = None, + encoded: jax.Array | None = None, + decode: bool = False, + train: bool = False, + ) -> tuple[jax.Array, Mapping[str, jax.Array]]: + """Autoregressive decoding.""" + targets_embedded = self.targets_emb(targets) + + if decode: + decoder_mask = None + else: + decoder_mask = nn.make_causal_mask(targets[:, :, 0]) + b = targets.shape[0] + labels_embedded = self.embed_labels(labels, b) + assert labels_embedded.shape == (b, 1, self.emb_dim), ( + labels_embedded.shape, (b, 1, self.emb_dim)) + targets_embedded = jnp.concatenate( + [labels_embedded, targets_embedded[:, : -1]], axis=1) + + logits, out = self.decoder( + targets_embedded, + encoded=encoded, + decoder_mask=decoder_mask, + decode=decode, + deterministic=not train, + return_reps=True) + + return logits, out + + def _get_special_label(self, size, label: _SpecialLabel): + return self.labels_emb( + jnp.full(size, self.special_labels[label], jnp.int32) + ) + + def _decode_masked( + self, + targets, + input_mask, + labels=None, + encoded=None, + train=False, + ): + """Masked decoding.""" + b, s, _ = targets.shape + assert input_mask.shape == (b, s) + + if self.mask_style == "replace": + targets_embedded = jnp.where( + input_mask[:, :, None], + self._get_special_label((b, s), _SpecialLabel.MASK), + self.targets_emb(targets), + ) + elif self.mask_style == "concat": + masks = jnp.where( + input_mask[:, :, None], + self._get_special_label((b, s), _SpecialLabel.MASK), + self._get_special_label((b, s), _SpecialLabel.NOMASK), + ) + embedded_targets = self.targets_emb(targets) + targets_embedded = jnp.where( + input_mask[:, :, None], + self._get_special_label((b, s), _SpecialLabel.REPLACE), + embedded_targets, + ) + # Only take half of each to get the right embedding size. + targets_embedded = jnp.concatenate( + [masks[..., ::2], targets_embedded[..., ::2]], axis=-1 + ) + else: + raise ValueError(self.mask_style) + + labels_embedded = self.embed_labels(labels, b) + assert labels_embedded.shape == (b, 1, self.emb_dim) + # Note that we do not truncate the input here, so this has shape + # (B, L+1, E). + targets_embedded = jnp.concatenate( + [labels_embedded, targets_embedded], axis=1) + + logits = self.decoder( + targets_embedded, + encoded=encoded, + decoder_mask=None, + decode=False, + deterministic=not train) + + logits = logits[:, 1:, ...] # Remove class label + assert logits.shape[:2] == (b, s) + return logits + + def _drop_labels(self, drop_labels_mask, labels): + if labels is None: + return None + if self.drop_labels_probability >= 0.999: + logging.warning("Dropping all labels...") + return jnp.full_like(labels, self.special_labels[_SpecialLabel.NOLABEL]) + if drop_labels_mask is None: + return labels + assert _SpecialLabel.NOLABEL in self.special_labels + nolabel = jnp.full_like( + labels, self.special_labels[_SpecialLabel.NOLABEL] + ) + return jnp.where(drop_labels_mask, nolabel, labels) + + def decode( + self, + targets: jax.Array, + labels: jax.Array | None = None, + encoded: jax.Array | None = None, + decode: bool = False, + train: bool = False, + max_decode_length: int | None = None, + input_mask: jax.Array | None = None, + drop_labels: jax.Array | None = None, + return_reps: bool = False, + ) -> jax.Array | tuple[jax.Array, Mapping[str, jax.Array]]: + """Applies Transformer decoder-branch on encoded-input and target. + + Args: + targets: target text tokens [B, L, out_dim]. + labels: optional class labes, [B]. + encoded: encoded image patches from encoder [B, P, E]. + decode: whether to prepare and use an autoregressive cache. + train: whether it is training. + max_decode_length: optional max length for positional embeddings. + input_mask: If given, mask input. Required for style=="masked". + Shape [B, L], bool tensor. True means the token will be removed + from the input. + drop_labels: Drop labels at corresponding locations [B]. + return_reps: whether to return intermediate representations. + + Returns: + logits array from transformer decoder [B, L, 3 * num_mixtures * out_dim]. + """ + del max_decode_length + labels = self._drop_labels(drop_labels, labels) + if self.style == "ar": + logits, out = self._decode_ar( + targets, labels, encoded, decode, train) + if return_reps: + return logits, out + return logits + elif self.style == "masked": + assert not decode # Cache not supported. + assert input_mask is not None + assert not return_reps # Not implemented. + return self._decode_masked(targets, input_mask, labels, encoded, train) + else: + raise NotImplementedError(self.style) + + def _square_plus(self, x): + # Via https://twitter.com/jon_barron/status/1387167648669048833 + if self.fix_square_plus: + return (x + jnp.sqrt(jnp.square(x) + 4)) / 2 + else: + return x + jnp.sqrt(jnp.square(x) + 4) / 2 + + def get_pdf( + self, + logits: jax.Array, + temperature_scales: float | None = None, + temperature_probs: float | None = None, + ) -> distrax.Distribution: + assert logits.shape[-1] == self.num_logits + if self.multivariate: + scales = logits[..., :self.out_dim ** 2] + locs = logits[..., self.out_dim ** 2:] + assert locs.shape[-1] == self.out_dim + scales = self._square_plus(scales) + # Turn into a square matrix. + *leading, _ = scales.shape + scales = scales.reshape(*leading, self.out_dim, self.out_dim) + # Make sure the diagonals are non zero. + diag_scale_tol = jnp.eye(self.out_dim) * self.scale_tol + scales = jnp.maximum(scales, diag_scale_tol) + if (t := temperature_scales) is not None: + scales = scales * t + + # Note that there is `tfd.MultivariateNormalFullCovariance`` but it just + # calls linalg.cholesky on the covariance and then uses the + # MultivariateNormalTri class. Using ... direcly avoids having to + # construct a hermetian matrix. + # + # Note that only the lower triag part of `scales` is used by applying + # jnp.tril. The other elements are replaced with zeros. + # + # Note on output shapes: + # - .sample() -> shape (..., seq_len, out_dim) + # - .prob() -> shape (..., seq_len). + return distrax.MultivariateNormalTri(locs, scales) + + elif self.per_channel_mixtures: + # [..., 3 * num_mixtures * out_dim] -> [..., 3 * out_dim, num_mixtures] + logits = jnp.reshape(logits, logits.shape[: -1] + (-1, self.num_mixtures)) + # 3 tensors with shape [..., out_dim, num_mixtures] + probs, locs, scales = jnp.split(logits, 3, axis=-2) + if (t := temperature_probs) is not None: + probs = probs * t + + # normalize mixture probabilities + probs = nn.softmax(probs) + scales = self._square_plus(scales) + # threshold scale + scales = jnp.maximum(scales, self.scale_tol) + if (t := temperature_scales) is not None: + scales = scales * t + + # Note on output shapes: + # - .sample() -> shape (..., seq_len, out_dim) + # - .prob() -> shape (..., seq_len, out_dim). + return distrax.MixtureSameFamily( + mixture_distribution=distrax.Categorical(probs=probs), + components_distribution=distrax.Normal(loc=locs, scale=scales), + ) + else: + *shape, num_logits = logits.shape + assert num_logits == self.num_logits, (num_logits, self.num_logits) + prob_logits, other_logits = ( + logits[..., : self.num_mixtures], + logits[..., self.num_mixtures :], + ) + if (t := temperature_probs) is not None: + prob_logits = prob_logits * t + other_logits = jnp.reshape( + other_logits, (*shape, self.num_mixtures, 2, self.out_dim) + ) + locs = other_logits[..., 0, :] + scales = self._square_plus(other_logits[..., 1, :]) + + scales = jnp.maximum(scales, self.scale_tol) # Threshold scale + if (t := temperature_scales) is not None: + scales = scales * t + + # prob_logits has shape (b, seq_len, m) + # locs/scales has shape (b, seq_len, m, d) + assert prob_logits.ndim == locs.ndim - 1, (prob_logits.shape, locs.shape) + assert locs.shape == scales.shape, (locs.shape, scales.shape) + + # Note on output shapes: + # - .sample() -> shape (..., seq_len, out_dim) + # - .prob() -> shape (..., seq_len,) + # - .nll() -> shape (..., seq_len,) + return distrax.MixtureSameFamily( + mixture_distribution=distrax.Categorical(logits=prob_logits), + components_distribution=distrax.MultivariateNormalDiag( + loc=locs, scale_diag=scales + ), + ) + + def __call__( + self, + sequence: jax.Array, + labels: jax.Array | None = None, + *, + image: jax.Array | None = None, + decode: bool = False, + input_mask: jax.Array | None = None, + drop_labels: jax.Array | None = None, + train: bool = False, + ) -> tuple[jax.Array, distrax.Distribution]: + """Applies Transformer model on the inputs. + + Args: + sequence: batch of sequences [B, L]. + labels: class labels for class conditional generation [B]. + image: batch of images [B, H, W, 3]. + decode: whether to prepare and use an autoregressive cache. + input_mask: If given, mask input. Required for style=="masked" [B, L]. + drop_labels: If given, drop labels of the corresponding batches [B]. + train: whether it is training. + + Returns: + logits array from full transformer [B, L, out_dim]. + """ + if self.style == "masked" and input_mask is None: + raise ValueError("Cannot run masked model without input mask!") + + if self.encoder is not None: + assert image is not None + encoded = self.encode(image, train=train) + else: + assert image is None + encoded = None + + logits = self.decode(sequence, labels=labels, encoded=encoded, + decode=decode, input_mask=input_mask, train=train) + pdf = self.get_pdf(logits) + return logits, pdf + + def get_input_mask_training( + self, + rng: jax.Array, + shape: tuple[int, int], + ) -> jax.Array | None: + """Creates a random maask of shape (B, L) for training masked models.""" + if self.style == "ar": + return None + b, s = shape + # Sample b values in [0, 1-min_mask_ratio]. + keep = jax.random.uniform( + rng, shape=(b,), maxval=1.0 - self.min_masking_rate_training + ) + mask_ratio = apply_mask_schedule(keep, self.mask_schedule_train) + return _random_mask_with_ratios(rng, ratios=mask_ratio, seq_len=s) + + def get_input_mask_teacher_forced( + self, + shape: tuple[int, int], + ) -> jax.Array | None: + """Creates a random maask of shape (B, L) for training masked models.""" + if self.style == "ar": + return None + return jnp.zeros(shape, dtype=jnp.bool_) + + def get_drop_labels( + self, + rng: jax.Array, + batch_size: int, + ) -> jax.Array | None: + if (p := self.drop_labels_probability) > 0: + return jax.random.uniform(rng, shape=(batch_size,)) <= p + else: + return None + + +def load( + init_params: Any, + init_files: str | Mapping[str, str], + model_params: Any = None, + dont_load: Sequence[str] = (), + resample_encoder_posemb: bool = False, + trim_decoder_posemb: bool = False, +) -> Any: + """Loads params from init checkpoint and merges into init_params.""" + del model_params + if isinstance(init_files, str): + ckpt_params = utils.load_params(init_files) + ckpt_params = common.merge_params(ckpt_params, init_params, dont_load) + + if resample_encoder_posemb: + if init_params and "pos_embedding_encoder" in init_params: + ckpt_params["pos_embedding_encoder"] = vit.resample_posemb( + old=ckpt_params["pos_embedding_encoder"], + new=init_params["pos_embedding_encoder"]) + + if trim_decoder_posemb: + if init_params and "pos_embedding_decoder" in init_params: + ckpt_params["pos_embedding_decoder"] = ( + ckpt_params["pos_embedding_decoder"][ + :, :init_params["pos_embedding_decoder"].shape[1], :]) + + else: + init_files = {**init_files} # Shallow copy because we'll pop stuff off. + + enc_init = init_files.pop("encoder", None) + if enc_init: + ckpt_params = init_params.copy() + vit_params = { + "pos_embedding": ckpt_params["pos_embedding_encoder"], + "Transformer": ckpt_params["encoder"], + "embedding": ckpt_params["EmbedPatches"], + } + encoder_params = vit.load( + vit_params, enc_init, model_cfg={}, + dont_load=dont_load) + ckpt_params["encoder"] = encoder_params["Transformer"] + ckpt_params["pos_embedding_encoder"] = encoder_params["pos_embedding"] + ckpt_params["EmbedPatches"] = encoder_params["embedding"] + else: + raise ValueError("Only encoder init is supported: {}.".format(init_files)) + + return ckpt_params diff --git a/big_vision/models/proj/givt/givt_test.py b/big_vision/models/proj/givt/givt_test.py new file mode 100644 index 0000000000000000000000000000000000000000..acfc59e73448efffbb34bb73728ca3a52cbd02fb --- /dev/null +++ b/big_vision/models/proj/givt/givt_test.py @@ -0,0 +1,124 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for GIVT model.""" + +from absl.testing import parameterized +from big_vision.models.proj.givt import givt +import jax +import jax.numpy as jnp +import numpy as np + +from absl.testing import absltest + + +_BATCH_SIZE = 2 +_OUT_DIM = 4 +_SEQ_LEN = 16 +_NUM_MIXTURES = 4 + + +def _make_test_model(**overwrites): + config = dict( + num_heads=2, + num_decoder_layers=1, + mlp_dim=64, + emb_dim=16, + seq_len=_SEQ_LEN, + out_dim=_OUT_DIM, + num_mixtures=_NUM_MIXTURES, + ) + config.update(overwrites) + return givt.Model(**config) + + +class MaskedTransformerTest(parameterized.TestCase): + + @parameterized.product(rng_seed=[0]) + def test_masks(self, rng_seed): + m = _make_test_model(style="masked") + mask = m.get_input_mask_training(jax.random.PRNGKey(rng_seed), (2, 16)) + self.assertEqual(mask.shape, (2, 16)) + # At least one should definitly be masked out. + self.assertTrue(np.all(mask.sum(-1) > 1)) + + @parameterized.product( + train=[True, False], + multivariate=[True, False], + per_channel_mixtures=[True, False], + drop_labels_probability=[0.0, 0.1], + style=["masked", "ar"], + ) + def test_apply( + self, + train, + multivariate, + per_channel_mixtures, + drop_labels_probability, + style, + ): + if per_channel_mixtures and multivariate: + self.skipTest("Not supported") + model = _make_test_model( + style=style, + multivariate=multivariate, + num_mixtures=1 if multivariate else _NUM_MIXTURES, + per_channel_mixtures=per_channel_mixtures, + drop_labels_probability=drop_labels_probability, + ) + sequence = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM) + ) + labels = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE,), maxval=10 + ).astype(jnp.int32) + input_mask = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE, _SEQ_LEN) + ).astype(jnp.bool_) + variables = model.init( + jax.random.PRNGKey(0), + sequence, + labels, + input_mask=input_mask, + train=train, + ) + logits, pdf = model.apply( + variables, sequence, labels, input_mask=input_mask, train=train + ) + nll = -pdf.log_prob(sequence) + self.assertFalse(np.any(np.isnan(nll))) + if multivariate: + self.assertEqual( + logits.shape, (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM**2 + _OUT_DIM) + ) + self.assertEqual(nll.shape, (_BATCH_SIZE, _SEQ_LEN)) + elif per_channel_mixtures: + self.assertEqual( + logits.shape, + (_BATCH_SIZE, _SEQ_LEN, 3 * _NUM_MIXTURES * _OUT_DIM), + ) + self.assertEqual(nll.shape, (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM)) + else: + self.assertEqual( + logits.shape, + (_BATCH_SIZE, _SEQ_LEN, _NUM_MIXTURES + _NUM_MIXTURES * _OUT_DIM * 2), + ) + self.assertEqual(nll.shape, (_BATCH_SIZE, _SEQ_LEN)) + + sample = pdf.sample(seed=jax.random.PRNGKey(0)) + self.assertEqual(sample.shape, (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM)) + + +if __name__ == "__main__": + googletest.main() diff --git a/big_vision/models/proj/givt/parallel_decode.py b/big_vision/models/proj/givt/parallel_decode.py new file mode 100644 index 0000000000000000000000000000000000000000..b05903a7e30a870ba5b09b0965932321090f1190 --- /dev/null +++ b/big_vision/models/proj/givt/parallel_decode.py @@ -0,0 +1,523 @@ +# Copyright 2024 Big Vision 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. + +"""Decode autoregressive/bidirectional masked transformers. + + +Currently, we implement MaskGIT style temperature sampling: + +In each step: +1. Get P = model(inputs), predicted GMMs +2. Get samples = sample_from(P) +3. Get probs = P[samples], ie, model evaluated at samples. + We use this now as a confidence metric, but we scale the probs: +4. probs = probs ^ 1/choice_temperature +4. set probs[already_uncovered_points] = inf, ie, we will always keep + uncovered points (no resampling!) +5. Now pick top K points from probs to keep for the next steps, where + K = some monotonically increasing ratio of points as we go along decoding +""" + +import dataclasses +from typing import Literal + +from absl import logging +from big_vision.models.proj.givt import givt +import distrax +import flax +import jax +import jax.numpy as jnp + + +_CONFIDENCE_OF_KNOWN_TOKENS = jnp.inf + + +@jax.vmap +def _get_per_batch_mask(arr, k): + (d,) = arr.shape + indices = jnp.argsort(arr) + valid_indices = jnp.arange(d) < k + return jnp.zeros((d,), jnp.bool_).at[indices].set(valid_indices) + + +def _get_bottom_k_mask(arr, k): + *leading, d = arr.shape + arr = arr.reshape((-1, d)) + mask = _get_per_batch_mask(arr, k) + return mask.reshape(*leading, -1) + + +def mask_by_random_topk(rng, mask_len, probs, temperature=1.0): + """Create a mask. + + Adaption of jax.random.choice where probabilities are changed by scaling with + `temperature` (probs = probs ^ (1/temperature)). + + Additionally, this function returns a mask of tokens to mask out, which + are picked to be the low confidence ones. Thus, this function is roughly + equivalent to (but not exactly at edge cases such as prob = inf..): + + keep = jax.random.choice( + rng, seq_len, + shape=(seq_len - mask_len,), + # NOTE: probabilities are updated with `temperature`. + p=jnp.power(probs, 1/temperature), + replace=False + ) + mask = jnp.ones((seq_len,), dtype=jnp.bool_) + return mask.at[..., keep].set(False) + + Args: + rng: a PRNG key used as the random key. + mask_len: the number to mask. + probs: the probabilities associated with each entry. + temperature: when temperature = 1.0, it's identical to jax's implementation. + The larger this value is, the more random the masking is picked. + + Returns: + A binary masking map [batch_size, seq_len]. Contains True where we should + mask (at mask_len locations), and False where we should keep. + """ + confidence = jnp.log(probs) + temperature * jax.random.gumbel( + rng, probs.shape) + return _get_bottom_k_mask(confidence, mask_len) + + +@flax.struct.dataclass +class DecodeState: + """Holds decoding state data.""" + + rng: jax.Array # Sampling random state. + # The position of the decoding loop in the length dimension. Scalar int32. + step: jax.Array + # What we input at each step. Starts from all masks and is uncovered by + # sampling. Note that this is an array with leading + # dimension `num_steps + 1` because we start with all masked tokens and then + # need `num_steps` to uncover all, i.e., the final output is given by + # all_inputs_q[-1, ...]. + all_inputs_q: jax.Array # float32 [num_steps + 1, batch, seq_len, c] + # Has a 1 for every _uncovered_ point. + uncovered_per_step: jax.Array # bool_ [num_steps, batch, seq_len] + logits_per_step: jax.Array # [num_steps, batch, seq_len, num_logits] + uncond_logits_per_step: jax.Array # [num_steps, batch, seq_len, num_logits] + prob_per_step: jax.Array # Probability per step. + # If CFG: Rejection sampling success rate. + rejection_sampling_success_per_step: jax.Array + + @classmethod + def make( + cls, + initial_rng: jax.Array, + all_masked_input: jax.Array, + num_logits: int, + num_steps: int, + ) -> "DecodeState": + """Creates the initial state.""" + b, seq_len, c = all_masked_input.shape + all_inputs_q = jnp.broadcast_to( + all_masked_input, + (num_steps + 1, b, seq_len, c), + ) + return cls( + initial_rng, + step=jnp.array(0), + all_inputs_q=all_inputs_q, + uncovered_per_step=jnp.full((num_steps, b, seq_len), False, jnp.bool_), + logits_per_step=jnp.full( + (num_steps, b, seq_len, num_logits), jnp.nan, jnp.float32 + ), + uncond_logits_per_step=jnp.full( + (num_steps, b, seq_len, num_logits), jnp.nan, jnp.float32 + ), + prob_per_step=jnp.full((num_steps, b, seq_len), jnp.nan, jnp.float32), + rejection_sampling_success_per_step=jnp.full( + (num_steps,), jnp.nan, jnp.float32 + ), + ) + + @property + def current_inputs_q(self) -> jax.Array: + """Returns the current quantized input.""" + return self.all_inputs_q[self.step, ...] + + @property + def num_steps(self) -> int: + """Returns number of decode steps.""" + return self.uncovered_per_step.shape[0] + + def _steps_mask(self) -> jax.Array: + return jnp.arange(self.num_steps) <= self.step + + @property + def total_uncovered(self) -> jax.Array: + """Returns the total uncovered mask up to and including current step.""" + return self.uncovered_per_step.sum( + axis=0, where=self._steps_mask()[:, jnp.newaxis, jnp.newaxis] + ).astype(jnp.bool_) + + def split_rng(self) -> tuple["DecodeState", jax.Array]: + """Splits of RNG for the current step.""" + rng, step_rng = jax.random.split(self.rng, 2) + return self.replace(rng=rng), step_rng + + def set_next_input(self, next_input_q: jax.Array) -> "DecodeState": + """Sets the input for the next step.""" + return self._set_row("all_inputs_q", self.step + 1, next_input_q) + + def set_uncover_at_current_step(self, uncovered: jax.Array) -> "DecodeState": + """Sets what was uncovered after the current step.""" + return self._set_row("uncovered_per_step", self.step, uncovered) + + def set_logits_at_current_step(self, logits: jax.Array) -> "DecodeState": + return self._set_row("logits_per_step", self.step, logits) + + def set_uncond_logits_at_current_step( + self, logits: jax.Array + ) -> "DecodeState": + return self._set_row("uncond_logits_per_step", self.step, logits) + + def set_rejection_sampling_success_at_current_step( + self, success: jax.Array + ) -> "DecodeState": + return self._set_row( + "rejection_sampling_success_per_step", self.step, success + ) + + def set_prob_at_current_step(self, prob: jax.Array) -> "DecodeState": + return self._set_row("prob_per_step", self.step, prob) + + def increment_step(self) -> "DecodeState": + """Increments step.""" + return self.replace(step=self.step + 1) + + def _set_row(self, attr_name, row_index, row_value): + """Sets one row of the variables that have shape (num_steps, ...).""" + current_value = getattr(self, attr_name) + _, *expected_shape = current_value.shape + if row_value.shape != tuple(expected_shape): + raise ValueError(f"Expected {row_value.shape} == {expected_shape}!") + if row_value.dtype != current_value.dtype: + raise ValueError(f"Expected {row_value.dtype} == {current_value.dtype}") + new_value = current_value.at[row_index, ...].set(row_value) + return self.replace(**{attr_name: new_value}) + + +@dataclasses.dataclass(frozen=True) +class MaskedGenerationConfig: + """Config for masked generation. + + Attributes: + num_steps: Number of sampling steps. + should_anneal_temperature: If given, anneal choice temperature as we go + through the sampling steps. + choice_temperature: Temperature for picking points. + ordering: How to order to select. Supports: + maskgit: Maskgit style, use P[samples] + schedule: Inference mask schedule. + cfg_inference_weight: CFG Inference weight. + """ + num_steps: int = 16 + should_anneal_temperature: bool = True + choice_temperature: float = 1.0 + ordering: Literal["maskgit"] = "maskgit" + schedule: str = "cosine" + cfg_inference_weight: float = 0.0 + + +def _assert_single_component_get_loc_scale( + pdf: distrax.Distribution, rng=None, mixture=None +): + """Extracts loc and scale from a single mixture GMM.""" + if not isinstance(pdf, distrax.MixtureSameFamily): + raise ValueError(f"Expected mixture! Got {type(pdf)}") + components_d = pdf.components_distribution + if isinstance(components_d, distrax.MultivariateNormalDiag): + loc, scale_diag = components_d.loc, components_d.scale_diag + b, s, m, _ = loc.shape + if mixture is None: + assert rng is not None + # Shape (b, seq) + mixture = pdf.mixture_distribution.sample(seed=rng) + mixture = jax.nn.one_hot(mixture, num_classes=m, axis=-1) + assert mixture.shape == (b, s, m), (mixture.shape, loc.shape) + loc = (loc * mixture[..., None]).sum(-2) + scale_diag = (scale_diag * mixture[..., None]).sum(-2) + return loc, scale_diag, mixture + else: + loc, scale = components_d.loc, components_d.scale + if loc.shape[-1] != 1 or scale.shape[-1] != 1: + raise ValueError(f"Expected one mixture! {loc.shape}/{scale.shape}") + return loc[..., 0], scale[..., 0], None + + +class CFGDensity: + """Helper to get probability and samples via CFG.""" + + pdf_c: distrax.Distribution + pdf_u: distrax.Distribution + w: float + simple: distrax.Distribution + fac: jax.Array + + def __init__( + self, + pdf_c: distrax.Distribution, + pdf_u: distrax.Distribution, + w: float, + rng: jax.Array, + ) -> None: + loc_c, scale_c, mixture = _assert_single_component_get_loc_scale(pdf_c, rng) + # Note: RNG only needed when we have mixtures, to select components. + loc_u, scale_u, _ = _assert_single_component_get_loc_scale( + pdf_u, rng, mixture=mixture + ) + + # Definitly wider than whatever we had before. The mean should be slightly + # away though! + loc_simple = loc_c + scale_simple = jnp.stack([scale_c, scale_u], -1).max(-1) * 2 + self.simple = distrax.Normal(loc_simple, scale_simple) + + self.pdf_c = distrax.Normal(loc_c, scale_c) + self.pdf_u = distrax.Normal(loc_u, scale_u) + self.w = w + + assert loc_c.ndim == 3, loc_c.shape + points = loc_c[jnp.newaxis, ...] + jnp.linspace(-10, 10, 1001).reshape( + -1, 1, 1, 1 + ) + p_at_c, _ = self._unnormalized_p(points) + + self.fac = jnp.max(p_at_c / self.simple.prob(loc_c), axis=0) + jax.debug.print("🎲 CFG {fac}", fac=self.fac.mean()) + + def _unnormalized_p(self, x): + w = self.w + logp_cfg = (1 + w) * self.pdf_c.log_prob(x) - w * self.pdf_u.log_prob(x) + return jnp.exp(logp_cfg), logp_cfg + + def rejection_sample( + self, + seed: jax.Array, + max_samples: int = 1_000, + ) -> tuple[jax.Array, jax.Array]: + """Rejection sampling, try `max_samples`, take first match.""" + rng_sample, rng_uni = jax.random.split(seed, 2) + # Shape (max_samples, b, seq_len, c) + xs = self.simple.sample(seed=rng_sample, sample_shape=(max_samples,)) + facq = self.fac * self.simple.prob(xs) + ys = jax.random.uniform(rng_uni, shape=facq.shape, minval=0.0, maxval=facq) + # Shape (max_samples, b, seq_len, c), True where `xs` is a valid sample + # from p. We might have anywhere between 0 and `max_samples` valid samples! + p, _ = self._unnormalized_p(xs) + mask = ys < p + # Now we need to do fancy tricks to get the first element in `mask` that is + # True. We do this by making a shifted mask that is False for every element + # after the first True. + # > Example: + # mask [0, 1, 0, 1, 0, 0, 1, 0] + # > implies: + # cmask [0, 1, 1, 1, 1, 1, 1, 1] + # shifted_cmask [0, 0, 1, 1, 1, 1, 1, 1] + # keep [0, 1, 0, 0, 0, 0, 0, 0] # <- picks the first valid! + cmask = jnp.cumsum(mask, axis=0).astype(jnp.bool_) + shifted_cmask = jnp.pad( + cmask, [(1, 0), (0, 0), (0, 0), (0, 0)], constant_values=False + )[:-1] + assert shifted_cmask.shape == mask.shape + keep = jnp.logical_and(cmask, jnp.logical_not(shifted_cmask)) + # Now we can grab the first valid sample by doing a sum over the + # `max_samples` dimension. + sample = jnp.where(keep, xs, 0).sum(0) + # If the rejection sampler fails, we fall back to the conditional + # distribution. + ok = mask.sum(0) > 0 # Shape (b, seq_len, c) + # jax.debug.print("🎲 CFG ok {ok}%", ok=ok.mean() * 100) + sample = jnp.where( + ok, sample, self.pdf_c.sample(seed=rng_sample) + ) + return sample, ok.mean() * 100 + + def sample( + self, + seed: jax.Array, + max_samples: int = 1_000, + ) -> jax.Array: + result, ok = self.rejection_sample(seed, max_samples) + jax.debug.print("Debug ok={ok}%", ok=ok) + return result + + # Unnormalized! But we only use it for ordering. + def prob(self, xs: jax.Array) -> jax.Array: + p, _ = self._unnormalized_p(xs) + return p + + def log_prob(self, xs: jax.Array) -> jax.Array: + _, lp = self._unnormalized_p(xs) + return lp + + +def decode_masked( + rng: jax.Array, + labels: jax.Array, + seq_len: int, + feature_dim: int, + model: givt.Model, + variables: flax.core.FrozenDict, + config: MaskedGenerationConfig, +) -> DecodeState: + """Implements an masked bidirectional sampling loop. + + This function implements the loop from the docstring. + + Args: + rng: RNG, only required if sampling. + labels: Shape (b,), labels per batch. Determines batch size. + seq_len: How many tokens to sample per batch. + feature_dim: Output dimension of the VAE, i.e., number of channels, `c`. + model: GIVT model to sample from. + variables: Variables of the model. + config: Configures style. + + Returns: + Final state. + """ + logging.info("Masked Generation Config:\n%s", config) + + if model.style != "masked": + raise ValueError(f"Need masked model! Got `{model.style}`.") + + (b,) = labels.shape + all_masked_input = jnp.zeros((b, seq_len, feature_dim)) + init_state = DecodeState.make( + rng, + all_masked_input, + num_logits=model.num_logits, + num_steps=config.num_steps, + ) + + def loop_cond_fn(state: DecodeState): + return state.step < state.num_steps + + def tokens_to_logits(tokens, input_mask, drop_labels=None): + return model.apply( + variables, + tokens, + labels=labels, + # Note that the model applies the mask token internally given the input. + input_mask=input_mask, + drop_labels=drop_labels, + method="decode", + ) + + def loop_body_fn(state: DecodeState) -> DecodeState: + # 1 where we should mask, cumulative. + unknown = jnp.logical_not(state.total_uncovered) + + # Defines the mask ratio for the next round. The number to mask out is + # determined by mask_ratio * unknown_number_in_the_beginning. + ratio = (state.step + 1) / config.num_steps + # Note that the mask schedule inverts the function, so `mask_ratio` givts + # near 1 and goes to 0 monotonically. + mask_ratio = givt.apply_mask_schedule(ratio, method=config.schedule) + mask_len = jnp.floor(seq_len * mask_ratio).reshape(1, 1) + num_unknown = jnp.sum(unknown, axis=-1, keepdims=True) + mask_len = jnp.maximum( + 0, + # Keeps at least one of prediction in this round: Avoids the case where + # mask_len is equal to num_unknown, in which case the mask is not + # updated! We substract 1 to always remove at least one masked token. + jnp.minimum(num_unknown - 1, mask_len)) + + # Run model --- + logits = tokens_to_logits(state.current_inputs_q, unknown) + # Book keeping: store all logits. + state = state.set_logits_at_current_step(logits) + + pdf = model.get_pdf(logits) + state, sample_rng = state.split_rng() + if config.cfg_inference_weight > 0: + drop_all_labels = jnp.full((b,), True, jnp.bool_) + logits_uncond = tokens_to_logits( + state.current_inputs_q, unknown, drop_labels=drop_all_labels + ) + state = state.set_uncond_logits_at_current_step(logits_uncond) + pdf_uncond = model.get_pdf(logits_uncond) + state, cfg_rng = state.split_rng() + pdf = CFGDensity( + pdf_c=pdf, + pdf_u=pdf_uncond, + w=config.cfg_inference_weight, + rng=cfg_rng, + ) + sample, rejection_sampling_success = pdf.rejection_sample(sample_rng) + state = state.set_rejection_sampling_success_at_current_step( + rejection_sampling_success + ) + else: + sample = pdf.sample(seed=sample_rng) + + # Sample at the unknown spots. + sampled = jnp.where(unknown[:, :, None], sample, state.current_inputs_q) + assert sampled.shape == (b, seq_len, feature_dim), ( + sampled.shape, + b, + seq_len, + feature_dim, + ) + + prob = pdf.prob(sampled) + if model.multivariate: + assert prob.ndim == 2 # (b, seq_len) already + elif model.per_channel_mixtures or config.cfg_inference_weight > 0: + # Independence accross channels. + # This reduction is also required when using CFG and also + # `model.per_channel_mixtures == False` due to the 2-step CFG redefining + # the pdf, but the reduction is not needed without CFG. + prob = prob.prod(-1) + state = state.set_prob_at_current_step(prob) + + if config.ordering == "maskgit": + ordering = jnp.where(unknown, prob, _CONFIDENCE_OF_KNOWN_TOKENS) + else: + raise NotImplementedError(config.ordering) + + assert ordering.shape == (b, seq_len), (ordering.shape, b, seq_len) + + temp = config.choice_temperature + if config.should_anneal_temperature: + temp *= (1. - ratio) + + # True where we should mask input. Note that this is cumulative (ie this + # starts with all True and keeps getting more False entries as we go through + # the steps). + state, choice_rng = state.split_rng() + masking = mask_by_random_topk(choice_rng, mask_len, ordering, temp) + assert masking.shape == (b, seq_len) + masking = jnp.where(mask_len == 0, jnp.zeros_like(masking), masking) + + # Remove the masked tokens from the sampled array for safety (the model will + # again apply the mask anyway...). + sampled = jnp.where(masking[:, :, None], jnp.zeros_like(sampled), sampled) + + # Get next_uncover --- + # New tokens to uncover (non cumulative): where it was unknown + # but is now known. + next_uncover = jnp.logical_and(unknown, jnp.logical_not(masking)) + assert next_uncover.shape == (b, seq_len), (next_uncover.shape, b, seq_len) + state = state.set_uncover_at_current_step(next_uncover) + state = state.set_next_input(sampled) + return state.increment_step() + + return jax.lax.while_loop(loop_cond_fn, loop_body_fn, init_state) diff --git a/big_vision/models/proj/givt/parallel_decode_test.py b/big_vision/models/proj/givt/parallel_decode_test.py new file mode 100644 index 0000000000000000000000000000000000000000..c03effc5f944c99db7ec3e392abe255bb8583eb9 --- /dev/null +++ b/big_vision/models/proj/givt/parallel_decode_test.py @@ -0,0 +1,154 @@ +# Copyright 2024 Big Vision 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. + +from absl.testing import parameterized +from big_vision.models.proj.givt import givt +from big_vision.models.proj.givt import parallel_decode +import chex +import jax +import jax.numpy as jnp + +from absl.testing import absltest + + +_BATCH_SIZE = 2 +_OUT_DIM = 4 +_SEQ_LEN = 6 +_NUM_MIXTURES = 4 + + +def _make_test_model(**overwrites): + config = dict( + num_heads=2, + num_decoder_layers=1, + mlp_dim=64, + emb_dim=16, + seq_len=_SEQ_LEN, + out_dim=_OUT_DIM, + num_mixtures=_NUM_MIXTURES, + style="masked", + ) + config.update(overwrites) + return givt.Model(**config) + + +def _mask(*flags): + return jnp.asarray(flags).astype(jnp.bool_) + + +class HelperTest(googletest.TestCase): + + def test_get_first_n(self): + with self.subTest("ordered"): + values = jnp.asarray([4, 3, 2, 1, 0]) + k = jnp.asarray([3], jnp.int32) + chex.assert_trees_all_equal( + parallel_decode._get_bottom_k_mask(values, k), _mask(0, 0, 1, 1, 1) + ) + + with self.subTest("equal_values"): + values = jnp.ones((5,)) + k = jnp.asarray([3], jnp.int32) + chex.assert_trees_all_equal( + parallel_decode._get_bottom_k_mask(values, k), _mask(1, 1, 1, 0, 0) + ) + + with self.subTest("equal_values"): + values = jnp.asarray([1, 2, 2, 2, 3]) + k = jnp.asarray([3], jnp.int32) + chex.assert_trees_all_equal( + parallel_decode._get_bottom_k_mask(values, k), _mask(1, 1, 1, 0, 0) + ) + + +class ParallelDecodeTest(parameterized.TestCase): + + def _make_model(self, **overwrites): + model = _make_test_model(**overwrites) + sequence = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE, _SEQ_LEN, _OUT_DIM) + ) + labels = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE,), maxval=10 + ).astype(jnp.int32) + input_mask = jax.random.uniform( + jax.random.PRNGKey(0), (_BATCH_SIZE, _SEQ_LEN) + ).astype(jnp.bool_) + variables = model.init( + jax.random.PRNGKey(0), + sequence, + labels, + input_mask=input_mask, + train=False, + ) + return model, variables + + def _test_model(self, rng, model, variables, config): + labels = jnp.ones((_BATCH_SIZE,), dtype=jnp.int32) + state = parallel_decode.decode_masked( + rng, + seq_len=_SEQ_LEN, + feature_dim=_OUT_DIM, + labels=labels, + model=model, + variables=variables, + config=config, + ) + self.assertEqual(int(state.step), 4) + # Each point uncovered exactly once. + chex.assert_trees_all_equal( + state.uncovered_per_step.sum(0), + jnp.ones((_BATCH_SIZE, _SEQ_LEN), dtype=jnp.int32), + ) + + @parameterized.product( + rng_seed=[1, 2], + choice_temperature=[1.0, 4.0], + multivariate=[True, False], + ) + def test_decode_masked(self, rng_seed, choice_temperature, multivariate): + rng = jax.random.PRNGKey(rng_seed) + model, variables = self._make_model( + num_mixtures=1 if multivariate else _NUM_MIXTURES, + multivariate=multivariate, + ) + config = parallel_decode.MaskedGenerationConfig( + num_steps=4, + choice_temperature=choice_temperature, + ) + self._test_model(rng, model, variables, config) + + @parameterized.product( + rng_seed=[1, 2], + choice_temperature=[1.0, 4.0], + w=[0.0, 1.0, 3.0], + per_channel_mixtures=[True, False], + ) + def test_cfg(self, rng_seed, choice_temperature, w, per_channel_mixtures): + rng = jax.random.PRNGKey(rng_seed) + model, variables = self._make_model( + num_mixtures=1 if per_channel_mixtures else 3, + drop_labels_probability=0.1, + per_channel_mixtures=per_channel_mixtures, + ) + config = parallel_decode.MaskedGenerationConfig( + num_steps=4, + choice_temperature=choice_temperature, + cfg_inference_weight=w, + ) + self._test_model(rng, model, variables, config) + + +if __name__ == "__main__": + googletest.main() diff --git a/big_vision/models/proj/givt/vae.py b/big_vision/models/proj/givt/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..db1ab66721e0fc6131f2f98a2cbbddad9d66007c --- /dev/null +++ b/big_vision/models/proj/givt/vae.py @@ -0,0 +1,94 @@ +# Copyright 2024 Big Vision 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. + +"""Abstract VAE model class. + +Gaussian encoder and decoder (the latter assumed to have constant variance). + +Inspiration drawn from https://github.com/pytorch/examples/tree/main/vae. +""" + +import abc +from typing import Optional, Mapping + + +import flax.linen as nn +import jax +import jax.numpy as jnp + + +class Model(nn.Module, metaclass=abc.ABCMeta): + """Abstract VAE model class.""" + + codeword_dim: Optional[int] = None + code_len: int = 256 + code_dropout: str = "none" + + @abc.abstractmethod + def encode( + self, + x: jax.Array, + *, + train: bool = False, + ) -> tuple[jax.Array, jax.Array]: + ... + + def reparametrize( + self, + mu: jax.Array, + logvar: jax.Array, + rng: jax.Array | None = None, + ) -> jax.Array: + std = jnp.exp(0.5 * logvar) + if rng is None: + rng = self.make_rng("dropout") + eps = jax.random.normal(rng, shape=std.shape, dtype=std.dtype) + return mu + std * eps + + @abc.abstractmethod + def decode( + self, x: jax.Array, + train: bool = False, + ) -> jax.Array | Mapping[str, jax.Array]: + ... + + def code_dropout_fn(self, z: jax.Array, *, train: bool = False) -> jax.Array: + # "seq" drops out tokens later in the sequence with higher probablility than + # tokens earlier in the sequence. + assert self.code_dropout in ["none", "seq", "random"] + if train and self.code_dropout != "none": + importance = jnp.linspace(1.0, 0.0, self.code_len + 2)[1:-1] + thr = jax.random.uniform(self.make_rng("dropout"), z.shape[:1]) + mask = importance[None, :] > thr[:, None] + if self.code_dropout == "random": + mask = jax.random.permutation( + self.make_rng("dropout"), mask, axis=-1, independent=True) + z = z * mask[:, :, None] + return z + + def __call__( + self, + x: jax.Array, + *, + train: bool = False, + ) -> tuple[jax.Array | Mapping[str, jax.Array], Mapping[str, jax.Array]]: + mu, logvar = self.encode(x, train=train) + # Only reparametrize when training for simplicity. + if train: + z = self.reparametrize(mu, logvar) + else: + z = mu + z = self.code_dropout_fn(z, train=train) + x = self.decode(z, train=train) + return x, {"mu": mu, "logvar": logvar, "z": z} diff --git a/big_vision/models/proj/givt/vit.py b/big_vision/models/proj/givt/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..7ed36ef069db09868a8b3be97ce6cb607b7e4053 --- /dev/null +++ b/big_vision/models/proj/givt/vit.py @@ -0,0 +1,188 @@ +# Copyright 2024 Big Vision 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. + +"""Simple VAE fork of the UViM VQ-VAE (proj/uvim/vit.py) with small changes.""" + +from typing import Optional, Sequence, Mapping, Any + +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +from big_vision.models.proj.givt import vae + +import einops +import flax.linen as nn +import flax.training.checkpoints +import jax +import jax.numpy as jnp +import numpy as np + + +class Model(vae.Model): + """ViT model.""" + + input_size: Sequence[int] = (256, 256) + patch_size: Sequence[int] = (16, 16) + width: int = 768 + enc_depth: int = 6 + dec_depth: int = 6 + mlp_dim: Optional[int] = None + num_heads: int = 12 + posemb: str = "learn" # Can also be "sincos2d" + dropout: float = 0.0 + head_zeroinit: bool = True + bottleneck_resize: bool = False + inout_specs: Optional[Mapping[str, tuple[int, int]]] = None + scan: bool = False + remat_policy: str = "nothing_saveable" + + def setup(self) -> None: + self.grid_size = np.array(self.input_size) // np.array(self.patch_size) + + self.embedding = nn.Conv( + self.width, self.patch_size, strides=self.patch_size, + padding="VALID", name="embedding") + + self.pos_embedding_encoder = vit.get_posemb( + self, self.posemb, self.grid_size, self.width, "pos_embedding_encoder") + self.encoder = vit.Encoder( + depth=self.enc_depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout, + scan=self.scan, + remat_policy=self.remat_policy, + name="encoder") + + if not self.bottleneck_resize: + self.bottleneck_downsample = self.param( + "bottleneck_downsample", + nn.initializers.xavier_uniform(), + (np.prod(self.grid_size), self.code_len)) + + if not self.bottleneck_resize: + self.bottleneck_upsample = self.param( + "bottleneck_upsample", + nn.initializers.xavier_uniform(), + (self.code_len, np.prod(self.grid_size))) + + self.pos_embedding_decoder = vit.get_posemb( + self, self.posemb, self.grid_size, self.width, "pos_embedding_decoder") + self.decoder = vit.Encoder( + depth=self.dec_depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout, + scan=self.scan, + remat_policy=self.remat_policy, + name="decoder") + + # Setting num_outputs to 2 * codeword_dim to predict mean and variance per + # element + self.encoder_head = nn.Dense(self.codeword_dim * 2 or self.width * 2) + self.decoder_stem = nn.Dense(self.width) + + kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {} + + if self.inout_specs is not None: + num_out_channels = sum( + num_classes for _, num_classes in self.inout_specs.values()) + else: + num_out_channels = 3 + + self.head = nn.Dense( + num_out_channels * np.prod(self.patch_size), + name="decoder_head", **kw) + + def encode( + self, + x: jax.Array, + *, + train: bool = False, + ) -> tuple[jax.Array, jax.Array]: + if self.inout_specs is not None: + one_hot_inputs = [] + for in_ch, num_classes in self.inout_specs.values(): + one_hot_inputs.append(nn.one_hot(x[..., in_ch], num_classes)) + x = jnp.concatenate(one_hot_inputs, axis=-1) + x = self.embedding(x) + x = einops.rearrange(x, "b h w c -> b (h w) c") + + x, _ = self.encoder(x + self.pos_embedding_encoder, deterministic=not train) + + if self.bottleneck_resize: + x = einops.rearrange(x, "b (h w) c -> b h w c", + h=self.grid_size[0], w=self.grid_size[1]) + l = int(np.round(self.code_len ** 0.5)) + x = jax.image.resize( + x, (x.shape[0], l, l, x.shape[3]), + method="linear") + x = einops.rearrange(x, "b h w c -> b (h w) c") + else: + x = jnp.einsum("btc,tn->bnc", x, self.bottleneck_downsample) + + x = self.encoder_head(x) + + mu, logvar = jnp.split(x, 2, axis=-1) + return mu, logvar + + def decode( + self, + x: jax.Array, + train: bool = False, + ) -> jax.Array | Mapping[str, jax.Array]: + x = self.decoder_stem(x) + + if self.bottleneck_resize: + l = int(np.round(self.code_len ** 0.5)) + x = einops.rearrange(x, "b (h w) c -> b h w c", h=l, w=l) + x = jax.image.resize( + x, (x.shape[0], self.grid_size[0], self.grid_size[1], x.shape[3]), + method="linear") + x = einops.rearrange(x, "b h w c -> b (h w) c") + else: + x = jnp.einsum("bnc,nt->btc", x, self.bottleneck_upsample) + + x, _ = self.decoder(x + self.pos_embedding_decoder, deterministic=not train) + x = self.head(x) + # c = 3 for RGB images + x = einops.rearrange(x, "b (h w) (p q c) -> b (h p) (w q) c", + h=self.grid_size[0], w=self.grid_size[1], + p=self.patch_size[0], q=self.patch_size[1]) + + if self.inout_specs is None: + x = jnp.clip(x, -1.0, 1.0) + else: + x_dict = {} + channel_index = 0 + for name, (_, num_channels) in self.inout_specs.items(): + x_dict[name] = x[..., channel_index : channel_index + num_channels] + channel_index += num_channels + x = x_dict + + return x + + +def load( + init_params: Any, + init_file: str, + model_params: Any = None, + dont_load: Sequence[str] = (), +) -> Any: + """Loads params from init checkpoint and merges into init_params.""" + del model_params + params = flax.core.unfreeze(utils.load_params(init_file)) + if init_params is not None: + params = common.merge_params(params, init_params, dont_load) + return params diff --git a/big_vision/models/proj/image_text/text_transformer.py b/big_vision/models/proj/image_text/text_transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..696eab5b4f2bf6e37a50ea3b2b02688a860bb254 --- /dev/null +++ b/big_vision/models/proj/image_text/text_transformer.py @@ -0,0 +1,119 @@ +# Copyright 2024 Big Vision 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. + +"""Transformer encoders for text, similar to CLIP.""" + +from typing import Any + +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import flax.linen as nn +import flax.training.checkpoints +import numpy as np + +ConfigDict = Any + + +class _Model(nn.Module): + """Text transformer similar to CLIP.""" + + # Differences to CLIP text encoder (gpt-2) that I am aware of: + # 1. https://imgur.com/HNi3jix (gpt-1) + # 2. https://imgur.com/qKGZgBR (gpt-2) + # 3. https://imgur.com/a/xrpYHF0 (clip) + # - LayerNorm is on res-path (like pre-activation resnet) + # - dropout 0.1 everywhere + # - init as var=0.02, scaled by depth + # - BOS and EOS tokens, take repr from EOS. + # - self-attention is autoregressively masked. + # - scaled in width only, with the image model. + + num_classes: int + width: int = 512 + depth: int = 12 + mlp_dim: int = 2048 + num_heads: int = 8 + dropout: float = 0.0 + vocab_size: int = 32_000 + pool_type: str = "last" + scan: bool = False + remat_policy: str = "nothing_saveable" + + @nn.compact + def __call__(self, text, *, train=False): + out = {} + + # We can't use where/argwhere since the output shape is not fixed. + # Here we use the fact that sequences are padded with EOS tokens, that the + # EOS token has value 1, and that argmin returns the first index. + # eos_indices = jnp.argmin(text, axis=1) + + embedding = nn.Embed(num_embeddings=self.vocab_size, features=self.width) + x = out["embedded"] = embedding(text) + + # Add posemb + n, l, d = x.shape # pylint: disable=unused-variable + x = x + self.param("pos_embedding", + nn.initializers.normal(stddev=1/np.sqrt(d)), + (1, l, d), x.dtype) + + x, encoder_out = vit.Encoder( + depth=self.depth, mlp_dim=self.mlp_dim, num_heads=self.num_heads, + scan=self.scan, remat_policy=self.remat_policy, dropout=self.dropout)( + x, deterministic=not train) + + out.update({"transformed": x, **encoder_out}) + + # Share weights between embeddings and logit transformation. + out["vocab_logits"] = embedding.attend(x) + + if self.pool_type == "last": + # Assuming "sticky" EOS tokenization, last token is always EOS. + x = out["pre_logits"] = x[:, -1, :] + elif self.pool_type == "first": + x = out["pre_logits"] = x[:, 0, :] + elif self.pool_type in ("mean", "gap"): + x = out["pre_logits"] = x.mean(axis=1) + elif self.pool_type in ("max", "gmp"): + x = out["pre_logits"] = x.max(axis=1) + elif self.pool_type == "map": + x = out["pre_logits"] = vit.MAPHead( + num_heads=self.num_heads, mlp_dim=self.mlp_dim)(x) + else: + raise NotImplementedError(f"Cannot do pooling '{self.pool_type}'") + + if self.num_classes: + x = out["logits"] = nn.Dense(self.num_classes, name="head")(x) + return x, out + + +def Model(num_classes, *, variant=None, **kw): # pylint: disable=invalid-name + """Factory function, because linen really don't like what I'm doing!""" + return _Model(num_classes, **{**vit.decode_variant(variant), **kw}) + + +def load(init_params, init_file, model_cfg, dont_load=()): # pylint: disable=invalid-name + """Load init from checkpoint, both old model and this one. +Hi-res posemb.""" + del model_cfg # unused + params = utils.load_params(init_file) + params = flax.core.unfreeze( + flax.training.checkpoints.convert_pre_linen(params)) + + # Some older (but expensive to train) checkpoints had the posemb added twice + # by mistake. We detect this here and merge them. + extra_posemb = params["Encoder_0"].pop("pos_embedding", 0) + params["pos_embedding"] += extra_posemb + + return common.merge_params(params, init_params, dont_load) diff --git a/big_vision/models/proj/image_text/two_towers.py b/big_vision/models/proj/image_text/two_towers.py new file mode 100644 index 0000000000000000000000000000000000000000..a9a045eaeb72383795bf903b51d89a6150bc3a16 --- /dev/null +++ b/big_vision/models/proj/image_text/two_towers.py @@ -0,0 +1,154 @@ +# Copyright 2024 Big Vision 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. + +"""Transformer encoders both for text and for images.""" + +import importlib +from typing import Any, Optional, Tuple, Union +from absl import logging + +from big_vision import utils +import flax.linen as nn +import jax.numpy as jnp + +ConfigDict = Any + + +class Model(nn.Module): + """Two towers transformer.""" + image: Optional[ConfigDict] = None + text: Optional[ConfigDict] = None + text_model: str = "proj.image_text.text_transformer" + image_model: str = "vit" + out_dim: Union[int, Tuple[int, int]] = 128 + temperature_init: float = 1.0 + bias_init: Optional[float] = None + + @nn.compact + def __call__(self, image, text=None, **kw): + """Returns (B,C) image and (B,C) text representations.""" + + # Support calling without text or without image, for example for few-shot. + ztxt, zimg = None, None + out = {} + out_dims = self.out_dim + if isinstance(out_dims, int): + out_dims = (out_dims, out_dims) + + # Embed the text: + if text is not None: + text_model = importlib.import_module( + f"big_vision.models.{self.text_model}" + ).Model(**{"num_classes": out_dims[1], **(self.text or {})}, name="txt") + + ztxt, out_txt = text_model(text, **kw) + for k, v in out_txt.items(): + out[f"txt/{k}"] = v + + # Normalize the embeddings the models give us. + out["txt/norm"] = jnp.linalg.norm(ztxt, axis=1, keepdims=True) + out["txt/normalized"] = ztxt = ztxt / (out["txt/norm"] + 1e-8) + + if image is not None: + image_model = importlib.import_module( + f"big_vision.models.{self.image_model}" + ).Model(**{"num_classes": out_dims[0], **(self.image or {})}, name="img") # pylint: disable=not-a-mapping + + zimg, out_img = image_model(image, **kw) + for k, v in out_img.items(): + out[f"img/{k}"] = v + + # Normalize the embeddings the models give us. + out["img/norm"] = jnp.linalg.norm(zimg, axis=1, keepdims=True) + out["img/normalized"] = zimg = zimg / (out["img/norm"] + 1e-8) + + temp_init = jnp.log(self.temperature_init) + t = self.param("t", + lambda key, shape, dtype: temp_init * jnp.ones(shape, dtype), + (1,), jnp.float32) + out["t"] = jnp.exp(t) + + out["t/parameter"] = t + if (b_init := self.bias_init) is not None: + out["b"] = self.param("b", lambda k, s, d: b_init * jnp.ones(s, d), + (1,), jnp.float32) + + # We could actually play with pre-multiplying by temperature here, such + # that out["t"] is nothing special to the trainer anymore. + + return zimg, ztxt, out + + +def load(init_params, init_files, model_cfg, img_load_kw={}, txt_load_kw={}): # pylint: disable=dangerous-default-value + """Loads both towers, `init_files` is now a dict with `img` and `txt` keys.""" + if isinstance(init_files, str): + init_files = VANITY_NAMES.get(init_files, init_files) + + if isinstance(init_files, str): + # A shortcut for a single file checkpoint of a two_towers model. + if "bias_init" in model_cfg.keys(): + logging.info("loading img, txt, t, and b from a single checkpoint.") + init_files = {k: f"{init_files}:{k}" for k in ("img", "txt", "t", "b")} + else: + logging.info("loading img, txt, and t from a single checkpoint.") + init_files = {k: f"{init_files}:{k}" for k in ("img", "txt", "t")} + else: + init_files = {**init_files} # Shallow copy because we'll pop stuff off. + + if not init_params: # Convenience to skip checks in colab. + init_params = {"img": None, "txt": None} + restored_params = {**init_params} + + img_init = init_files.pop("image", init_files.pop("img", None)) + if img_init: + restored_params["img"] = importlib.import_module( + f"big_vision.models.{model_cfg.get('image_model', 'vit')}" + ).load(init_params["img"], img_init, model_cfg.image, **img_load_kw) + + txt_init = init_files.pop("text", init_files.pop("txt", None)) + if txt_init: + restored_params["txt"] = importlib.import_module( + f"big_vision.models.{model_cfg.get('text_model', 'proj.image_text.text_transformer')}" # pylint: disable=line-too-long + ).load(init_params["txt"], txt_init, model_cfg.text, **txt_load_kw) + + t_init = init_files.pop("temperature", init_files.pop("t", None)) + if t_init: + restored_params["t"] = utils.load_params(t_init) + + b_init = init_files.pop("bias", init_files.pop("b", None)) + if b_init: + restored_params["b"] = utils.load_params(b_init) + + assert not init_files, ( + f"There's something unused left in `config.model_init`. You probably got " + f"a typo. Here it is: {init_files}") + + return restored_params + + +# Shortcut names for some canonical paper checkpoints: +VANITY_NAMES = { + # pylint: disable=line-too-long + # SigLIP image encoder checkpoints from https://arxiv.org/abs/2303.15343 + "SigLIP B/16 224": "gs://big_vision/siglip/webli_en_b16_224_63724782.npz", + "SigLIP B/16 256": "gs://big_vision/siglip/webli_en_b16_256_60500360.npz", + "SigLIP B/16 384": "gs://big_vision/siglip/webli_en_b16_384_68578854.npz", + "SigLIP B/16 512": "gs://big_vision/siglip/webli_en_b16_512_68580893.npz", + "SigLIP L/16 256": "gs://big_vision/siglip/webli_en_l16_256_60552751.npz", + "SigLIP L/16 384": "gs://big_vision/siglip/webli_en_l16_384_63634585.npz", + "SigLIP So400m/14 224": "gs://big_vision/siglip/webli_en_so400m_224_57633886.npz", + "SigLIP So400m/14 384": "gs://big_vision/siglip/webli_en_so400m_384_58765454.npz", + "SigLIP B/16-i18n 256": "gs://big_vision/siglip/webli_i18n_b16_256_66117334.npz", + # pylint: enable=line-too-long +} diff --git a/big_vision/models/proj/paligemma/__pycache__/gemma_bv.cpython-310.pyc b/big_vision/models/proj/paligemma/__pycache__/gemma_bv.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dece1ba5ca107ce8e1382cd3a4acf8f3906a3fdd Binary files /dev/null and b/big_vision/models/proj/paligemma/__pycache__/gemma_bv.cpython-310.pyc differ diff --git a/big_vision/models/proj/paligemma/__pycache__/paligemma.cpython-310.pyc b/big_vision/models/proj/paligemma/__pycache__/paligemma.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee61aa3134e2da9be775dc80e22fd2644b0f9647 Binary files /dev/null and b/big_vision/models/proj/paligemma/__pycache__/paligemma.cpython-310.pyc differ diff --git a/big_vision/models/proj/paligemma/gemma_bv.py b/big_vision/models/proj/paligemma/gemma_bv.py new file mode 100644 index 0000000000000000000000000000000000000000..bdc01e20c50454991e6ffb2c5399e96e24e3f346 --- /dev/null +++ b/big_vision/models/proj/paligemma/gemma_bv.py @@ -0,0 +1,182 @@ +# Copyright 2024 Big Vision 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. + +"""Gemma wrapper to make it work for us.""" + +from big_vision.models.ppp import gemma +import flax.linen as nn +import jax +import jax.numpy as jnp + + +def _get_config(model): + config = gemma.get_config(model.variant) + config.scan = model.scan + config.remat_policy = model.remat_policy + if model.vocab_size is not None: + config.vocab_size = model.vocab_size + config.dropout = model.dropout + config.dropout_bdims = model.dropout_bdims + config.cache_dtype = model.cache_dtype + return config + + +@jax.vmap +def _left_to_right_align(x, input_mask, attn_mask): + """Converts input from left-align to right-aligned.""" + # Due to vmap, this is operating in a single example (not batch level). + assert x.ndim == 2 and input_mask.ndim == 1 and attn_mask.ndim == 2 + assert x.shape[0] == input_mask.shape[0] + assert attn_mask.shape[0] == attn_mask.shape[1], attn_mask.shape + seqlen = jnp.sum(input_mask) + x = jnp.roll(x, -seqlen, axis=0) + input_mask = jnp.roll(input_mask, -seqlen, axis=0) + attn_mask = jnp.roll(attn_mask, -seqlen, axis=(0, 1)) + return x, input_mask, attn_mask + + +class Model(nn.Module): + """Wrapping gemma big_vision model.""" + variant: str = "gemma_2b" + scan: bool = True + remat_policy: str = "nothing_saveable" + vocab_size: int | None = None + + dropout: float = 0.0 + dropout_bdims: tuple[int, ...] = () # Every float is dropped independently. + cache_dtype: str | None = "bfloat16" # bfloat16 to save memory and transfers. + + def setup(self): + # The parent+name avoids an unnecessary nesting in params pytree. + self.model = gemma.Model(**_get_config(self), parent=self.scope, name="") + + def embed_tokens(self, tokens, train=False): + # Turns int32[B,T] tokens into float32[B,T,d_model] embeddings. + # Really just the vocab embedding. + return self.model(tokens, embed_only=True, deterministic=not train) + + def compute_logits(self, pre_logits, train=False): + return self.model(None, pre_logits=pre_logits, deterministic=not train)[0] + + def __call__(self, embs, mask=None, train=False): + # Turns float32[B,T,d_model] embedding sequence to logits. + # call(emb_tokens(tokens)) should be a forward pass. + # Allow for specifying int32[B,T,T] attention masks. For convenience + # default to triangular autorgressive mask when None, but not P0. + # Return float32[B,T,vocab_size] logits and out-dict. + + batch_size, _, d_model = embs.shape + assert d_model == self.embdim + logits, out = self.model( + tokens=jnp.zeros([batch_size, 0], dtype=jnp.int32), + embedded_prefix=embs, + mask=mask, + deterministic=not train, + ) + return logits, out + + def prefill_cache(self, x, input_mask, attn_mask, *, cache_size): + """Initializes decoding cache with `x` [B, N, E] as prompt. + + IMPORTANT: Inputs MUST be left-aligned and attn_mask should not allow + input tokens to attend to padding tokens. + + TODO: Relax left-align requirement by converting any input into + a right aligned input with no attention to padding tokens. + + Args: + x: float[B, N, E] with prompt tokens. + input_mask: bool[B, N]. True indicates tokens are part of the prompt. + False indicates padding tokens. This class doesn't combine this with + attn_mask, so mask out the attention to padding tokens beforehand. + attn_mask: bool[B, N, N]. Indicates which tokens can attend to which while + processing the prompt tokens. During extend_cache tokens, it is assumed + that tokens can attend all previous valid tokens. + cache_size: int. Indicates the size of the cache. The prompt will consume + the first N entries of the cache. Each subsequent extend_cache will + consume one entry. Behaviour is undefined when prefill_len plus number + of extend_cache exceeds the cache_size. + + Returns: + logits of the last valid token (i.e. last logits where input_mask=True). + """ + # To call the model with decode=True we need to be able to provide: + # (a) positions of tokens [B, N], ([B, 1] for extend) + # (b) attention mask [B, N, cache_size] ([B, 1, cache_size] for extend) + # + # To do so we track how many tokens each example has seen so far, and we + # align the prompt to the right so that cache usage for each example is in + # a continuous subsequent of (cache_begin, cache_end] such that cache_end + # is the same for all sequences (this allows to do faster row updates of + # the cache during decoding). + x, input_mask, attn_mask = _left_to_right_align(x, input_mask, attn_mask) + + # Track sequence len + seq_len = jnp.sum(input_mask, axis=-1) + self.put_variable("cache", "seq_len", seq_len) + positions = jnp.cumsum(input_mask, axis=-1) - 1 + + # Initialize cache_begin and cache_end. Note: cache_end is the same for all + # sequences but we keep it per example to allow easy sharding rules with + # batch as the first axis. + batch_size, prefill_len, _ = x.shape + self.put_variable("cache", "cache_begin", prefill_len - seq_len) + self.put_variable( + "cache", "cache_end", jnp.full((batch_size,), prefill_len, jnp.int32) + ) + + # Pad attention to set the cache size. + mask = jnp.pad(attn_mask, ((0, 0), (0, 0), (0, cache_size - prefill_len))) + + _, aux = self.model( + tokens=None, + embedded_prefix=x, + positions=positions, + mask=mask, + decode=True, + ) + return self.compute_logits(aux["pre_logits"][:, -1:]) + + def extend_cache(self, x): + """Extends decoding cache with `x` [B, 1, E] and returns logits.""" + assert x.shape[1] == 1, "Only supports extend the cache by one token." + if self.model.scan: + cache_size = self.variables["cache"]["layers"]["attn"]["k_cache"].shape[2] + else: + raise NotImplementedError("Not implemented yet.") + + # Lookup current token position and increment by one for next call. + positions = self.get_variable("cache", "seq_len") + self.put_variable("cache", "seq_len", positions + 1) + + # Update which cache positions are in use and construct attention mask. + # Tokens can attend to all cache positions which are in use including self. + cache_begin = self.get_variable("cache", "cache_begin") + cache_end = self.get_variable("cache", "cache_end") + 1 + self.put_variable("cache", "cache_end", cache_end) + mask = jnp.logical_and( + jnp.arange(cache_size)[None, None, :] >= cache_begin[:, None, None], + jnp.arange(cache_size)[None, None, :] < cache_end[:, None, None]) + + logits, _ = self.model( + tokens=None, embedded_prefix=x, + positions=positions[:, None], mask=mask, decode=True) + return logits + + @property + def embdim(self): + return _get_config(self).width + + +load = gemma.load diff --git a/big_vision/models/proj/paligemma/paligemma.py b/big_vision/models/proj/paligemma/paligemma.py new file mode 100644 index 0000000000000000000000000000000000000000..95aa62b6a82ba3015b25f1525e78b10f1957b368 --- /dev/null +++ b/big_vision/models/proj/paligemma/paligemma.py @@ -0,0 +1,289 @@ +# Copyright 2024 Big Vision 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. + +"""Image encoder + AR-decoder LLM.""" + +import importlib +from typing import Any, Optional + +import flax.linen as nn +import jax +import jax.numpy as jnp + +ConfigDict = Any + + +def make_attn_mask(input_mask, mask_ar): + """Returns attention mask bool[B, N, N] to use in transformer. + + Tokens can attend to valid inputs tokens which have a cumulative mask_ar + smaller or equal to theirs. This way `mask_ar` int[B, N] can be used to + setup several types of attention, for example: + + [[1 1 1 1 1 1]]: pure causal attention. + + [[0 0 0 1 1 1]]: prefix-lm attention. The first 3 tokens can attend between + themselves and the last 3 tokens have a causal attention. The first + entry could also be a 1 without changing behaviour. + + [[1 0 1 0 1 0 0 1 0 0]]: causal attention between 4 blocks. Tokens of a + block can attend all previous blocks and all tokens on the same block. + + Args: + input_mask: bool[B, N] true if its part of the input, false if padding. + mask_ar: int32[B, N] mask that's 1 where previous tokens cannot depend on + it and 0 where it shares the same attention mask as the previous token. + """ + cumsum = jnp.cumsum(mask_ar, axis=1) + attn_mask = (cumsum[:, None, :] <= cumsum[:, :, None]) + valid_mask = (input_mask[:, None, :] * input_mask[:, :, None]) + return jnp.logical_and(attn_mask, valid_mask) + + +class Model(nn.Module): + """Two towers transformer.""" + img_model: str = "vit" + img: Optional[ConfigDict] = None + llm_model: str = "proj.paligemma.gemma_bv" + llm: Optional[ConfigDict] = None + + def setup(self): + self._llm = importlib.import_module( + f"big_vision.models.{self.llm_model}" + ).Model(**(self.llm or {}), name="llm") + + img_config = {"num_classes": self._llm.embdim, **(self.img or {})} + self._img_model = importlib.import_module( + f"big_vision.models.{self.img_model}" + ).Model(**img_config, name="img") + + def embed_image(self, image, train=False): + out = {} + + # if we have video, fold frame dimension into the batch dimension + image_shape = image.shape + if len(image_shape) == 5: # video frames + image = jnp.reshape(image, (-1, *image.shape[-3:])) + + # Do we want to normalize? are they huge? + zimg, out_img = self._img_model(image, train=train) + + if len(image_shape) == 5: # concatenate tokens from all video frames + zimg = jnp.reshape(zimg, (image_shape[0], -1, zimg.shape[-1])) + + out["img/zimg"] = zimg + for k, v in out_img.items(): + out[f"img/{k}"] = v + return zimg, out + + def embed_text(self, tokens, train=False): + out = {} + ztxt = out["llm/ztxt"] = self._llm.embed_tokens(tokens, train=train) + return ztxt, out + + def embed_image_and_text(self, image, text, *, + input_mask=None, mask_ar=None, train=False): + """Concats image/text into a sequence of embeded tokens to pass to `llm`. + + Args: + image: float[B, H, W, 3] image to be embedded by the `img` model and used + as prefix to the sequence passed to the `llm` model. + text: int32[B, T] token sequence to embedded by the `llm`. + input_mask: bool[B, T] true if the text token is a valid token and false + if its a token to pad the sequence. Defaults to all being input tokens. + mask_ar: int32[B, T] mask that's 1 where `text` should be attended to + causally, and 0 where it can be attended to with full self-attention. + Defaults to all text tokens being auto-regressive. + train: bool whether we're in train or test mode (dropout etc). + + Returns: + Tuple (x: float[B, N, E], input_mask: bool[B, N], mask_ar: int[B, N]) and + auxiliary outputs. + """ + zimg, out_img = self.embed_image(image, train=train) + ztxt, out_txt = self.embed_text(text, train=train) + + if input_mask is None: + input_mask = jnp.full(text.shape, True) + if mask_ar is None: + mask_ar = jnp.full(text.shape, 1) + + # Concatenate embeded image and text into a single token sequence. + x = jnp.concatenate([zimg, ztxt], axis=1) + _, img_len, _ = zimg.shape + pad_width = ((0, 0), (img_len, 0)) + mask_ar = jnp.pad(mask_ar, pad_width, constant_values=0) + input_mask = jnp.pad(input_mask, pad_width, constant_values=True) + + return (x, input_mask, mask_ar), {**out_img, **out_txt} + + def __call__(self, image, text, mask_ar, train=False): + """Concats image/text and returns text logits. + + Args: + image: float32[B, H, W, 3] image that can be passed to the `img` model. + text: int32[B, T] token sequence that can be embedded by the `txt` model. + mask_ar: int32[B, T] mask that's 1 where `text` should be attended to + causally, and 0 where it can be attended to with full self-attention. + train: bool whether we're in train or test mode (dropout etc). + + Returns: + float32[B, T, V] logits for the `text` input, and an out-dict of named + intermediates. + """ + # Embed the image and text. + (x, input_mask, mask_ar), out = self.embed_image_and_text( + image, text, mask_ar=mask_ar, train=train) + + # Call transformer on the embedded token sequence. + attn_mask = out["attn_mask"] = make_attn_mask(input_mask, mask_ar) + _, out_llm = self._llm(x, mask=attn_mask, train=train) + for k, v in out_llm.items(): + out[f"llm/{k}"] = v + + # Extract the logits for the text tokens. + zimg = out["img/zimg"] + text_pre_logits = out["llm/pre_logits"][:, zimg.shape[1]:, :] + text_logits = self._llm.compute_logits(text_pre_logits, train=train) + out["text_logits"] = text_logits + out["text_tokens"] = jnp.argmax(text_logits, axis=-1) + return text_logits, out + + def prefill_cache(self, x, input_mask, mask_ar, *, cache_size): + """Initializes decoding cache with `x` [B, N, E] as prompt.""" + if hasattr(self._llm, "prefill_cache"): + attn_mask = make_attn_mask(input_mask, mask_ar) + return self._llm.prefill_cache( + x, input_mask, attn_mask, cache_size=cache_size) + else: + return self._fallback_prefill_cache(x, input_mask, mask_ar, cache_size) + + def extend_cache(self, x): + """Advances decoding cache with `x` [B, 1, E].""" + if hasattr(self._llm, "prefill_cache"): + return self._llm.extend_cache(x) + else: + return self._fallback_extend_cache(x) + + def _fallback_prefill_cache(self, x, input_mask, mask_ar, cache_size): + # FALLBACK: only cache inputs and call the model with the full sequence + # for each and every decode step. Very slowwww... + # + # This very slow codepath does not requires the model to implement caching. + # It is intended to allow to plug any model under development quite early + # into some decoding tasks and not as a long term decoding solution. + attn_mask = make_attn_mask(input_mask, mask_ar) + logits, _ = self._llm(x, mask=attn_mask) + + # Save the prefill inputs for subsequent extend_calls in the cache. + # Unused entries are zero-initialized. + pad_size = cache_size - x.shape[1] + x = jnp.pad(jnp.where(input_mask[..., None], x, 0), + [(0, 0), (0, pad_size), (0, 0)]) + mask_ar = jnp.pad(jnp.where(input_mask, mask_ar, 0), + [(0, 0), (0, pad_size)]) + input_mask = jnp.pad(input_mask, [(0, 0), (0, pad_size)]) + self.put_variable("cache", "x_cache", x) + self.put_variable("cache", "input_mask_cache", input_mask) + self.put_variable("cache", "mask_ar_cache", mask_ar) + + # Extract logits of the last token (using einsum). + last_pos = jnp.sum(input_mask, axis=1)[:, None] - 1 + last_onehot = jax.nn.one_hot(last_pos, logits.shape[1], dtype=jnp.int32) + last_logits = jnp.einsum("bnh,ben->beh", logits, last_onehot) + + return last_logits + + def _fallback_extend_cache(self, x): + # FALLBACK: append inputs to cache and call the model with the full sequence + # for each and every decode step. Very slowwww... + assert x.shape[1] == 1 + mask_ar = jnp.full(x.shape[:-1], 1) + input_mask = jnp.full(x.shape[:-1], True) + + # Append inputs to cache by add/or on the next available cache position, + # which is zero-initialized. + c_x = self.get_variable("cache", "x_cache") + c_input_mask = self.get_variable("cache", "input_mask_cache") + c_mask_ar = self.get_variable("cache", "mask_ar_cache") + next_pos = jnp.sum(c_input_mask, axis=1)[:, None] + move_onehot = jax.nn.one_hot(next_pos, c_x.shape[1], dtype=jnp.int32) + x = jnp.add(c_x, jnp.einsum("beh,ben->bnh", x, move_onehot)) + mask_ar = jnp.add(c_mask_ar, jnp.einsum("be,ben->bn", mask_ar, move_onehot)) + input_mask = jnp.logical_or( + c_input_mask, jnp.einsum("be,ben->bn", input_mask, move_onehot)) + self.put_variable("cache", "x_cache", x) + self.put_variable("cache", "input_mask_cache", input_mask) + self.put_variable("cache", "mask_ar_cache", mask_ar) + + # Call model on the full cached sequence. + attn_mask = make_attn_mask(input_mask, mask_ar) + logits, _ = self._llm(x, mask=attn_mask) + + # Extract logits of the last token. + last_pos = jnp.sum(input_mask, axis=1)[:, None] - 1 + last_onehot = jax.nn.one_hot(last_pos, logits.shape[1], dtype=jnp.int32) + last_logits = jnp.einsum("bnh,ben->beh", logits, last_onehot) + + return last_logits + + +# pylint: disable=line-too-long +import os +GEMMA_DIR = os.environ.get("BV_GEMMA_DIR", "PLEASE_SET_BV_GEMMA_DIR") +VANITY_NAMES = { + # Because checkpoints are behind an ACK-wall, the user has to download them + # to some folder (or bucket), take that from an environment variable. + "pt_224": os.path.join(GEMMA_DIR, "pt_224.npz"), + "pt_224.bf16": os.path.join(GEMMA_DIR, "pt_224.bf16.npz"), + "pt_224.f16": os.path.join(GEMMA_DIR, "pt_224.f16.npz"), + "pt_448": os.path.join(GEMMA_DIR, "pt_448.npz"), + "pt_448.bf16": os.path.join(GEMMA_DIR, "pt_448.bf16.npz"), + "pt_448.f16": os.path.join(GEMMA_DIR, "pt_448.f16.npz"), + "pt_896": os.path.join(GEMMA_DIR, "pt_896.npz"), + "pt_896.bf16": os.path.join(GEMMA_DIR, "pt_896.bf16.npz"), + "pt_896.f16": os.path.join(GEMMA_DIR, "pt_896.f16.npz"), +} +# pylint: enable=line-too-long + + +def load(init_params, init_files, model_cfg, img_load_kw={}, llm_load_kw={}): # pylint: disable=dangerous-default-value + """Loads both pieces, `init_files` is now a dict with `img` and `llm` keys.""" + + # A slight shortcut when loading an already combined model: + if isinstance(init_files, str): + init_files = VANITY_NAMES.get(init_files, init_files) + init_files = {"img": f"{init_files}:img", "llm": f"{init_files}:llm"} + + if not init_params: # Convenience to skip checks in colab. + init_params = {"img": None, "llm": None} + restored_params = {**init_params} + + init_files = {**init_files} # Needed because ConfigDict but we'll pop stuff. + + if img_init := init_files.pop("img", None): + restored_params["img"] = importlib.import_module( + f"big_vision.models.{model_cfg.get('img_model', 'vit')}" + ).load(init_params["img"], img_init, model_cfg.img, **img_load_kw) + + if llm_init := init_files.pop("llm", None): + restored_params["llm"] = importlib.import_module( + f"big_vision.models.{model_cfg.get('llm_model', 'proj.paligemma.gemma_bv')}" + ).load(init_params["llm"], llm_init, model_cfg.llm, **llm_load_kw) + + assert not init_files, ( + f"There's something unused left in `config.model_init`. You probably got " + f"a typo. Here it is: {init_files}") + + return restored_params diff --git a/big_vision/models/proj/uvim/decode.py b/big_vision/models/proj/uvim/decode.py new file mode 100644 index 0000000000000000000000000000000000000000..809d4f342b2ae35a2b83a5edcba5f52fcd165248 --- /dev/null +++ b/big_vision/models/proj/uvim/decode.py @@ -0,0 +1,384 @@ +# Copyright 2022 Big Vision 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. + +"""Inference.""" +import functools + +from typing import Any, Callable, Optional, Tuple + +import flax +from flax import linen as nn +import jax +from jax import lax +from jax import numpy as jnp + +import numpy as np + + +EOS_ID = 1 +NEG_INF = np.array(-1.0e7) # Effective negative infinity. + + +GenerateFn = Callable[..., + Tuple[jnp.ndarray, jnp.ndarray, Optional[jnp.ndarray]]] + + +def temperature_sampling(*args, temperature=1.0, top_k=0, top_p=0.0, **kwargs): + """Convenience wrapper for temperature sampling.""" + return generate(*args, generate_fn=_temperature_sampling, + temperature=temperature, + top_k=top_k, + top_p=top_p, + **kwargs) + + +def topk_sampling(*args, temperature=1.0, top_k=20, **kwargs): + """Convenience wrapper for top-k sampling.""" + return generate(*args, generate_fn=_temperature_sampling, + temperature=temperature, + top_k=top_k, + top_p=0.0, + **kwargs) + + +def nucleus_sampling(*args, temperature=1.0, top_p=0.2, **kwargs): + """Convenience wrapper for nucleus sampling.""" + return generate(*args, generate_fn=_temperature_sampling, + temperature=temperature, + top_k=0, + top_p=top_p, + **kwargs) + + +def argmax_sampling(*args, **kwargs): + """Convenience wrapper for argmax sampling.""" + return generate(*args, generate_fn=_temperature_sampling, + temperature=1e-7, + top_k=0, + top_p=0.0, + **kwargs) + + +def generate(params, inputs, prompts, seed, *, + model: nn.Module, + generate_fn: GenerateFn, + num_samples: int = 1, + prefill: bool = False, + eos_token: int = EOS_ID, + **generate_fn_kwargs): + """Generate sequence with fast decoding beam search on a batch. + + Model must support: + encode(inputs) -> encoded, or encode(*inputs) -> encoded. + decode(encoded, prompts, decode=True/False, max_decode_length) -> logits + + Args: + params: model parameters. + inputs: either a single `jnp.ndarray` of e.g. images, or + a tuple of inputs which are passed via `model.encode(*inputs)`. + prompts: [batch_size, max_decode_len] forced tokens for generation. + prompts need to finish with 0 token, they should not contain the end + markers. If no prompting is required, pass an all zeros tensor. + seed: PRNG key for random sampling. + model: object with methods encode and decode. + generate_fn: search or sampling function to generate sequences. + num_samples: number of samples to generate per item. + prefill: whether to prefill cache. + eos_token: if of end-of-sentence token for target vocabulary. + **generate_fn_kwargs: generate fn specific kwargs. + + Returns: + Top-scoring sequences (worst scores first). + [batch_size, num_samples, max_decode_len] + Scores of the generated sequences (worst scores first). The + returned scores are modified log probabilities. May be absent. + [batch_size, max_decode_len] + Log probs for the generated tokens. May be absent. + [batch_size, num_samples, max_decode_len] + """ + _, max_decode_len = prompts.shape + decode_kwargs = {"max_decode_length": max_decode_len} + + def encode(model, inputs): + if not isinstance(inputs, tuple): + inputs = (inputs,) + return model.encode(*inputs) + + encoded_inputs = nn.apply(encode, model)(params, inputs) + if isinstance(encoded_inputs, tuple): + encoded_inputs, enc_pos_emb = encoded_inputs + decode_kwargs["enc_pos_emb"] = enc_pos_emb + + def init_cache(model): + encoded = jnp.zeros_like(encoded_inputs) + targets = jnp.zeros_like(prompts) + return model.decode(encoded, targets, decode=True, **decode_kwargs) + + cache = nn.apply(init_cache, model, mutable=True)(params)[1]["cache"] + + def prefill_cache(model, encoded, targets): + return model.decode(encoded, targets, prefill=True, **decode_kwargs) + + if prefill: + cache = nn.apply(prefill_cache, model, mutable=True)( + {"params": params["params"], "cache": cache}, + encoded_inputs, prompts)[1]["cache"] + + def tokens_to_logits(tokens, cache): + def decode_step(model, tokens): + encoded = expand_samples_dim_and_flatten( + encoded_inputs, num_samples) + return model.decode(encoded, tokens, decode=True, **decode_kwargs) + + logits, aux = nn.apply(decode_step, model, mutable=True)( + {"params": params["params"], "cache": cache}, tokens) + return logits.squeeze(axis=1), aux["cache"] + + beam_seqs, scores, logprobs = generate_fn( + prompts, + cache, + tokens_to_logits, + num_samples=num_samples, + eos_token=eos_token, + max_decode_len=max_decode_len, + seed=seed, + **generate_fn_kwargs) + return beam_seqs, scores, logprobs + + +def expand_samples_dim(x, num_samples): + """Creates new dimension in non-scalar array and tiles into it.""" + if x.ndim == 0: # ignore scalars (e.g. cache index) + return x + x = jnp.expand_dims(x, axis=1) + tile_dims = [1] * x.ndim + tile_dims[1] = num_samples + return jnp.tile(x, tile_dims) + + +def flatten_samples_dim(x): + """Flattens samples dim into batch dim.""" + if x.ndim == 0: # ignore scalars (e.g. cache index) + return x + return x.reshape((x.shape[0] * x.shape[1],) + x.shape[2:]) + + +def unflatten_samples_dim(x, batch_size, num_samples): + """Unflattens first dim into batch and samples dims.""" + if x.ndim == 0: # ignore scalars (e.g. cache index) + return x + assert batch_size * num_samples == x.shape[0] + return x.reshape((batch_size, num_samples) + x.shape[1:]) + + +def expand_samples_dim_and_flatten(x, num_samples): + """Expands the each batch item by num_samples in batch dimension.""" + return flatten_samples_dim(expand_samples_dim(x, num_samples)) + + +def cache_map(fn, cache): + """Maps function over caches, even multiple caches in various layers.""" + frozen = isinstance(cache, flax.core.FrozenDict) + if frozen: + cache = flax.core.unfreeze(cache) + flat_cache = flax.traverse_util.flatten_dict(cache) + # Exclude cached relative position bias from beam expansion, etc. + keyvals = {k: v for k, v in flat_cache.items() if k[-1] != "cached_bias"} + keyvals = jax.tree_map(fn, keyvals) + flat_cache.update(keyvals) + new_cache = flax.traverse_util.unflatten_dict(flat_cache) + if frozen: + new_cache = flax.core.freeze(new_cache) + return new_cache + + +@flax.struct.dataclass +class LoopState: + """Internal state of the temperature sampling loop.""" + # Position in the sequence that we are currently looking at. + cur_index: int + # Cache for fast auto-regressive decoding. + cache: Any + # Flags indicating whether the sequence reached eos [B*N]. + flags_finished: jnp.ndarray + # Sequences being generated [B*N, L+1]. Note: sequences start with 0 token. + sequences: jnp.ndarray + scores: jnp.array # Total sequence scores per batch element [B*N]. + logprobs: jnp.array # Logprobs of selected tokens [B*N, L]. + rng: jnp.ndarray # PRNGKey of the loop state. + + +def _init_state(prompts, cache, init_rng_key, num_samples): + batch_size, max_decode_len_plus_one = prompts.shape + # Add extra samples dim to attention cache pytree elements. + cache = cache_map( + lambda x: expand_samples_dim_and_flatten(x, num_samples), cache) + return LoopState( + cur_index=0, + cache=cache, + flags_finished=jnp.zeros((batch_size*num_samples), dtype=jnp.bool_), + sequences=expand_samples_dim_and_flatten(prompts, num_samples), + scores=jnp.zeros((batch_size*num_samples)), + logprobs=jnp.zeros((batch_size*num_samples, max_decode_len_plus_one-1)), + rng=init_rng_key) + + +def _should_temperature_sampling_continue(state, max_decode_len): + """Check if we should continue or not.""" + + max_length_not_reached = state.cur_index < max_decode_len - 1 + all_seqs_finished = jnp.all(state.flags_finished) + return max_length_not_reached & (~all_seqs_finished) + + +def _temperature_sampling_iteration(state, tokens_to_logits, temperature, eos, + top_k, top_p, mask_token_ids=()): + """Temperature sampling step function.""" + + rng_sampling, rng = jax.random.split(state.rng) + + # 1. Use the model to generate a distribution over the vocabulary (for the + # next token) and sample from it, optionally applying the temperature. + # --> [B,]. + cur_tokens = state.sequences[:, state.cur_index] + logits, new_cache = tokens_to_logits(cur_tokens[:, None], state.cache) + assert logits.ndim == 2, ("tokens_to_logits expected to return a" + f"2-dimensional array [B, V], got {logits.ndim}" + "dimensions.") + logprobs = jax.nn.log_softmax(logits) + + # Do not sample special tokens in with ids in mask_token_ids. + if mask_token_ids: + probs = jax.nn.softmax(logits) + for i in mask_token_ids: + probs = probs.at[:, i].set(0.) + probs = probs / jnp.sum(probs, -1, keepdims=True) + logits = jnp.log(probs) + + if top_p: # Nucleus sampling. + logits_sorted = jnp.sort(logits, axis=-1)[:, ::-1] + sorted_cum_probs = jnp.cumsum( + jax.nn.softmax(logits_sorted, axis=-1), axis=-1) + cutoff_index = jnp.sum(sorted_cum_probs < top_p, axis=-1, keepdims=True) + cutoff_logit = jnp.take_along_axis(logits_sorted, cutoff_index, axis=-1) + logits = jnp.where(logits < cutoff_logit, + jnp.full_like(logits, NEG_INF), logits) + if top_k: + topk_logits, topk_indices = jax.lax.top_k(logits, top_k) + topk_token = jax.random.categorical(rng_sampling, topk_logits / temperature) + sampled_tokens = jnp.squeeze( + jnp.take_along_axis(topk_indices, jnp.expand_dims(topk_token, -1), + axis=-1), axis=-1) + else: + sampled_tokens = jax.random.categorical(rng_sampling, logits / temperature) + + sampled_logprobs = jnp.squeeze(jnp.take_along_axis( + logprobs, jnp.expand_dims(sampled_tokens, axis=1), axis=-1), axis=-1) + + # 2. Use the sampled tokens to update the sequences that did not finish yet, + # but only if they are out of prompt. + next_tokens = state.sequences[:, state.cur_index + 1] + next_logprobs = jnp.squeeze(jnp.take_along_axis( + logprobs, jnp.expand_dims(next_tokens, axis=1), axis=-1), axis=-1) + out_of_prompt = next_tokens == 0 + update_pos = out_of_prompt * (~state.flags_finished) + next_tokens = sampled_tokens * update_pos + next_tokens * (~update_pos) + sampled_logprobs = update_pos*sampled_logprobs + ~update_pos*next_logprobs + sequences = state.sequences.at[:, state.cur_index + 1].set(next_tokens) + scores = state.scores + sampled_logprobs + seqs_logprobs = state.logprobs.at[:, state.cur_index].set(sampled_logprobs) + + # 3. Update the finished flags. Only out of prompts seqs can finish. + flags_finished = out_of_prompt & (state.flags_finished | + (sampled_tokens == eos)) + return LoopState( + cur_index=state.cur_index+1, + cache=new_cache, + flags_finished=flags_finished, + sequences=sequences, + scores=scores, + logprobs=seqs_logprobs, + rng=rng) + + +def _temperature_sampling(prompts, cache, tokens_to_logits, num_samples=1, + eos_token=EOS_ID, max_decode_len=None, + seed=0, temperature=1., top_k=0, top_p=0.0, + mask_token_ids=()): + """Temperature sampling. + + Purely stochastic sampling-based greedy procedure to generate sequences. Every + next token in the sequence is sampled from the discrete vocab distribution + produced by the auto-regressive sequence model. Optionally we can adjust the + distribution by changing the temperature before sampling from it. Generated + sequences are no longer than max_decode_len. + + Args: + prompts: optional prompts [B, L]. By default (None), we call free form + generation without any prompts. Prompt sequences should finish with + trailing zeros and should not contain eos tokens. + cache: cache for fast decoding (generation). + tokens_to_logits: fast autoregressive decoder function taking single token + slices and cache and returning next-token logits and updated cache. + num_samples: int: number of samples to generate per batch item. Note, no + deduplication is performed, and in dependence of parameter settings, same + sequences could be generated and returned. + eos_token: end-of-sentence token. + max_decode_len: maximal length of generated sequences (L). + seed: PRNGKey for random sampling. + temperature: positive real-valued sampling temperature. By default we sample + from the original distribution. As the temperature approaches 0., the + entire distribution concentrates on the most probable outcome(s). + top_k: limit sampling to only top-k logits. Zero means no limit. + top_p: limit sampling to smallest number of top logits with max cumulative + prob <= top_p. Zero means no limit. Cannot use both top_p and top_k. + mask_token_ids: if set then tokens with given ids are not sampled. + + Returns: + sequences: generated sequences [B, num_samples, L]. + scores: not implemented in the naive temperature sampling [B, num_samples]. + logprobs: Log probabilities for the generated tokens [B, num_samples, L]. + """ + if top_k > 0 and top_p > 0.0: + raise ValueError(f"Cannot use both top_k {top_k} and top_p {top_p}.") + if max_decode_len is None: + max_decode_len = prompts.shape[1] + # We will start generating sequences from 0 token. + prompts = jnp.pad(prompts, ((0, 0), (1, 0))) + eos = jnp.array(eos_token) + if isinstance(seed, int): + seed = jax.random.PRNGKey(seed) + + # Initialize the state. + loop_init_state = _init_state(prompts, cache, seed, num_samples) + should_temperature_sampling_continue_fn = functools.partial( + _should_temperature_sampling_continue, + max_decode_len=max_decode_len+1) # Account for prompt padding with 0's. + temperature_sampling_iteration_fn = functools.partial( + _temperature_sampling_iteration, + tokens_to_logits=tokens_to_logits, + temperature=temperature, top_k=top_k, top_p=top_p, + eos=eos, mask_token_ids=mask_token_ids) + + # Run the temperature sampling and generate the sequences. + final_state = lax.while_loop( + should_temperature_sampling_continue_fn, + temperature_sampling_iteration_fn, + loop_init_state) + + # Return the generated sequences, discarding the 0 token in the beginning. + return ( + final_state.sequences[:, 1:].reshape((-1, num_samples, max_decode_len)), + final_state.scores.reshape((-1, num_samples)), + final_state.logprobs.reshape((-1, num_samples, max_decode_len))) diff --git a/big_vision/models/proj/uvim/vit.py b/big_vision/models/proj/uvim/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..7f3ac42b357b2f2f7315373df4419d3c69e7073c --- /dev/null +++ b/big_vision/models/proj/uvim/vit.py @@ -0,0 +1,338 @@ +# Copyright 2022 Big Vision 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. + +"""VQ-VAE autoencoder with ViT backbone.""" + +import functools +from typing import Mapping, Optional, Sequence, Union + +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit + +import einops +import flax.linen as nn +import flax.training.checkpoints +import jax +import jax.numpy as jnp +import numpy as np + + +partial = functools.partial + +# Multiplicative perturbation applied to codewords when doing the split. +# Note, the multiplicative pertubation is not perfectly symmetric and rep. +# applications can shrink the embedding. However, in practice it does not matter +# for the value we use. +PERTURB = 0.001 + + +# The function below takes a vector `x` and a dictioniary of vectors `e` as an +# input. It then returns a "quantized" version of x (namely the closest to `x` +# vector from `e`) and its index in `e` as well. +# On top of this, it has two extra features: +# 1. Double `vmap` vectorizes this function to operate on many `x` vectors. +# More concretely, we add two extra dimensions (batch and space) to `x`. +# Also note we compute euclidian distance in a decomposed way, because it +# makes it more efficient for vmapping. +# 2. `quantize` is a "discrete" operation, so it does not have a gradient for +# `x`. So we implement a so-called "straight-through" gradient estimator +# using `stop_gradient` magic. It does not affect forward pass, but changes +# the gradient. +@partial(jax.vmap, in_axes=(0, None), out_axes=(0, 0)) +@partial(jax.vmap, in_axes=(0, None), out_axes=(0, 0)) +def quantize(x, e): + dist = jnp.sum(x * x)[None] - 2 * x.dot(e.T) + jnp.sum(e * e, axis=1) + idx = jnp.argmin(dist) + x_q = jax.lax.stop_gradient(e[idx] - x) + x # just `e[idx]` for the fwd pass. + return x_q, idx + + +def split_the_most_frequent_embedding(state): + """Splits most frequent embedding into two and eliminates least frequent. + + Args: + state: a dict. that contains current jax rng, embeddings and their counts. + + Returns: + New dict. with the updated jax rng, embeddings and counts. + """ + rng, e, c = state["rng"], state["dictionary"], state["counts"] + rng, rng_local = jax.random.split(rng) + + i_max = jnp.argmax(c) + i_min = jnp.argmin(c) + + e = e.at[i_min].set( + e[i_max] * jax.random.uniform(rng_local, (e.shape[1],), jnp.float32, + 1.0-PERTURB, 1.0+PERTURB)) + + c = c.at[i_min].set(c[i_max] / 2.0) + c = c.at[i_max].set(c[i_max] / 2.0) + + e = e.at[i_min].set(e[i_min] / 2.0) + e = e.at[i_max].set(e[i_max] / 2.0) + + return {"rng": rng, "dictionary": e, "counts": c} + + +class Model(nn.Module): + """ViT model.""" + + inputs: Mapping[str, Sequence[int]] + outputs: Mapping[str, Sequence[int]] + input_size: Sequence[int] = (256, 256) + patch_size: Sequence[int] = (8, 8) + code_len: int = 256 + width: int = 768 + enc_depth: int = 6 + dec_depth: int = 6 + mlp_dim: Optional[int] = None + num_heads: int = 12 + posemb: str = "learn" # Can also be "sincos2d" + rep_size: Union[int, bool] = False + dropout: float = 0.0 + reinit: Optional[Sequence[str]] = None + head_zeroinit: bool = True + dict_size: int = 512 # Number of words in dict. + codeword_dim: Optional[int] = None + dict_momentum: float = 0.995 # Exp. moving average coeff. for dict. learning. + quantize: bool = True + # Useful to set to None when running without pmap, e.g. testing. + statistics_axis_name: str = "batch" + # Threshold for the discounted count after which the codeword will be + # considered unused. For the `dict_momentum` param of 0.995 the codeword + # should not be present in ~500 batches in a row. + min_count: float = 0.1 # ~= 0.995 ** 500 + with_encoder_ctx: bool = False + with_decoder_ctx: bool = False + code_dropout: str = "none" + bottleneck_resize: bool = False + zero_decoder_seq: bool = False + + def setup(self): + + self.grid_size = np.array(self.input_size) // np.array(self.patch_size) + + self.embeddings = { + k: nn.DenseGeneral(features=(self.width,), axis=range(-len(shape), 0), + name=f"embedding_{k}") + for k, shape in self.inputs.items() + } + + kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {} + self.heads = { + k: nn.DenseGeneral(features=shape, name=f"head_{k}", **kw) + for k, shape in self.outputs.items() + } + + if self.with_encoder_ctx: + self.stem_conv_ctx_enc = nn.Conv( + self.width, self.patch_size, strides=self.patch_size, + padding="VALID", name="ctx_enc_embedding") + + if self.with_decoder_ctx: + self.stem_conv_ctx_dec = nn.Conv( + self.width, self.patch_size, strides=self.patch_size, + padding="VALID", name="ctx_dec_embedding") + + self.pos_embedding_encoder = vit.get_posemb( + self, self.posemb, self.grid_size, self.width, "pos_embedding_encoder") + self.encoder = vit.Encoder( + depth=self.enc_depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout, + name="encoder") + + if not self.bottleneck_resize: + self.bottleneck_downsample = self.param( + "bottleneck_downsample", + nn.initializers.xavier_uniform(), + (np.prod(self.grid_size), self.code_len)) + + norm_init = nn.initializers.normal(stddev=1.0 / np.sqrt(self.dict_size)) + self.dictionary = self.variable( + "state", "dictionary", + lambda shape: norm_init(self.make_rng("state"), shape), + (self.dict_size, self.codeword_dim or self.width)) + self.counts = self.variable("state", "counts", jnp.ones, (self.dict_size,)) + + if not self.bottleneck_resize: + self.bottleneck_upsample = self.param( + "bottleneck_upsample", + nn.initializers.xavier_uniform(), + (self.code_len, np.prod(self.grid_size))) + + self.pos_embedding_decoder = vit.get_posemb( + self, self.posemb, self.grid_size, self.width, "pos_embedding_decoder") + self.decoder = vit.Encoder( + depth=self.dec_depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout, + name="decoder") + + self.encoder_head = nn.Dense(self.codeword_dim or self.width) + self.decoder_stem = nn.Dense(self.width) + + def get_codewords(self): + e = self.dictionary.value / self.counts.value[:, None] + e = e / jnp.linalg.norm(e, axis=-1, keepdims=True) + return e + + def encode(self, x, *, ctx=None, train=False, update_dict=True): + out = {} + + out["stem"] = {} + for key, embed in self.embeddings.items(): + out["stem"][key] = embed(x[key]) + x = sum(out["stem"].values()) + + if self.with_encoder_ctx: + ctx_tokens = self.stem_conv_ctx_enc(ctx) + ctx_tokens = einops.rearrange(ctx_tokens, "b h w c -> b (h w) c") + x = x + ctx_tokens + + x, _ = self.encoder(x + self.pos_embedding_encoder, deterministic=not train) + + if self.bottleneck_resize: + x = einops.rearrange(x, "b (h w) c -> b h w c", + h=self.grid_size[0], w=self.grid_size[1]) + l = int(np.round(self.code_len ** 0.5)) + x = jax.image.resize( + x, (x.shape[0], l, l, x.shape[3]), + method="linear") + x = einops.rearrange(x, "b h w c -> b (h w) c") + else: + x = jnp.einsum("btc,tn->bnc", x, self.bottleneck_downsample) + + x = self.encoder_head(x) + + x = jax.nn.standardize(x, axis=-1) + x_pre_q = out["bottleneck"] = x + e = self.get_codewords() + x, idx = quantize(x, e) + out["bottleneck_q"] = x + out["code"] = idx + + # Implements explicit dictionary learning algo outlined in the VQ-VAE paper. + # We slightly deviate from the papers formulation, as we find it confusing, + # especially in the multi-host scenario. What is implemented below can be + # seen as computing discounted counts and sums of all embeddings. + if train: + # Compute counts and sum(x) of code in the global batch. + counts = jnp.zeros(self.dict_size, dtype=jnp.int32) + counts = counts.at[idx].add(1) + + # Below we introduce redundant stop_gradient, because jax' dead code + # elimination for our program's gradient fails to infer that the code + # below does not require gradient computation. + # Relevant github issue: https://github.com/google/jax/issues/9042. + # TODO: remove stop_gradient when the bug is fixed. + x_sum = jnp.zeros_like(self.dictionary.value) + x_sum = x_sum.at[idx].add(jax.lax.stop_gradient(x_pre_q)) + + if self.statistics_axis_name: + counts = jax.lax.psum(counts, axis_name=self.statistics_axis_name) + x_sum = jax.lax.psum(x_sum, axis_name=self.statistics_axis_name) + + out["codebook_max_ratio"] = jnp.max(counts) / jnp.sum(counts) + out["codebook_zeros_ratio"] = jnp.sum(counts == 0) / len(counts) + + if update_dict: + self.counts.value = self.counts.value * self.dict_momentum + counts + self.dictionary.value = (self.dictionary.value * self.dict_momentum + + x_sum) + + state = {"dictionary": self.dictionary.value, + "counts": self.counts.value, + "rng": self.make_rng("vqvae")} + new_state = jax.lax.while_loop( + lambda state: jnp.any(state["counts"] < self.min_count), + split_the_most_frequent_embedding, + state) + self.counts.value = new_state["counts"] + self.dictionary.value = new_state["dictionary"] + + if not self.quantize: + x = x_pre_q + out["bottleneck_q"] = x + return x, out + + def decode(self, x, ctx=None, discrete_input=False, train=False): + out = {} + + if discrete_input: + e = self.get_codewords() + x = e[x] + + if self.zero_decoder_seq: + x = jnp.zeros_like(x) + + if train and self.code_dropout != "none": + importance = jnp.linspace(1.0, 0.0, self.code_len + 2)[1:-1] + thr = jax.random.uniform(self.make_rng("dropout"), x.shape[:1]) + mask = importance[None, :] > thr[:, None] + if self.code_dropout == "random": + mask = jax.random.permutation( + self.make_rng("dropout"), mask, axis=-1, independent=True) + x = x * mask[:, :, None] + + x = self.decoder_stem(x) + + if self.bottleneck_resize: + l = int(np.round(self.code_len ** 0.5)) + x = einops.rearrange(x, "b (h w) c -> b h w c", h=l, w=l) + x = jax.image.resize( + x, (x.shape[0], self.grid_size[0], self.grid_size[1], x.shape[3]), + method="linear") + x = einops.rearrange(x, "b h w c -> b (h w) c") + else: + x = jnp.einsum("bnc,nt->btc", x, self.bottleneck_upsample) + + if self.with_decoder_ctx: + ctx_tokens = self.stem_conv_ctx_dec(ctx) + ctx_tokens = einops.rearrange(ctx_tokens, "b h w c -> b (h w) c") + x = x + ctx_tokens + + x, _ = self.decoder(x + self.pos_embedding_decoder) + + out["logits"] = {} + for key, head in self.heads.items(): + out["logits"][key] = head(x) + + return out["logits"], out + + def __call__(self, x, *, ctx=None, train=False, update_dict=True): + x, out_enc = self.encode(x, ctx=ctx, train=train, update_dict=update_dict) + x, out_dec = self.decode(x, ctx=ctx, train=train) + return x, {**out_enc, **out_dec} + + +def load(init_params, init_file, model_params=None, dont_load=()): + """Loads params from init checkpoint and merges into init_params.""" + del model_params + ckpt = flax.core.unfreeze(utils.load_checkpoint(None, init_file)) + params = {"params": ckpt["params"], "state": ckpt["state"]} + params = flax.training.checkpoints.convert_pre_linen(params) + # Fix old-style param name. + if "Encoder" in params["params"]: + p = params["params"] + p["encoder"] = p.pop("Encoder") + p["decoder"] = p.pop("Decoder") + params["params"] = p + if init_params is not None: + params = common.merge_params(params, init_params, dont_load) + return params["params"], params["state"] diff --git a/big_vision/models/proj/uvim/vit_test.py b/big_vision/models/proj/uvim/vit_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f42c85a2bb3ff040c73a233d01d1053adfbba5ed --- /dev/null +++ b/big_vision/models/proj/uvim/vit_test.py @@ -0,0 +1,76 @@ +# Copyright 2022 Big Vision 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. + +"""Tests for vit vqvae model.""" +from absl.testing import absltest + +from big_vision.models.proj.uvim import vit +import jax +import jax.numpy as jnp +import ml_collections + + +class ViTVQVAEModelTest(absltest.TestCase): + + def test_model(self): + model_config = ml_collections.ConfigDict({ + "input_size": (32, 32), + "code_len": 4, + "width": 16, + "mlp_dim": 64, + "num_heads": 4, + "enc_depth": 1, + "dec_depth": 1, + "with_encoder_ctx": True, + "with_decoder_ctx": True, + "statistics_axis_name": None, + "inputs": { + "in1": (10, 3), + "in2": (25,), + }, + "outputs": { + "out1": (5,), + "out2": (20,), + }, + }) + + model = vit.Model(**model_config) + batch_size = 4 + seq_len = (32 // 8) ** 2 + x = { + "in1": jnp.zeros((batch_size, seq_len, 10, 3)), + "in2": jnp.zeros((batch_size, seq_len, 25)), + } + ctx_image = jnp.zeros((batch_size,) + model_config.input_size + (3,)) + init_rngs = { + "params": jax.random.PRNGKey(0), + "state": jax.random.PRNGKey(1), + } + params = model.init(init_rngs, x, ctx=ctx_image) + self.assertEqual(params.keys(), set(["params", "state"])) + + apply_rngs = { + "dropout": jax.random.PRNGKey(0), + "vqvae": jax.random.PRNGKey(0), + } + (logits, _), params = model.apply( + params, x, ctx=ctx_image, train=True, update_dict=True, + rngs=apply_rngs, mutable=["state"]) + self.assertEqual(logits.keys(), set(["out1", "out2"])) + self.assertEqual(logits["out1"].shape, (batch_size, seq_len, 5)) + self.assertEqual(logits["out2"].shape, (batch_size, seq_len, 20)) + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/models/proj/uvim/vtt.py b/big_vision/models/proj/uvim/vtt.py new file mode 100644 index 0000000000000000000000000000000000000000..7bfd87cd7e6a5bc86d1aa6e6f7bc0afa8efa3b08 --- /dev/null +++ b/big_vision/models/proj/uvim/vtt.py @@ -0,0 +1,270 @@ +# Copyright 2022 Big Vision 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. + +"""Simple vision-text transformer with encoder-decoder architecture. + +Used abbreviations for dimension annotations: + B: batch size. + H: image height. + W: image width. + P: number of patches (PH/PW: number of patches in height/width dimensions). + E: embedding size. + L: sequence length of text tokens. + V: vocab size. +""" +from typing import Sequence +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import einops +import flax +import flax.linen as nn +import jax.numpy as jnp +import ml_collections +import numpy as np + + +def shift_right(x, axis=1): + """Shift to the right on given axis with padding value 0.""" + pad_widths = [(0, 0)] * len(x.shape) + pad_widths[axis] = (1, 0) + padded = jnp.pad(x, pad_widths, constant_values=0) + return padded[:, :-1] + + +class EncoderDecoderBlock(nn.Module): + """Transformer encoder-decoder layer.""" + mlp_dim: int + num_heads: int + dropout_rate: float = 0. + decode: bool = False + + @nn.compact + def __call__(self, targets, encoded, decoder_mask=None, deterministic=True): + """Applies EncoderDecoder1DBlock module. + + Args: + targets: target text embeddings [B, L, E]. + encoded: encoded image patches from encoder [B, P, E]. + decoder_mask: decoder self-attention mask. + deterministic: bool, deterministic or not (to apply dropout). + + Returns: + output after transformer encoder-decoder block [B, L, E]. + """ + # Decoder block. + x = nn.LayerNorm(name="LayerNorm1")(targets) + x = nn.SelfAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, decode=self.decode, name="SelfAttn")( + x, decoder_mask, deterministic=deterministic) + x = nn.Dropout(rate=self.dropout_rate)(x, deterministic=deterministic) + x = x + targets + + # Encoder-Decoder block. + y = nn.LayerNorm(name="LayerNorm2")(x) + y = nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, use_bias=False, broadcast_dropout=False, + dropout_rate=self.dropout_rate, name="CrossAttn")( + y, encoded, deterministic=deterministic) + y = nn.Dropout(rate=self.dropout_rate)(y, deterministic=deterministic) + y = y + x + + # MLP block. + z = nn.LayerNorm(name="LayerNorm3")(y) + z = vit.MlpBlock(mlp_dim=self.mlp_dim, dropout=self.dropout_rate, + name="MLP")(z, deterministic=deterministic) + + return y + z + + +class Decoder(nn.Module): + """Transformer Model Decoder for sequence to sequence translation.""" + emb_dim: int + mlp_dim: int + num_heads: int + num_layers: int + dropout_rate: float = 0. + output_vocab_size: int = 32000 + zero_decoder_seq: bool = False + + @nn.compact + def __call__(self, + encoded, + targets, + pos_emb, + decoder_mask=None, + decode=False, + deterministic=True, + max_decode_length=None): + """Applies Transformer model on the inputs. + + Args: + encoded: encoded image patches from encoder [B, P, E]. + targets: target text tokens [B, L]. + pos_emb: positional embeddings. + decoder_mask: decoder self-attention mask. + decode: bool, whether to perform fast autoregressive decoding with cache. + deterministic: bool, deterministic or not (to apply dropout). + max_decode_length: optional max length for positional embeddings. + + Returns: + output of a transformer decoder [B, L, V]. + """ + y = targets.astype("int32") + if not decode: + y = shift_right(y) + y = nn.Embed(self.output_vocab_size, self.emb_dim, name="EmbedTargets", + embedding_init=nn.initializers.normal(stddev=1.0))(y) + if self.zero_decoder_seq: + y = jnp.zeros_like(y) + y = common.AddPositionEmbs( + decode=decode, name="PosEmbedTargets")(y, pos_emb) + y = nn.Dropout(rate=self.dropout_rate)(y, deterministic=deterministic) + + for lyr in range(self.num_layers): + y = EncoderDecoderBlock( + num_heads=self.num_heads, mlp_dim=self.mlp_dim, + dropout_rate=self.dropout_rate, decode=decode, + name=f"EncDecBlock{lyr}")(y, encoded, decoder_mask=decoder_mask, + deterministic=deterministic) + y = nn.LayerNorm(name="LayerNorm")(y) + logits = nn.Dense(self.output_vocab_size, kernel_init=nn.initializers.zeros, + name="LogitsDense")(y) + return logits + + +class Model(nn.Module): + """Transformer Model for sequence to sequence translation.""" + patches: ml_collections.ConfigDict + # Encoder/decoder shared params: + num_heads: int = 8 + num_layers: int = 6 + mlp_dim: int = 2048 + dropout_rate: float = 0. + # Decoder params: + emb_dim: int = 512 + vocab_size: int = 32000 + seq_len: int = 256 + # Encoder params: + input_size: Sequence[int] = (256, 256) + posemb_type: str = "sincos2d" # Can also be "learn" + zero_decoder_seq: bool = False + + def setup(self): + grid_size = np.array(self.input_size) // np.array(self.patches.size) + self.pos_emb_for_encoder = vit.get_posemb( + self, self.posemb_type, grid_size, self.emb_dim, + "pos_embedding_encoder") + self.pos_emb_for_decoder = vit.get_posemb( + self, self.posemb_type, (1, self.seq_len), self.emb_dim, + "pos_embedding_decoder") + + self.encoder = vit.Encoder( + depth=self.num_layers, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout_rate) + self.decoder = Decoder( + num_layers=self.num_layers, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout_rate=self.dropout_rate, + emb_dim=self.emb_dim, + output_vocab_size=self.vocab_size, + zero_decoder_seq=self.zero_decoder_seq, + ) + self.conv = nn.Conv(self.emb_dim, self.patches.size, padding="VALID", + strides=self.patches.size, name="EmbedPatches") + + def encode(self, image, train=False): + """Encodes input image or embeddings.""" + emb = self.conv(image) + patch_embeddings = einops.rearrange(emb, "B PH PW E -> B (PH PW) E") + encoded, _ = self.encoder( + patch_embeddings + self.pos_emb_for_encoder, deterministic=not train) + return encoded + + def decode(self, encoded, targets, decode=False, train=False, + max_decode_length=None): + """Applies Transformer decoder-branch on encoded-input and target. + + Args: + encoded: encoded image patches from encoder [B, P, E]. + targets: target text tokens [B, L]. + decode: whether to prepare and use an autoregressive cache. + train: whether it is training. + max_decode_length: optional max length for positional embeddings. + + Returns: + logits array from transformer decoder [B, L, V]. + """ + decoder_mask = None if decode else nn.make_causal_mask(targets) + logits = self.decoder( + encoded, + targets, + pos_emb=self.pos_emb_for_decoder, + decoder_mask=decoder_mask, + decode=decode, + deterministic=not train, + max_decode_length=max_decode_length) + return logits + + def __call__(self, image, text, *, decode=False, train=False): + """Applies Transformer model on the inputs. + + Args: + image: batch of images [B, H, W, 3]. + text: batch of tokenized texts [B, L]. + decode: whether to prepare and use an autoregressive cache. + train: whether it is training. + + Returns: + logits array from full transformer [B, L, V]. + """ + encoded = self.encode(image, train=train) + return self.decode(encoded, text, decode=decode, train=train) + + +def load(init_params, init_files, model_params=None, + dont_load=("head/kernel", "head/bias", "cls")): + """Loads params from init checkpoint and merges into init_params.""" + del model_params + if isinstance(init_files, str): + # A shortcut for a single file checkpoint of a vtt model. + ckpt_params = utils.load_params(None, init_files) + ckpt_params = flax.training.checkpoints.convert_pre_linen(ckpt_params) + if init_params is not None: + ckpt_params = common.merge_params(ckpt_params, init_params, dont_load) + else: + init_files = {**init_files} # Shallow copy because we'll pop stuff off. + + enc_init = init_files.pop("encoder", None) + if enc_init: + ckpt_params = init_params.copy() + vit_params = { + "pos_embedding": ckpt_params["pos_embedding_encoder"], + "Transformer": ckpt_params["encoder"], + "embedding": ckpt_params["EmbedPatches"], + } + encoder_params = vit.load( + vit_params, enc_init, model_cfg={}, + dont_load=dont_load) + ckpt_params["encoder"] = encoder_params["Transformer"] + ckpt_params["pos_embedding_encoder"] = encoder_params["pos_embedding"] + ckpt_params["EmbedPatches"] = encoder_params["embedding"] + else: + raise ValueError("Only encoder init is supported: {}.".format(init_files)) + + return ckpt_params diff --git a/big_vision/models/proj/uvim/vtt_test.py b/big_vision/models/proj/uvim/vtt_test.py new file mode 100644 index 0000000000000000000000000000000000000000..50b279b565af6cb644bae83253b297cae1569cd7 --- /dev/null +++ b/big_vision/models/proj/uvim/vtt_test.py @@ -0,0 +1,50 @@ +# Copyright 2022 Big Vision 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. + +"""Tests for vision-text-transformer.""" +from absl.testing import absltest + +from big_vision.models.proj.uvim import vtt +import jax +import jax.numpy as jnp +import ml_collections + + +class VTTTest(absltest.TestCase): + + def test_vtt_with_1_step(self): + model_config = ml_collections.ConfigDict(dict( + input_size=(224, 224), + patches={"size": (16, 16)}, + num_heads=2, + num_layers=2, + mlp_dim=128, + emb_dim=64, + vocab_size=500)) + batch_size, max_len = 8, 50 + image = jnp.ones((batch_size, 224, 224, 3)) + text = jnp.ones((batch_size, max_len), dtype=jnp.int32) + + m = vtt.Model(**model_config) + variables = m.init(jax.random.PRNGKey(42), image, text) + self.assertCountEqual(variables.keys(), ["params"]) + + params = variables["params"] + out = m.apply({"params": params}, image, text) + expected_shape = (batch_size, max_len, model_config.vocab_size) + self.assertEqual(out.shape, expected_shape) + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/models/vit.py b/big_vision/models/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..376cb6bd8616eb49af40f0815cfbc4985df156b9 --- /dev/null +++ b/big_vision/models/vit.py @@ -0,0 +1,478 @@ +# Copyright 2024 Big Vision 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. + +"""A refactored and simplified ViT. + +However, the names of modules are made to match the old ones for easy loading. +""" + +from typing import Optional, Sequence, Union + +from absl import logging +from big_vision import utils +from big_vision.models import common +import flax +import flax.linen as nn +import flax.training.checkpoints +import jax +import jax.numpy as jnp +import numpy as np +import scipy.ndimage + + +def posemb_sincos_2d(h, w, width, temperature=10_000., dtype=jnp.float32): + """Follows the MoCo v3 logic.""" + y, x = jnp.mgrid[:h, :w] + + assert width % 4 == 0, "Width must be mult of 4 for sincos posemb" + omega = jnp.arange(width // 4) / (width // 4 - 1) + omega = 1. / (temperature**omega) + y = jnp.einsum("m,d->md", y.flatten(), omega) + x = jnp.einsum("m,d->md", x.flatten(), omega) + pe = jnp.concatenate([jnp.sin(x), jnp.cos(x), jnp.sin(y), jnp.cos(y)], axis=1) + return jnp.asarray(pe, dtype)[None, :, :] + + +def get_posemb(self, typ, seqshape, width, name, dtype=jnp.float32): + if typ == "learn": + return self.param(name, nn.initializers.normal(stddev=1/np.sqrt(width)), + (1, np.prod(seqshape), width), dtype) + elif typ == "sincos2d": + return posemb_sincos_2d(*seqshape, width, dtype=dtype) + else: + raise ValueError(f"Unknown posemb type: {typ}") + + +class MlpBlock(nn.Module): + """Transformer MLP / feed-forward block.""" + mlp_dim: Optional[int] = None # Defaults to 4x input dim + dropout: float = 0.0 + dtype_mm: str = "float32" + + @nn.compact + def __call__(self, x, deterministic=True): + """Applies Transformer MlpBlock module.""" + inits = dict( + kernel_init=nn.initializers.xavier_uniform(), + bias_init=nn.initializers.normal(stddev=1e-6), + ) + + n, l, d = x.shape # pylint: disable=unused-variable + x = nn.Dense(self.mlp_dim or 4 * d, dtype=self.dtype_mm, **inits)(x) + x = nn.gelu(x) + x = nn.Dropout(rate=self.dropout)(x, deterministic) + x = nn.Dense(d, dtype=self.dtype_mm, **inits)(x) + return x + + +class Encoder1DBlock(nn.Module): + """Single transformer encoder block (MHSA + MLP).""" + mlp_dim: Optional[int] = None # Defaults to 4x input dim + num_heads: int = 12 + dropout: float = 0.0 + dtype_mm: str = "float32" + + @nn.compact + def __call__(self, x, deterministic=True): + out = {} + x = nn.with_logical_constraint(x, ("act_batch", "act_len", "act_emb")) + y = nn.LayerNorm()(x) + y = out["sa"] = nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, + kernel_init=nn.initializers.xavier_uniform(), + deterministic=deterministic, + dtype=self.dtype_mm, + )(y, y) + y = nn.with_logical_constraint(y, ("act_batch", "act_len", "act_emb")) + y = nn.Dropout(rate=self.dropout)(y, deterministic) + x = out["+sa"] = x + y + + y = nn.LayerNorm()(x) + y = out["mlp"] = MlpBlock( + mlp_dim=self.mlp_dim, dropout=self.dropout, + dtype_mm=self.dtype_mm, + )(y, deterministic) + y = nn.with_logical_constraint(y, ("act_batch", "act_len", "act_emb")) + y = nn.Dropout(rate=self.dropout)(y, deterministic) + x = out["+mlp"] = x + y + x = nn.with_logical_constraint(x, ("act_batch", "act_len", "act_emb")) + return x, out + + +class Encoder(nn.Module): + """Transformer Model Encoder for sequence to sequence translation.""" + depth: int + mlp_dim: Optional[int] = None # Defaults to 4x input dim + num_heads: int = 12 + dropout: float = 0.0 + scan: bool = False + remat_policy: str = "nothing_saveable" + dtype_mm: str = "float32" + + @nn.compact + def __call__(self, x, deterministic=True): + out = {} + + if self.scan: + block = nn.remat( + Encoder1DBlock, + prevent_cse=False, + static_argnums=(2,), # 0=self, 2=deterministic + policy=getattr(jax.checkpoint_policies, self.remat_policy, None), + ) + x, scan_out = nn.scan( + block, + variable_axes={"params": 0}, + split_rngs={"params": True, "dropout": True}, + in_axes=nn.broadcast, + length=self.depth)( + name="encoderblock", + dtype_mm=self.dtype_mm, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout)(x, deterministic) + for lyr in range(self.depth): + out[f"block{lyr:02d}"] = jax.tree.map(lambda o, l=lyr: o[l], scan_out) + else: + # Input Encoder + for lyr in range(self.depth): + block_cur = Encoder1DBlock( + name=f"encoderblock_{lyr}", + dtype_mm=self.dtype_mm, + mlp_dim=self.mlp_dim, num_heads=self.num_heads, + dropout=self.dropout) + x, out[f"block{lyr:02d}"] = block_cur(x, deterministic) + out["pre_ln"] = x # Alias for last block, but without the number in it. + + return nn.LayerNorm(name="encoder_norm")(x), out + + +class MAPHead(nn.Module): + """Multihead Attention Pooling.""" + mlp_dim: Optional[int] = None # Defaults to 4x input dim + num_heads: int = 12 + + @nn.compact + def __call__(self, x): + # TODO + n, l, d = x.shape # pylint: disable=unused-variable + probe = self.param("probe", nn.initializers.xavier_uniform(), + (1, 1, d), x.dtype) + probe = jnp.tile(probe, [n, 1, 1]) + + x = nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, + kernel_init=nn.initializers.xavier_uniform())(probe, x) + + # TODO: dropout on head? + y = nn.LayerNorm()(x) + x = x + MlpBlock(mlp_dim=self.mlp_dim)(y) + return x[:, 0] + + +class _Model(nn.Module): + """ViT model.""" + + num_classes: Optional[int] = None + patch_size: Sequence[int] = (16, 16) + width: int = 768 + depth: int = 12 + mlp_dim: Optional[int] = None # Defaults to 4x input dim + num_heads: int = 12 + posemb: str = "learn" # Can also be "sincos2d" + rep_size: Union[int, bool] = False + dropout: float = 0.0 + pool_type: str = "gap" # Can also be "map" or "tok" + head_zeroinit: bool = True + scan: bool = False + # or "dots_with_no_batch_dims_saveable" for more speed (memory costly) + remat_policy: str = "nothing_saveable" + dtype_mm: str = "float32" + + @nn.compact + def __call__(self, image, *, train=False): + out = {} + + image = jnp.asarray(image, self.dtype_mm) + + # Patch extraction + x = out["stem"] = nn.Conv( + self.width, self.patch_size, strides=self.patch_size, + padding="VALID", name="embedding", dtype=self.dtype_mm)(image) + + n, h, w, c = x.shape + x = jnp.reshape(x, [n, h * w, c]) + + # Add posemb before adding extra token. + x = out["with_posemb"] = x + get_posemb( + self, self.posemb, (h, w), c, "pos_embedding", x.dtype) + + if self.pool_type == "tok": + cls = self.param("cls", nn.initializers.zeros, (1, 1, c), x.dtype) + x = jnp.concatenate([jnp.tile(cls, [n, 1, 1]), x], axis=1) + + n, l, c = x.shape # pylint: disable=unused-variable + x = nn.Dropout(rate=self.dropout)(x, not train) + + x, out["encoder"] = Encoder( + depth=self.depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + dropout=self.dropout, + scan=self.scan, + remat_policy=self.remat_policy, + dtype_mm=self.dtype_mm, + name="Transformer")( + x, deterministic=not train) + encoded = out["encoded"] = x + + if self.pool_type == "map": + x = out["head_input"] = MAPHead( + num_heads=self.num_heads, mlp_dim=self.mlp_dim)(x) + elif self.pool_type == "gap": + x = out["head_input"] = jnp.mean(x, axis=1) + elif self.pool_type == "0": + x = out["head_input"] = x[:, 0] + elif self.pool_type == "tok": + x = out["head_input"] = x[:, 0] + encoded = encoded[:, 1:] + elif self.pool_type == "none": + pass + else: + raise ValueError(f"Unknown pool type: '{self.pool_type}'") + + x_2d = jnp.reshape(encoded, [n, h, w, -1]) + + if self.rep_size: + rep_size = self.width if self.rep_size is True else self.rep_size + hid = nn.Dense(rep_size, name="pre_logits") + # NOTE: In the past we did not include tanh in pre_logits. + # For few-shot, it should not matter much, as it whitens anyways. + x_2d = nn.tanh(hid(x_2d)) + x = nn.tanh(hid(x)) + + out["pre_logits_2d"] = x_2d + out["pre_logits"] = x + + if self.num_classes: + kw = {"kernel_init": nn.initializers.zeros} if self.head_zeroinit else {} + head = nn.Dense(self.num_classes, name="head", **kw) + x_2d = out["logits_2d"] = head(x_2d) + x = out["logits"] = head(x) + + return x, out + + +def Model(num_classes=None, *, variant=None, **kw): # pylint: disable=invalid-name + """Factory function, because linen really don't like what I'm doing!""" + return _Model(num_classes, **{**decode_variant(variant), **kw}) + + +def decode_variant(variant): + """Converts a string like "B" or "B/32" into a params dict.""" + if variant is None: + return {} + + v, patch = variant, {} + if "/" in variant: + v, patch = variant.split("/") + patch = {"patch_size": (int(patch), int(patch))} + + return { + # pylint:disable=line-too-long + # Reference: Table 2 of https://arxiv.org/abs/2106.04560. + "width": {"mu": 32, "Ti": 192, "S": 384, "M": 512, "B": 768, "L": 1024, "So400m": 1152, "H": 1280, "g": 1408, "g-opt": 1536, "G": 1664, "G-opt": 1536, "e": 1792}[v], + "depth": {"mu": 1, "Ti": 12, "S": 12, "M": 12, "B": 12, "L": 24, "So400m": 27, "H": 32, "g": 40, "g-opt": 40, "G": 48, "G-opt": 48, "e": 56}[v], + "mlp_dim": {"mu": 128, "Ti": 768, "S": 1536, "M": 2048, "B": 3072, "L": 4096, "So400m": 4304, "H": 5120, "g": 6144, "g-opt": 6144, "G": 8192, "G-opt": 8192, "e": 15360}[v], + "num_heads": {"mu": 2, "Ti": 3, "S": 6, "M": 8, "B": 12, "L": 16, "So400m": 16, "H": 16, "g": 16, "g-opt": 16, "G": 16, "G-opt": 16, "e": 16}[v], + # pylint:enable=line-too-long + **patch + } + + +def resample_posemb(old, new): + """This function implements "high-res finetuning" for transformer models.""" + # Rescale the grid of position embeddings. Param shape is (1,N,1024) + if old.shape == new.shape: + return old + + logging.info("ViT: resize %s to %s", old.shape, new.shape) + gs_old = int(np.sqrt(old.shape[1])) + gs_new = int(np.sqrt(new.shape[1])) + logging.info("ViT: grid-size from %s to %s", gs_old, gs_new) + grid = old.reshape(gs_old, gs_old, -1) + + zoom = (gs_new/gs_old, gs_new/gs_old, 1) + grid = scipy.ndimage.zoom(grid, zoom, order=1) + grid = grid.reshape(1, gs_new*gs_new, -1) + return grid + + +def fix_old_checkpoints(params): + """Fix small bwd incompat that can't be resolved with names in model def.""" + + params = flax.core.unfreeze( + flax.training.checkpoints.convert_pre_linen(params)) + + # Original ViT paper variant had posemb in a module: + if "posembed_input" in params["Transformer"]: + logging.info("ViT: Loading and fixing VERY old posemb") + posemb = params["Transformer"].pop("posembed_input") + params["pos_embedding"] = posemb["pos_embedding"] + + # Widely used version before 2022 had posemb in Encoder: + if "pos_embedding" in params["Transformer"]: + logging.info("ViT: Loading and fixing old posemb") + params["pos_embedding"] = params["Transformer"].pop("pos_embedding") + + # Old vit.py used to first concat [cls] token, then add posemb. + # This means a B/32@224px would have 7x7+1 posembs. This is useless and clumsy + # so we changed to add posemb then concat [cls]. We can recover the old + # checkpoint by manually summing [cls] token and its posemb entry. + if "pos_embedding" in params: + pe = params["pos_embedding"] + if int(np.sqrt(pe.shape[1])) ** 2 + 1 == int(pe.shape[1]): + logging.info("ViT: Loading and fixing combined cls+posemb") + pe_cls, params["pos_embedding"] = pe[:, :1], pe[:, 1:] + if "cls" in params: + params["cls"] += pe_cls + + # MAP-head variants during ViT-G development had it inlined: + if "probe" in params: + params["MAPHead_0"] = { + k: params.pop(k) for k in + ["probe", "MlpBlock_0", "MultiHeadDotProductAttention_0", "LayerNorm_0"] + } + + return params + + +def pyloop_to_scan(params_pyloop): + """Converts a python for-loop ViT checkpoint to a lax.scan based one.""" + # On a high level, they are the same except that the for loop has separate + # array pytrees for each encoderblock, while the scan one has just one + # encoderblock pytree, with all block's params concatenated. + + params_scan = jax.tree.map(lambda x: x, params_pyloop) # Structural copy + t = params_scan["Transformer"] + + # Find highest index of encoderblocks in the checkpoint (they start at 0): + encoderblocks = {k for k in t if k.startswith("encoderblock_")} + depth = 1 + max({int(k.split("_")[-1]) for k in encoderblocks}) + + def stack(*values): + return np.stack(values) + + # Stack all encoderblocks into a single one: + t["encoderblock"] = jax.tree.map( + stack, *[t[f"encoderblock_{lyr}"] for lyr in range(depth)]) + + for lyr in range(depth): + del t[f"encoderblock_{lyr}"] + + return params_scan + + +def scan_to_pyloop(params_scan): + """Converts a lax.scan ViT checkpoint to a python for-loop based one.""" + # See comment in pyloop_to_scan. + + params_scan = jax.tree.map(lambda x: x, params_scan) # Structural copy + t = params_scan["Transformer"] + + # Find out how many encoderblocks there are + depth = len(t["encoderblock"]["LayerNorm_0"]["bias"]) + + # Create that many encoderblocks, each with their slice of their sub-pytree. + for lyr in range(depth): + block = jax.tree.map(lambda x, lyr=lyr: x[lyr], t["encoderblock"]) + t[f"encoderblock_{lyr}"] = block + + del t["encoderblock"] + return params_scan + + +def load(init_params, init_file, model_cfg, dont_load=()): # pylint: disable=invalid-name because we had to CamelCase above. + """Load init from checkpoint, both old model and this one. +Hi-res posemb.""" + init_file = VANITY_NAMES.get(init_file, init_file) + restored_params = utils.load_params(init_file) + + restored_params = fix_old_checkpoints(restored_params) + + # Detect attempts to load non-scan checkpoint into scan model. + if (model_cfg.get("scan") and + "encoderblock" not in restored_params["Transformer"]): + restored_params = pyloop_to_scan(restored_params) + if (not model_cfg.get("scan") + and "encoderblock" in restored_params["Transformer"]): + restored_params = scan_to_pyloop(restored_params) + + # possibly use the random init for some of the params (such as, the head). + restored_params = common.merge_params(restored_params, init_params, dont_load) + + # resample posemb if needed. + # TODO: Take this from model_cfg to avoid need for init_params. + if init_params and "pos_embedding" in init_params: + restored_params["pos_embedding"] = resample_posemb( + old=restored_params["pos_embedding"], + new=init_params["pos_embedding"]) + + return restored_params + + +# Shortcut names for some canonical paper checkpoints: +VANITY_NAMES = { + # pylint: disable=line-too-long + # Recommended models from https://arxiv.org/abs/2106.10270 + # Many more models at https://github.com/google-research/vision_transformer + "howto-i21k-Ti/16": "gs://vit_models/augreg/Ti_16-i21k-300ep-lr_0.001-aug_none-wd_0.03-do_0.0-sd_0.0.npz", + "howto-i21k-S/32": "gs://vit_models/augreg/S_32-i21k-300ep-lr_0.001-aug_none-wd_0.1-do_0.0-sd_0.0.npz", + "howto-i21k-S/16": "gs://vit_models/augreg/S_16-i21k-300ep-lr_0.001-aug_light1-wd_0.03-do_0.0-sd_0.0.npz", + "howto-i21k-B/32": "gs://vit_models/augreg/B_32-i21k-300ep-lr_0.001-aug_light1-wd_0.1-do_0.0-sd_0.0.npz", + "howto-i21k-B/16": "gs://vit_models/augreg/B_16-i21k-300ep-lr_0.001-aug_medium1-wd_0.1-do_0.0-sd_0.0.npz", + "howto-i21k-B/8": "gs://vit_models/augreg/B_8-i21k-300ep-lr_0.001-aug_medium2-wd_0.1-do_0.0-sd_0.0.npz", + "howto-i21k-L/16": "gs://vit_models/augreg/L_16-i21k-300ep-lr_0.001-aug_strong1-wd_0.1-do_0.0-sd_0.0.npz", + + # Better plain vit-s16 baselines from https://arxiv.org/abs/2205.01580 + "i1k-s16-90ep": "gs://big_vision/vit_s16_i1k_90ep.npz", + "i1k-s16-150ep": "gs://big_vision/vit_s16_i1k_150ep.npz", + "i1k-s16-300ep": "gs://big_vision/vit_s16_i1k_300ep.npz", + + # DeiT-3 checkpoints from https://github.com/facebookresearch/deit/blob/main/README_revenge.md + # First layer converted to take inputs in [-1,1] + "deit3_S_224_1k": "gs://big_vision/zoo/deit3/bv_deit_3_small_224_1k.npz", + "deit3_S_224_21k": "gs://big_vision/zoo/deit3/bv_deit_3_small_224_21k.npz", + "deit3_S_384_1k": "gs://big_vision/zoo/deit3/bv_deit_3_small_384_1k.npz", + "deit3_S_384_21k": "gs://big_vision/zoo/deit3/bv_deit_3_small_384_21k.npz", + "deit3_B_224_1k": "gs://big_vision/zoo/deit3/bv_deit_3_base_224_1k.npz", + "deit3_B_224_21k": "gs://big_vision/zoo/deit3/bv_deit_3_base_224_21k.npz", + "deit3_B_384_1k": "gs://big_vision/zoo/deit3/bv_deit_3_base_384_1k.npz", + "deit3_B_384_21k": "gs://big_vision/zoo/deit3/bv_deit_3_base_384_21k.npz", + "deit3_L_224_1k": "gs://big_vision/zoo/deit3/bv_deit_3_large_224_1k.npz", + "deit3_L_224_21k": "gs://big_vision/zoo/deit3/bv_deit_3_large_224_21k.npz", + "deit3_L_384_1k": "gs://big_vision/zoo/deit3/bv_deit_3_large_384_1k.npz", + "deit3_L_384_21k": "gs://big_vision/zoo/deit3/bv_deit_3_large_384_21k.npz", + + # SigLIP image encoder checkpoints from https://arxiv.org/abs/2303.15343 + "SigLIP B/16 224": "gs://big_vision/siglip/webli_en_b16_224_63724782.npz:img", + "SigLIP B/16 256": "gs://big_vision/siglip/webli_en_b16_256_60500360.npz:img", + "SigLIP B/16 384": "gs://big_vision/siglip/webli_en_b16_384_68578854.npz:img", + "SigLIP B/16 512": "gs://big_vision/siglip/webli_en_b16_512_68580893.npz:img", + "SigLIP L/16 256": "gs://big_vision/siglip/webli_en_l16_256_60552751.npz:img", + "SigLIP L/16 384": "gs://big_vision/siglip/webli_en_l16_384_63634585.npz:img", + "SigLIP So400m/14 224": "gs://big_vision/siglip/webli_en_so400m_224_57633886.npz:img", + "SigLIP So400m/14 384": "gs://big_vision/siglip/webli_en_so400m_384_58765454.npz:img", + "SigLIP B/16-i18n 256": "gs://big_vision/siglip/webli_i18n_b16_256_66117334.npz:img", + # pylint: enable=line-too-long +} diff --git a/big_vision/optax.py b/big_vision/optax.py new file mode 100644 index 0000000000000000000000000000000000000000..10b856067449c42e35931a1bdad6edaed2030ab3 --- /dev/null +++ b/big_vision/optax.py @@ -0,0 +1,222 @@ +# Copyright 2024 Big Vision 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. + +"""Gradient transformations and other optax utilities.""" + +import operator +import big_vision.utils as u +import jax +import jax.numpy as jnp +import optax + + +def find_states(opt_state, cls): + leaves = jax.tree.leaves( + opt_state, is_leaf=lambda node: isinstance(node, cls)) + return [leaf for leaf in leaves if isinstance(leaf, cls)] + + +def get_count(opt_state, jittable=False): + """Returns `ScaleByScheduleState.count` from `opt_state` as an integer.""" + counts = [ + state.count + for state in find_states(opt_state, optax.ScaleByScheduleState) + ] + if jittable: + return counts[0] + else: + counts = {int(c) for c in counts} + assert len(counts) == 1, f"Expected exactly 1 ScaleByScheduleState:{counts}" + return next(iter(counts)) + + +def replace_frozen(schedule, pytree, replacement, log=None): + """Replaces values matching frozen params in `pytree` with `replacement`.""" + if not isinstance(schedule, (list, tuple)): + return pytree + masks, scheds = _make_mask_trees(pytree, schedule, log=log) + frozen_mask, _, _ = _split_frozen(masks, scheds) + return jax.tree.map( + lambda v, f: replacement if f else v, pytree, frozen_mask) + + +def clip_by_per_example_global_norm( + max_norm: float, +) -> optax.GradientTransformation: + """Clips the norm of per-example gradients.""" + + def init_fn(params): + del params + return optax.EmptyState() + + def update_fn(updates, state, params=None): + del params + grads_flat, grads_treedef = jax.tree_util.tree_flatten(updates) + clipped, _ = optax.per_example_global_norm_clip(grads_flat, max_norm) + return jax.tree_util.tree_unflatten(grads_treedef, clipped), state + + return optax.GradientTransformation(init_fn, update_fn) + + +def make(config, params, *, sched_kw): + """Returns gradient transform and learning rate functions.""" + + # Global schedule. No schedule means frozen. + schedule = config.get("schedule", {}) + if not isinstance(schedule, (tuple, list)): + schedule = [(".*", schedule)] + masks, scheds = _make_mask_trees(params, schedule, "config.schedule") + frozen_mask, masks, scheds = _split_frozen(masks, scheds) + not_frozen_mask = jax.tree.map(operator.not_, frozen_mask) + def create_schedule(mult=1.0, **kw): + assert "base" not in kw, kw + return u.create_learning_rate_schedule(base=mult, **kw) + schedule_fns = [create_schedule(**sched_kw, **sched) for sched in scheds] + schedule_txs = [ + optax.masked(optax.scale_by_schedule(schedule_fn), mask) + for schedule_fn, mask in zip(schedule_fns, masks) + ] + [ + # Removes weight decay updates. Note that weight decay already has an + # independent mask (which cannot be combined easily with a second mask), + # so instead we multiply updates for frozen params with zero. + optax.masked(optax.set_to_zero(), frozen_mask) + ] + + # Gradient clipping. + if clip_norm := config.get("grad_clip_norm"): + if config.get("grad_clip_per_example"): + clip_tx = clip_by_per_example_global_norm(clip_norm) + else: + clip_tx = optax.clip_by_global_norm(clip_norm) + grad_clip_norm_tx = optax.masked(clip_tx, not_frozen_mask) + else: + grad_clip_norm_tx = optax.identity() + + # Optimizer updates. + tx_func = operator.attrgetter(config.optax_name)(optax) + opt_txs = [optax.masked(tx_func(**config.get("optax", {})), not_frozen_mask)] + assert "optim" not in config, "Deprecated option, use config.optax." + + # Learning rate multipliers. Defaults to 1.0. + lr_mult_txs = [optax.scale(config.lr)] + if config.get("lr_mults"): + masks, mults = _make_mask_trees(params, config.lr_mults, "config.lr_mults") + assert all(mult > 0 for mult in mults), ( + f"Use schedule=None for parameter freezing instead of lr_mults={mults}") + lr_mult_txs += [ + optax.masked(optax.scale(mult), mask) + for mult, mask in zip(mults, masks) + ] + + # Weight decay. Defaults to 0.0. + # Weight decay is not gradient-based but instead uses "params side-input". + # Hence, weight decay is additive and independent of previous gradient-based + # updates. + assert "weight_decay" not in config, "Deprecated option. Use wd and schedule." + assert config.get("weight_decay_decouple", True), ( + "Coupled weight decay not supported anymore.") + if config.get("wd"): + wd_mults = config.get("wd_mults", [(".*/kernel$", 1.0)]) + masks, mults = _make_mask_trees(params, wd_mults, "config.wd_mults") + weight_decay_txs = [ + optax.add_decayed_weights(config.wd * mult, mask) + for mult, mask in zip(mults, masks) + ] + else: + weight_decay_txs = [] + + # Combine gradient updates and learning rate schedules. + return optax.chain( + grad_clip_norm_tx, + *opt_txs, + *lr_mult_txs, + *weight_decay_txs, + *schedule_txs, + optax.scale(-1.0)), schedule_fns + + +def _make_mask_trees(params, patterns_values, log): + patterns, values = zip(*patterns_values) + masks = u.make_mask_trees(params, patterns, log=log) + return masks, values + + +def _split_frozen(masks, scheds): + """Computes `frozen_mask` and updates `masks` and `scheds`.""" + # Specifying `None` as a scheduler freezes params. + all_false = jax.tree.map(lambda *bools: not any(bools), *masks) + not_covered = [k for k, v in u.tree_flatten_with_names(all_false)[0] if v] + assert not not_covered, ( + f"All params must be covered (use `None` for freezing): {not_covered}") + frozen_masks = [ + mask for mask, sched in zip(masks, scheds) if sched is None] + frozen_mask = jax.tree.map( + lambda *bools: any(bools), *frozen_masks, + all_false) # `all_false` is required when `frozen_masks==[]`. + masks, scheds = zip(*( + (mask, sched) for mask, sched in zip(masks, scheds) if sched is not None)) + return frozen_mask, masks, scheds + + +############ Custom BigVision optimizers ####################################### +# Currently there's only one custom optimizer and we don't foresee new ones in +# the near future, we opt not to create a new optimizer folder/module for just +# one isolated case. If there will be more optimizers, we can consider moving +# them into individual files in a subfolder. + + +# A dummy object to allow for foo.bar access syntax, see +# https://stackoverflow.com/a/19476841/2366315 +optax.big_vision = type("", (), {})() + + +def scale_by_adafactor(min_dim_size_to_factor=32, + decay_rate=0.8, decay_offset=0, + beta2_cap=0.999, + clipping_threshold=None, + momentum=0.9, dtype_momentum=jnp.bfloat16, + eps=1e-30): + """The BigVision variant of Adafactor optimizer.""" + + def _decay_rate_pow(i, exponent): + """Second-order moment decay schedule.""" + t = jnp.array(i, jnp.float32) + 1.0 + return jnp.minimum(beta2_cap, 1.0 - t**(-exponent)) + + scale_by_rms = optax.scale_by_factored_rms( + factored=True, + decay_rate=decay_rate, + step_offset=decay_offset, + min_dim_size_to_factor=min_dim_size_to_factor, + epsilon=eps, + decay_rate_fn=_decay_rate_pow) + + clip = (optax.clip_by_block_rms(clipping_threshold) if clipping_threshold + else optax.identity()) + + mom = (optax.ema(momentum, debias=False, accumulator_dtype=dtype_momentum) + if momentum else optax.identity()) + + return optax.chain(scale_by_rms, clip, mom) + +optax.big_vision.scale_by_adafactor = scale_by_adafactor # pytype: disable=module-attr + + +# A few more aliases we use frequently: +def momentum_hp(momentum=0.9, dtype=jnp.bfloat16, nesterov=False): + """SGD-Momentum with half-precision accumulator.""" + return optax.trace(decay=momentum, accumulator_dtype=dtype, nesterov=nesterov) + +optax.big_vision.momentum_hp = momentum_hp # pytype: disable=module-attr +optax.big_vision.sgd = optax.identity # pytype: disable=module-attr diff --git a/big_vision/optax_test.py b/big_vision/optax_test.py new file mode 100644 index 0000000000000000000000000000000000000000..86f7bd9999079b393565ad5a718b4c1dbd815e79 --- /dev/null +++ b/big_vision/optax_test.py @@ -0,0 +1,341 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for optax.""" + +from absl.testing import absltest +from absl.testing import parameterized +from big_vision import optax as bv_optax +import chex +import jax +import jax.numpy as jnp +import ml_collections +import numpy as np +import optax + + +class OptaxTest(parameterized.TestCase): + + def test_get_count(self): + params = jax.tree.map(jnp.array, {"a": 1.}) + tx = optax.masked( + optax.scale_by_schedule(lambda step: step), + {"a": True}, + ) + opt_state = tx.init(params) + self.assertEqual(bv_optax.get_count(opt_state), 0) + _, opt_state = tx.update(params, opt_state) + self.assertEqual(bv_optax.get_count(opt_state), 1) + + def test_split_frozen(self): + params = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 1., "bias": 2.}, + }) # pyformat: disable + sched1 = dict(decay_type="cosine") + sched2 = dict(decay_type="linear") + schedule = [ + (".*/kernel", sched1), + (".*/bias", sched2), + ] + masks, scheds = bv_optax._make_mask_trees(params, schedule, log="schedule") + frozen_mask, masks, scheds = bv_optax._split_frozen(masks, scheds) + chex.assert_trees_all_equal( + frozen_mask, + {"Dense_0": {"kernel": False, "bias": False}}, + ) # pyformat: disable + chex.assert_trees_all_equal( + masks, + ( + {"Dense_0": {"kernel": True, "bias": False}}, + {"Dense_0": {"kernel": False, "bias": True}}, + ), + ) # pyformat: disable + self.assertEqual(scheds, (sched1, sched2)) + # freeze some + schedule = [ + (".*/bias", None), + ("Dense_0/.*", sched1), + (".*", None), + ] + masks, scheds = bv_optax._make_mask_trees(params, schedule, log="schedule") + frozen_mask, masks, scheds = bv_optax._split_frozen(masks, scheds) + chex.assert_trees_all_equal( + frozen_mask, + {"Dense_0": {"kernel": False, "bias": True}}, + ) # pyformat: disable + chex.assert_trees_all_equal( + masks, + ({"Dense_0": {"kernel": True, "bias": False}},), + ) # pyformat: disable + self.assertEqual(scheds, (sched1,)) + # does not cover all params - fails + schedule = [ + (".*/kernel", None), + ] + masks, scheds = bv_optax._make_mask_trees(params, schedule, log="schedule") + with self.assertRaisesRegex(AssertionError, "All params must be covered"): + _ = bv_optax._split_frozen(masks, scheds) + + def test_replace_frozen(self): + params = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 1., "bias": 2.}, + }) # pyformat: disable + schedule = [ + (".*/kernel", {}), + (".*", None), + ] + chex.assert_trees_all_equal( + bv_optax.replace_frozen(schedule, params, 0.), + {"Dense_0": {"kernel": 1., "bias": 0.}}, + ) # pyformat: disable + + def test_make_simple(self): + params = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 1., "bias": 2.}, + }) # pyformat: disable + + config = ml_collections.ConfigDict() + config.lr = 0.01 + config.schedule = dict(decay_type="linear") + config.optax_name = "scale" + config.optax = ml_collections.ConfigDict() + g_scale = 0.5 + config.optax.step_size = g_scale + + total_steps = 10 + sched_kw = dict(global_batch_size=1, total_steps=total_steps) + tx, (schedule_fn,) = bv_optax.make(config, params, sched_kw=sched_kw) + opt_state = tx.init(params) + grads = jax.tree.map(jnp.ones_like, params) + for step in range(total_steps): + updates, opt_state = tx.update(grads, opt_state) + self.assertEqual(bv_optax.get_count(opt_state), step + 1) + sched = schedule_fn(step) + np.testing.assert_almost_equal( + sched, 1.0 / total_steps * (total_steps - step)) + make_tx = lambda sched: lambda g: -sched * config.lr * g_scale * g + chex.assert_trees_all_close(updates, jax.tree.map(make_tx(sched), grads)) + + def test_make_wd(self): + params = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 1., "bias": 2., "other": 3.}, + }) # pyformat: disable + wds = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 2e-3, "bias": 5e-4, "other": 0.}, + }) # pyformat: disable + + config = ml_collections.ConfigDict() + config.lr = 0.01 + config.wd = 1e-3 + config.wd_mults = [ + (".*/kernel", 2.0), + (".*/bias", 0.5), + ] + config.schedule = dict(decay_type="linear") + config.optax_name = "scale" + config.optax = ml_collections.ConfigDict() + g_scale = 0.5 + config.optax.step_size = g_scale + + total_steps = 10 + sched_kw = dict(global_batch_size=1, total_steps=total_steps) + tx, (sched_fn,) = bv_optax.make(config, params, sched_kw=sched_kw) + opt_state = tx.init(params) + grads = jax.tree.map(jnp.ones_like, params) + for step in range(total_steps): + updates, opt_state = tx.update(grads, opt_state, params) + self.assertEqual(bv_optax.get_count(opt_state), step + 1) + sched = sched_fn(step) + np.testing.assert_almost_equal( + sched, 1.0 / total_steps * (total_steps - step)) + + def make_tx(sched): + def inner(p, g, wd): + return -sched * (config.lr * g_scale * g + p * wd) + return inner + + chex.assert_trees_all_close( + updates, jax.tree.map(make_tx(sched), params, grads, wds)) + + def test_make_clip_norm(self): + params = jax.tree.map(jnp.array, { + "Dense_0": {"kernel": 1., "bias": 2., "other": 3.}, + }) # pyformat: disable + + config = ml_collections.ConfigDict() + config.lr = 0.01 + config.schedule = dict(decay_type="linear") + config.optax_name = "scale" + config.grad_clip_norm = 1.0 + config.optax = ml_collections.ConfigDict() + g_scale = 0.5 + config.optax.step_size = g_scale + + total_steps = 10 + sched_kw = dict(global_batch_size=1, total_steps=total_steps) + tx, (sched_fn,) = bv_optax.make(config, params, sched_kw=sched_kw) + opt_state = tx.init(params) + + grads = jax.tree.map(jnp.ones_like, params) + gflat = jax.tree.leaves(grads) + l2_g = jnp.sqrt(sum([jnp.vdot(p, p) for p in gflat])) + grad_clip_factor = jnp.minimum(1.0, config.grad_clip_norm / l2_g) + grads_scaled = jax.tree.map(lambda p: grad_clip_factor * p, grads) + + for step in range(total_steps): + updates, opt_state = tx.update(grads, opt_state) + self.assertEqual(bv_optax.get_count(opt_state), step + 1) + sched = sched_fn(step) + np.testing.assert_almost_equal( + sched, 1.0 / total_steps * (total_steps - step)) + make_tx = lambda sched: lambda g: -sched * config.lr * g_scale * g + chex.assert_trees_all_close(updates, + jax.tree.map(make_tx(sched), grads_scaled)) + + def test_make_multi(self): + params = jax.tree.map( + jnp.array, { + "Dense_0": {"kernel": 1.0, "bias": 2.0, "other": 3.0}, + "Dense_1": {"kernel": 4.0, "bias": 5.0, "other": 6.0}, + "Dense_2": {"kernel": 7.0, "bias": 8.0, "other": 9.0}, + "Dense_3": {"kernel": 10., "bias": 11., "other": 12.}, + }) # pyformat: disable + + # Manually specify lr + wd for computing expected values. + lrb = 0.01 + lr1 = 2.0 + lr2 = 0.5 + lr_mults = { + "Dense_0": {"kernel": lr1, "bias": lr1, "other": lr1}, + "Dense_1": {"kernel": lr2, "bias": lr2, "other": lr2}, + "Dense_2": {"kernel": 1.0, "bias": 1.0, "other": 1.0}, + "Dense_3": {"kernel": 1.0, "bias": 1.0, "other": 1.0}, + } # pyformat: disable + wdb = 1e-3 + wd1 = 10.0 + wd2 = 0.1 + wds = jax.tree.map( + jnp.array, { + "Dense_0": {"kernel": wd1 * wdb, "bias": wd2 * wdb, "other": 0.}, + "Dense_1": {"kernel": wd1 * wdb, "bias": wd2 * wdb, "other": 0.}, + "Dense_2": {"kernel": wd1 * wdb, "bias": wd2 * wdb, "other": 0.}, + "Dense_3": {"kernel": 0.0 * wdb, "bias": 0.0 * wdb, "other": 0.}, + }) # pyformat: disable + + config = ml_collections.ConfigDict() + config.lr = lrb + config.lr_mults = [ + ("Dense_0/.*", lr1), + ("Dense_1/.*", lr2), + ] + config.wd = wdb + config.wd_mults = [ + (".*/kernel", wd1), + (".*/bias", wd2), + ] + mult1 = 1.0 + mult2 = 0.1 + config.schedule = [ + ("Dense_0/.*", dict(decay_type="linear", mult=mult1, linear_end=mult1)), + ("Dense_[12]/.*", dict(decay_type="linear", mult=mult2)), + (".*", None), + ] + config.optax_name = "scale" + config.grad_clip_norm = 1.0 + config.optax = ml_collections.ConfigDict() + g_scale = 0.5 + config.optax.step_size = g_scale + + total_steps = 10 + sched_kw = dict(global_batch_size=1, total_steps=total_steps) + tx, (sched_fn1, + sched_fn2) = bv_optax.make(config, params, sched_kw=sched_kw) + opt_state = tx.init(params) + + # Manually specify schedules for computing expected values. + frozen_fn = lambda _: jnp.array(0.) + sched_fns = { + "Dense_0": {"kernel": sched_fn1, "bias": sched_fn1, "other": sched_fn1}, + "Dense_1": {"kernel": sched_fn2, "bias": sched_fn2, "other": sched_fn2}, + "Dense_2": {"kernel": sched_fn2, "bias": sched_fn2, "other": sched_fn2}, + "Dense_3": {"kernel": frozen_fn, "bias": frozen_fn, "other": frozen_fn}, + } # pyformat: disable + + grads = jax.tree.map(jnp.ones_like, params) + gflat, _ = jax.tree.flatten( + # Don't count frozen params towards gradient norm. + jax.tree.map(lambda g, sched_fn: {frozen_fn: 0}.get(sched_fn, g), + grads, sched_fns)) + l2_g = jnp.sqrt(sum([jnp.vdot(p, p) for p in gflat])) + grad_clip_factor = jnp.minimum(1.0, config.grad_clip_norm / l2_g) + grads_scaled = jax.tree.map(lambda p: grad_clip_factor * p, grads) + + def make_tx(step): + def get_update(p, g, wd, sched_fn, lr_mult): + return -sched_fn(step) * (lrb * lr_mult * g_scale * g + p * wd) + return get_update + + for step in range(total_steps): + updates, opt_state = tx.update(grads, opt_state, params) + self.assertEqual(bv_optax.get_count(opt_state), step + 1) + sched1, sched2 = sched_fn1(step), sched_fn2(step) + np.testing.assert_almost_equal(sched1, mult1) + np.testing.assert_almost_equal(sched2, + mult2 * (total_steps - step) / total_steps) + chex.assert_trees_all_close( + updates, + jax.tree.map( + make_tx(step), params, grads_scaled, wds, sched_fns, lr_mults)) + + def test_frozen_no_state(self): + params = {"small": jnp.zeros([1]), "large": jnp.zeros([1000])} + config = ml_collections.ConfigDict() + config.lr = 0.01 + config.schedule = [ + ("small", dict(decay_type="cosine")), + ("large", None), + ] + config.optax_name = "scale_by_adam" + + sched_kw = dict(global_batch_size=1, total_steps=1) + tx, _ = bv_optax.make(config, params, sched_kw=sched_kw) + + opt_state = tx.init(params) + adam_state = bv_optax.find_states(opt_state, optax.ScaleByAdamState) + nbytes = sum( + jax.tree.flatten(jax.tree.map(lambda x: x.nbytes, adam_state))[0]) + self.assertLess(nbytes, 1_000) + + def test_adafactor(self): + params = {"Dense_0": {"kernel": jnp.zeros([1024, 1024])}} + + config = ml_collections.ConfigDict() + config.optax_name = "big_vision.scale_by_adafactor" + config.lr = 0.01 + config.schedule = dict(decay_type="linear") + sched_kw = dict(global_batch_size=1, total_steps=1) + + tx, _ = bv_optax.make(config, params, sched_kw=sched_kw) + + opt_state = tx.init(params) + adafactor_state = bv_optax.find_states(opt_state, optax.FactoredState) + n_state_params = sum( + jax.tree.flatten( + jax.tree.map(lambda x: np.prod( + x.shape if hasattr(x, "shape") else 0), adafactor_state))[0]) + self.assertEqual(n_state_params, 2 * 1024 + 2) + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/pp/__init__.py b/big_vision/pp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/pp/__pycache__/__init__.cpython-310.pyc b/big_vision/pp/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36115ddb007ca9f814dcb5b4972e3cc8d330b1e3 Binary files /dev/null and b/big_vision/pp/__pycache__/__init__.cpython-310.pyc differ diff --git a/big_vision/pp/__pycache__/registry.cpython-310.pyc b/big_vision/pp/__pycache__/registry.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d45a2d1e167833beb088c9270f13f1dca5cfac7 Binary files /dev/null and b/big_vision/pp/__pycache__/registry.cpython-310.pyc differ diff --git a/big_vision/pp/archive/__init__.py b/big_vision/pp/archive/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/big_vision/pp/archive/autoaugment.py b/big_vision/pp/archive/autoaugment.py new file mode 100644 index 0000000000000000000000000000000000000000..f1af14ec6a1125ee9c3ea426f9224153483fd8e6 --- /dev/null +++ b/big_vision/pp/archive/autoaugment.py @@ -0,0 +1,700 @@ +# Copyright 2024 Big Vision 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. + +"""AutoAugment and RandAugment policies for enhanced image preprocessing. + +AutoAugment Reference: https://arxiv.org/abs/1805.09501 +RandAugment Reference: https://arxiv.org/abs/1909.13719 + +This code is forked from +https://github.com/tensorflow/tpu/blob/11d0db15cf1c3667f6e36fecffa111399e008acd/models/official/efficientnet/autoaugment.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import dataclasses +import inspect +import math +import tensorflow.compat.v1 as tf +from tensorflow_addons import image as contrib_image + +# This signifies the max integer that the controller RNN could predict for the +# augmentation scheme. +_MAX_LEVEL = 10. + + +@dataclasses.dataclass +class HParams: + """Parameters for AutoAugment and RandAugment.""" + cutout_const: int + translate_const: int + + +def policy_v0(): + """Autoaugment policy that was used in AutoAugment Paper.""" + # Each tuple is an augmentation operation of the form + # (operation, probability, magnitude). Each element in policy is a + # sub-policy that will be applied sequentially on the image. + policy = [ + [('Equalize', 0.8, 1), ('ShearY', 0.8, 4)], + [('Color', 0.4, 9), ('Equalize', 0.6, 3)], + [('Color', 0.4, 1), ('Rotate', 0.6, 8)], + [('Solarize', 0.8, 3), ('Equalize', 0.4, 7)], + [('Solarize', 0.4, 2), ('Solarize', 0.6, 2)], + [('Color', 0.2, 0), ('Equalize', 0.8, 8)], + [('Equalize', 0.4, 8), ('SolarizeAdd', 0.8, 3)], + [('ShearX', 0.2, 9), ('Rotate', 0.6, 8)], + [('Color', 0.6, 1), ('Equalize', 1.0, 2)], + [('Invert', 0.4, 9), ('Rotate', 0.6, 0)], + [('Equalize', 1.0, 9), ('ShearY', 0.6, 3)], + [('Color', 0.4, 7), ('Equalize', 0.6, 0)], + [('Posterize', 0.4, 6), ('AutoContrast', 0.4, 7)], + [('Solarize', 0.6, 8), ('Color', 0.6, 9)], + [('Solarize', 0.2, 4), ('Rotate', 0.8, 9)], + [('Rotate', 1.0, 7), ('TranslateY', 0.8, 9)], + [('ShearX', 0.0, 0), ('Solarize', 0.8, 4)], + [('ShearY', 0.8, 0), ('Color', 0.6, 4)], + [('Color', 1.0, 0), ('Rotate', 0.6, 2)], + [('Equalize', 0.8, 4), ('Equalize', 0.0, 8)], + [('Equalize', 1.0, 4), ('AutoContrast', 0.6, 2)], + [('ShearY', 0.4, 7), ('SolarizeAdd', 0.6, 7)], + [('Posterize', 0.8, 2), ('Solarize', 0.6, 10)], + [('Solarize', 0.6, 8), ('Equalize', 0.6, 1)], + [('Color', 0.8, 6), ('Rotate', 0.4, 5)], + ] + return policy + + +def policy_vtest(): + """Autoaugment test policy for debugging.""" + # Each tuple is an augmentation operation of the form + # (operation, probability, magnitude). Each element in policy is a + # sub-policy that will be applied sequentially on the image. + policy = [ + [('TranslateX', 1.0, 4), ('Equalize', 1.0, 10)], + ] + return policy + + +def blend(image1, image2, factor): + """Blend image1 and image2 using 'factor'. + Factor can be above 0.0. A value of 0.0 means only image1 is used. + A value of 1.0 means only image2 is used. A value between 0.0 and + 1.0 means we linearly interpolate the pixel values between the two + images. A value greater than 1.0 "extrapolates" the difference + between the two pixel values, and we clip the results to values + between 0 and 255. + Args: + image1: An image Tensor of type uint8. + image2: An image Tensor of type uint8. + factor: A floating point value above 0.0. + Returns: + A blended image Tensor of type uint8. + """ + if factor == 0.0: + return tf.convert_to_tensor(image1) + if factor == 1.0: + return tf.convert_to_tensor(image2) + + image1 = tf.to_float(image1) + image2 = tf.to_float(image2) + + difference = image2 - image1 + scaled = factor * difference + + # Do addition in float. + temp = tf.to_float(image1) + scaled + + # Interpolate + if factor > 0.0 and factor < 1.0: + # Interpolation means we always stay within 0 and 255. + return tf.cast(temp, tf.uint8) + + # Extrapolate: + # + # We need to clip and then cast. + return tf.cast(tf.clip_by_value(temp, 0.0, 255.0), tf.uint8) + + +def cutout(image, pad_size, replace=0): + """Apply cutout (https://arxiv.org/abs/1708.04552) to image. + This operation applies a (2*pad_size x 2*pad_size) mask of zeros to + a random location within `img`. The pixel values filled in will be of the + value `replace`. The located where the mask will be applied is randomly + chosen uniformly over the whole image. + Args: + image: An image Tensor of type uint8. + pad_size: Specifies how big the zero mask that will be generated is that + is applied to the image. The mask will be of size + (2*pad_size x 2*pad_size). + replace: What pixel value to fill in the image in the area that has + the cutout mask applied to it. + Returns: + An image Tensor that is of type uint8. + """ + image_height = tf.shape(image)[0] + image_width = tf.shape(image)[1] + + # Sample the center location in the image where the zero mask will be applied. + cutout_center_height = tf.random_uniform( + shape=[], minval=0, maxval=image_height, + dtype=tf.int32) + + cutout_center_width = tf.random_uniform( + shape=[], minval=0, maxval=image_width, + dtype=tf.int32) + + lower_pad = tf.maximum(0, cutout_center_height - pad_size) + upper_pad = tf.maximum(0, image_height - cutout_center_height - pad_size) + left_pad = tf.maximum(0, cutout_center_width - pad_size) + right_pad = tf.maximum(0, image_width - cutout_center_width - pad_size) + + cutout_shape = [image_height - (lower_pad + upper_pad), + image_width - (left_pad + right_pad)] + padding_dims = [[lower_pad, upper_pad], [left_pad, right_pad]] + mask = tf.pad( + tf.zeros(cutout_shape, dtype=image.dtype), + padding_dims, constant_values=1) + mask = tf.expand_dims(mask, -1) + mask = tf.tile(mask, [1, 1, 3]) + image = tf.where( + tf.equal(mask, 0), + tf.ones_like(image, dtype=image.dtype) * replace, + image) + return image + + +def solarize(image, threshold=128): + # For each pixel in the image, select the pixel + # if the value is less than the threshold. + # Otherwise, subtract 255 from the pixel. + return tf.where(image < threshold, image, 255 - image) + + +def solarize_add(image, addition=0, threshold=128): + # For each pixel in the image less than threshold + # we add 'addition' amount to it and then clip the + # pixel value to be between 0 and 255. The value + # of 'addition' is between -128 and 128. + added_image = tf.cast(image, tf.int64) + addition + added_image = tf.cast(tf.clip_by_value(added_image, 0, 255), tf.uint8) + return tf.where(image < threshold, added_image, image) + + +def color(image, factor): + """Equivalent of PIL Color.""" + degenerate = tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image)) + return blend(degenerate, image, factor) + + +def contrast(image, factor): + """Equivalent of PIL Contrast.""" + degenerate = tf.image.rgb_to_grayscale(image) + # Cast before calling tf.histogram. + degenerate = tf.cast(degenerate, tf.int32) + + # Compute the grayscale histogram, then compute the mean pixel value, + # and create a constant image size of that value. Use that as the + # blending degenerate target of the original image. + hist = tf.histogram_fixed_width(degenerate, [0, 255], nbins=256) + mean = tf.reduce_sum(tf.cast(hist, tf.float32)) / 256.0 + degenerate = tf.ones_like(degenerate, dtype=tf.float32) * mean + degenerate = tf.clip_by_value(degenerate, 0.0, 255.0) + degenerate = tf.image.grayscale_to_rgb(tf.cast(degenerate, tf.uint8)) + return blend(degenerate, image, factor) + + +def brightness(image, factor): + """Equivalent of PIL Brightness.""" + degenerate = tf.zeros_like(image) + return blend(degenerate, image, factor) + + +def posterize(image, bits): + """Equivalent of PIL Posterize.""" + shift = 8 - bits + return tf.bitwise.left_shift(tf.bitwise.right_shift(image, shift), shift) + + +def rotate(image, degrees, replace): + """Rotates the image by degrees either clockwise or counterclockwise. + Args: + image: An image Tensor of type uint8. + degrees: Float, a scalar angle in degrees to rotate all images by. If + degrees is positive the image will be rotated clockwise otherwise it will + be rotated counterclockwise. + replace: A one or three value 1D tensor to fill empty pixels caused by + the rotate operation. + Returns: + The rotated version of image. + """ + # Convert from degrees to radians. + degrees_to_radians = math.pi / 180.0 + radians = degrees * degrees_to_radians + + # In practice, we should randomize the rotation degrees by flipping + # it negatively half the time, but that's done on 'degrees' outside + # of the function. + image = contrib_image.rotate(wrap(image), radians) + return unwrap(image, replace) + + +def translate_x(image, pixels, replace): + """Equivalent of PIL Translate in X dimension.""" + image = contrib_image.translate(wrap(image), [-pixels, 0]) + return unwrap(image, replace) + + +def translate_y(image, pixels, replace): + """Equivalent of PIL Translate in Y dimension.""" + image = contrib_image.translate(wrap(image), [0, -pixels]) + return unwrap(image, replace) + + +def shear_x(image, level, replace): + """Equivalent of PIL Shearing in X dimension.""" + # Shear parallel to x axis is a projective transform + # with a matrix form of: + # [1 level + # 0 1]. + image = contrib_image.transform( + wrap(image), [1., level, 0., 0., 1., 0., 0., 0.]) + return unwrap(image, replace) + + +def shear_y(image, level, replace): + """Equivalent of PIL Shearing in Y dimension.""" + # Shear parallel to y axis is a projective transform + # with a matrix form of: + # [1 0 + # level 1]. + image = contrib_image.transform( + wrap(image), [1., 0., 0., level, 1., 0., 0., 0.]) + return unwrap(image, replace) + + +def autocontrast(image): + """Implements Autocontrast function from PIL using TF ops. + Args: + image: A 3D uint8 tensor. + Returns: + The image after it has had autocontrast applied to it and will be of type + uint8. + """ + + def scale_channel(image): + """Scale the 2D image using the autocontrast rule.""" + # A possibly cheaper version can be done using cumsum/unique_with_counts + # over the histogram values, rather than iterating over the entire image. + # to compute mins and maxes. + lo = tf.to_float(tf.reduce_min(image)) + hi = tf.to_float(tf.reduce_max(image)) + + # Scale the image, making the lowest value 0 and the highest value 255. + def scale_values(im): + scale = 255.0 / (hi - lo) + offset = -lo * scale + im = tf.to_float(im) * scale + offset + im = tf.clip_by_value(im, 0.0, 255.0) + return tf.cast(im, tf.uint8) + + result = tf.cond(hi > lo, lambda: scale_values(image), lambda: image) + return result + + # Assumes RGB for now. Scales each channel independently + # and then stacks the result. + s1 = scale_channel(image[:, :, 0]) + s2 = scale_channel(image[:, :, 1]) + s3 = scale_channel(image[:, :, 2]) + image = tf.stack([s1, s2, s3], 2) + return image + + +def sharpness(image, factor): + """Implements Sharpness function from PIL using TF ops.""" + orig_image = image + image = tf.cast(image, tf.float32) + # Make image 4D for conv operation. + image = tf.expand_dims(image, 0) + # SMOOTH PIL Kernel. + kernel = tf.constant( + [[1, 1, 1], [1, 5, 1], [1, 1, 1]], dtype=tf.float32, + shape=[3, 3, 1, 1]) / 13. + # Tile across channel dimension. + kernel = tf.tile(kernel, [1, 1, 3, 1]) + strides = [1, 1, 1, 1] + with tf.device('/cpu:0'): + # Some augmentation that uses depth-wise conv will cause crashing when + # training on GPU. See ((internal link)) for details. + degenerate = tf.nn.depthwise_conv2d( + image, kernel, strides, padding='VALID', rate=[1, 1]) + degenerate = tf.clip_by_value(degenerate, 0.0, 255.0) + degenerate = tf.squeeze(tf.cast(degenerate, tf.uint8), [0]) + + # For the borders of the resulting image, fill in the values of the + # original image. + mask = tf.ones_like(degenerate) + padded_mask = tf.pad(mask, [[1, 1], [1, 1], [0, 0]]) + padded_degenerate = tf.pad(degenerate, [[1, 1], [1, 1], [0, 0]]) + result = tf.where(tf.equal(padded_mask, 1), padded_degenerate, orig_image) + + # Blend the final result. + return blend(result, orig_image, factor) + + +def equalize(image): + """Implements Equalize function from PIL using TF ops.""" + def scale_channel(im, c): + """Scale the data in the channel to implement equalize.""" + im = tf.cast(im[:, :, c], tf.int32) + # Compute the histogram of the image channel. + histo = tf.histogram_fixed_width(im, [0, 255], nbins=256) + + # For the purposes of computing the step, filter out the nonzeros. + nonzero = tf.where(tf.not_equal(histo, 0)) + nonzero_histo = tf.reshape(tf.gather(histo, nonzero), [-1]) + step = (tf.reduce_sum(nonzero_histo) - nonzero_histo[-1]) // 255 + + def build_lut(histo, step): + # Compute the cumulative sum, shifting by step // 2 + # and then normalization by step. + lut = (tf.cumsum(histo) + (step // 2)) // step + # Shift lut, prepending with 0. + lut = tf.concat([[0], lut[:-1]], 0) + # Clip the counts to be in range. This is done + # in the C code for image.point. + return tf.clip_by_value(lut, 0, 255) + + # If step is zero, return the original image. Otherwise, build + # lut from the full histogram and step and then index from it. + result = tf.cond(tf.equal(step, 0), + lambda: im, + lambda: tf.gather(build_lut(histo, step), im)) + + return tf.cast(result, tf.uint8) + + # Assumes RGB for now. Scales each channel independently + # and then stacks the result. + s1 = scale_channel(image, 0) + s2 = scale_channel(image, 1) + s3 = scale_channel(image, 2) + image = tf.stack([s1, s2, s3], 2) + return image + + +def invert(image): + """Inverts the image pixels.""" + image = tf.convert_to_tensor(image) + return 255 - image + + +def wrap(image): + """Returns 'image' with an extra channel set to all 1s.""" + shape = tf.shape(image) + extended_channel = tf.ones([shape[0], shape[1], 1], image.dtype) + extended = tf.concat([image, extended_channel], 2) + return extended + + +def unwrap(image, replace): + """Unwraps an image produced by wrap. + Where there is a 0 in the last channel for every spatial position, + the rest of the three channels in that spatial dimension are grayed + (set to 128). Operations like translate and shear on a wrapped + Tensor will leave 0s in empty locations. Some transformations look + at the intensity of values to do preprocessing, and we want these + empty pixels to assume the 'average' value, rather than pure black. + Args: + image: A 3D Image Tensor with 4 channels. + replace: A one or three value 1D tensor to fill empty pixels. + Returns: + image: A 3D image Tensor with 3 channels. + """ + image_shape = tf.shape(image) + # Flatten the spatial dimensions. + flattened_image = tf.reshape(image, [-1, image_shape[2]]) + + # Find all pixels where the last channel is zero. + alpha_channel = flattened_image[:, 3] + + replace = tf.concat([replace, tf.ones([1], image.dtype)], 0) + + # Where they are zero, fill them in with 'replace'. + flattened_image = tf.where( + tf.equal(alpha_channel, 0), + tf.ones_like(flattened_image, dtype=image.dtype) * replace, + flattened_image) + + image = tf.reshape(flattened_image, image_shape) + image = tf.slice(image, [0, 0, 0], [image_shape[0], image_shape[1], 3]) + return image + + +NAME_TO_FUNC = { + 'AutoContrast': autocontrast, + 'Equalize': equalize, + 'Invert': invert, + 'Rotate': rotate, + 'Posterize': posterize, + 'Solarize': solarize, + 'SolarizeAdd': solarize_add, + 'Color': color, + 'Contrast': contrast, + 'Brightness': brightness, + 'Sharpness': sharpness, + 'ShearX': shear_x, + 'ShearY': shear_y, + 'TranslateX': translate_x, + 'TranslateY': translate_y, + 'Cutout': cutout, +} + + +def _randomly_negate_tensor(tensor): + """With 50% prob turn the tensor negative.""" + should_flip = tf.cast(tf.floor(tf.random_uniform([]) + 0.5), tf.bool) + final_tensor = tf.cond(should_flip, lambda: tensor, lambda: -tensor) + return final_tensor + + +def _rotate_level_to_arg(level): + level = (level/_MAX_LEVEL) * 30. + level = _randomly_negate_tensor(level) + return (level,) + + +def _shrink_level_to_arg(level): + """Converts level to ratio by which we shrink the image content.""" + if level == 0: + return (1.0,) # if level is zero, do not shrink the image + # Maximum shrinking ratio is 2.9. + level = 2. / (_MAX_LEVEL / level) + 0.9 + return (level,) + + +def _enhance_level_to_arg(level): + return ((level/_MAX_LEVEL) * 1.8 + 0.1,) + + +def _shear_level_to_arg(level): + level = (level/_MAX_LEVEL) * 0.3 + # Flip level to negative with 50% chance. + level = _randomly_negate_tensor(level) + return (level,) + + +def _translate_level_to_arg(level, translate_const): + level = (level/_MAX_LEVEL) * float(translate_const) + # Flip level to negative with 50% chance. + level = _randomly_negate_tensor(level) + return (level,) + + +def level_to_arg(hparams): + return { + 'AutoContrast': lambda level: (), + 'Equalize': lambda level: (), + 'Invert': lambda level: (), + 'Rotate': _rotate_level_to_arg, + 'Posterize': lambda level: (int((level/_MAX_LEVEL) * 4),), + 'Solarize': lambda level: (int((level/_MAX_LEVEL) * 256),), + 'SolarizeAdd': lambda level: (int((level/_MAX_LEVEL) * 110),), + 'Color': _enhance_level_to_arg, + 'Contrast': _enhance_level_to_arg, + 'Brightness': _enhance_level_to_arg, + 'Sharpness': _enhance_level_to_arg, + 'ShearX': _shear_level_to_arg, + 'ShearY': _shear_level_to_arg, + 'Cutout': lambda level: (int((level/_MAX_LEVEL) * hparams.cutout_const),), + 'TranslateX': lambda level: _translate_level_to_arg( + level, hparams.translate_const), + 'TranslateY': lambda level: _translate_level_to_arg( + level, hparams.translate_const), + # pylint:enable=g-long-lambda + } + + +def _parse_policy_info(name, prob, level, replace_value, augmentation_hparams): + """Return the function that corresponds to `name` and update `level` param.""" + func = NAME_TO_FUNC[name] + args = level_to_arg(augmentation_hparams)[name](level) + + # Check to see if prob is passed into function. This is used for operations + # where we alter bboxes independently. + # pytype:disable=wrong-arg-types + if 'prob' in inspect.getfullargspec(func).args: + args = tuple([prob] + list(args)) + # pytype:enable=wrong-arg-types + + # Add in replace arg if it is required for the function that is being called. + # pytype:disable=wrong-arg-types + if 'replace' in inspect.getfullargspec(func).args: + # Make sure replace is the final argument + assert 'replace' == inspect.getfullargspec(func).args[-1] + args = tuple(list(args) + [replace_value]) + # pytype:enable=wrong-arg-types + + return (func, prob, args) + + +def _apply_func_with_prob(func, image, args, prob): + """Apply `func` to image w/ `args` as input with probability `prob`.""" + assert isinstance(args, tuple) + + # If prob is a function argument, then this randomness is being handled + # inside the function, so make sure it is always called. + # pytype:disable=wrong-arg-types + if 'prob' in inspect.getfullargspec(func).args: + prob = 1.0 + # pytype:enable=wrong-arg-types + + # Apply the function with probability `prob`. + should_apply_op = tf.cast( + tf.floor(tf.random_uniform([], dtype=tf.float32) + prob), tf.bool) + augmented_image = tf.cond( + should_apply_op, + lambda: func(image, *args), + lambda: image) + return augmented_image + + +def select_and_apply_random_policy(policies, image): + """Select a random policy from `policies` and apply it to `image`.""" + policy_to_select = tf.random_uniform([], maxval=len(policies), dtype=tf.int32) + # Note that using tf.case instead of tf.conds would result in significantly + # larger graphs and would even break export for some larger policies. + for (i, policy) in enumerate(policies): + image = tf.cond( + tf.equal(i, policy_to_select), + lambda selected_policy=policy: selected_policy(image), + lambda: image) + return image + + +def build_and_apply_nas_policy(policies, image, + augmentation_hparams): + """Build a policy from the given policies passed in and apply to image. + Args: + policies: list of lists of tuples in the form `(func, prob, level)`, `func` + is a string name of the augmentation function, `prob` is the probability + of applying the `func` operation, `level` is the input argument for + `func`. + image: tf.Tensor that the resulting policy will be applied to. + augmentation_hparams: Hparams associated with the NAS learned policy. + Returns: + A version of image that now has data augmentation applied to it based on + the `policies` pass into the function. + """ + replace_value = [128, 128, 128] + + # func is the string name of the augmentation function, prob is the + # probability of applying the operation and level is the parameter associated + # with the tf op. + + # tf_policies are functions that take in an image and return an augmented + # image. + tf_policies = [] + for policy in policies: + tf_policy = [] + # Link string name to the correct python function and make sure the correct + # argument is passed into that function. + for policy_info in policy: + policy_info = list(policy_info) + [replace_value, augmentation_hparams] + + tf_policy.append(_parse_policy_info(*policy_info)) + # Now build the tf policy that will apply the augmentation procedue + # on image. + def make_final_policy(tf_policy_): + def final_policy(image_): + for func, prob, args in tf_policy_: + image_ = _apply_func_with_prob( + func, image_, args, prob) + return image_ + return final_policy + tf_policies.append(make_final_policy(tf_policy)) + + augmented_image = select_and_apply_random_policy( + tf_policies, image) + return augmented_image + + +def distort_image_with_autoaugment(image, augmentation_name): + """Applies the AutoAugment policy to `image`. + AutoAugment is from the paper: https://arxiv.org/abs/1805.09501. + Args: + image: `Tensor` of shape [height, width, 3] representing an image. + augmentation_name: The name of the AutoAugment policy to use. The available + options are `v0` and `test`. `v0` is the policy used for + all of the results in the paper and was found to achieve the best results + on the COCO dataset. `v1`, `v2` and `v3` are additional good policies + found on the COCO dataset that have slight variation in what operations + were used during the search procedure along with how many operations are + applied in parallel to a single image (2 vs 3). + Returns: + A tuple containing the augmented versions of `image`. + """ + available_policies = {'v0': policy_v0, + 'test': policy_vtest} + if augmentation_name not in available_policies: + raise ValueError('Invalid augmentation_name: {}'.format(augmentation_name)) + + policy = available_policies[augmentation_name]() + # Hparams that will be used for AutoAugment. + augmentation_hparams = HParams( + cutout_const=100, translate_const=250) + + return build_and_apply_nas_policy(policy, image, augmentation_hparams) + + +def distort_image_with_randaugment(image, num_layers, magnitude): + """Applies the RandAugment policy to `image`. + RandAugment is from the paper https://arxiv.org/abs/1909.13719, + Args: + image: `Tensor` of shape [height, width, 3] representing an image. + num_layers: Integer, the number of augmentation transformations to apply + sequentially to an image. Represented as (N) in the paper. Usually best + values will be in the range [1, 3]. + magnitude: Integer, shared magnitude across all augmentation operations. + Represented as (M) in the paper. Usually best values are in the range + [5, 30]. + Returns: + The augmented version of `image`. + """ + replace_value = [128] * 3 + tf.logging.info('Using RandAug.') + augmentation_hparams = HParams( + cutout_const=40, translate_const=100) + available_ops = [ + 'AutoContrast', 'Equalize', 'Invert', 'Rotate', 'Posterize', + 'Solarize', 'Color', 'Contrast', 'Brightness', 'Sharpness', + 'ShearX', 'ShearY', 'TranslateX', 'TranslateY', 'Cutout', 'SolarizeAdd'] + + for layer_num in range(num_layers): + op_to_select = tf.random_uniform( + [], maxval=len(available_ops), dtype=tf.int32) + random_magnitude = float(magnitude) + with tf.name_scope('randaug_layer_{}'.format(layer_num)): + for (i, op_name) in enumerate(available_ops): + prob = tf.random_uniform([], minval=0.2, maxval=0.8, dtype=tf.float32) + func, _, args = _parse_policy_info(op_name, prob, random_magnitude, + replace_value, augmentation_hparams) + image = tf.cond( + tf.equal(i, op_to_select), + lambda selected_func=func, selected_args=args: selected_func( + image, *selected_args), + # pylint:enable=g-long-lambda + lambda: image) + return image diff --git a/big_vision/pp/archive/randaug.py b/big_vision/pp/archive/randaug.py new file mode 100644 index 0000000000000000000000000000000000000000..e8acec830237ce8840a75e44788d1a425ec17289 --- /dev/null +++ b/big_vision/pp/archive/randaug.py @@ -0,0 +1,46 @@ +# Copyright 2024 Big Vision 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. + +"""RandAug depends on deprecated tfa.image package, now defunct.""" + +from big_vision.pp import registry +from big_vision.pp import utils +from big_vision.pp.archive import autoaugment + + +@registry.Registry.register("preprocess_ops.randaug") +@utils.InKeyOutKey() +def get_randaug(num_layers: int = 2, magnitude: int = 10): + """Creates a function that applies RandAugment. + + RandAugment is from the paper https://arxiv.org/abs/1909.13719, + + Args: + num_layers: Integer, the number of augmentation transformations to apply + sequentially to an image. Represented as (N) in the paper. Usually best + values will be in the range [1, 3]. + magnitude: Integer, shared magnitude across all augmentation operations. + Represented as (M) in the paper. Usually best values are in the range [5, + 30]. + + Returns: + a function that applies RandAugment. + """ + + def _randaug(image): + return autoaugment.distort_image_with_randaugment( + image, num_layers, magnitude + ) + + return _randaug diff --git a/big_vision/pp/autoaugment.py b/big_vision/pp/autoaugment.py new file mode 100644 index 0000000000000000000000000000000000000000..6cc45f14e5d8c49cb54c649104851e0729ebb180 --- /dev/null +++ b/big_vision/pp/autoaugment.py @@ -0,0 +1,700 @@ +# Copyright 2023 Big Vision 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. + +"""AutoAugment and RandAugment policies for enhanced image preprocessing. + +AutoAugment Reference: https://arxiv.org/abs/1805.09501 +RandAugment Reference: https://arxiv.org/abs/1909.13719 + +This code is forked from +https://github.com/tensorflow/tpu/blob/11d0db15cf1c3667f6e36fecffa111399e008acd/models/official/efficientnet/autoaugment.py +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import dataclasses +import inspect +import math +import tensorflow.compat.v1 as tf +from tensorflow_addons import image as contrib_image + +# This signifies the max integer that the controller RNN could predict for the +# augmentation scheme. +_MAX_LEVEL = 10. + + +@dataclasses.dataclass +class HParams: + """Parameters for AutoAugment and RandAugment.""" + cutout_const: int + translate_const: int + + +def policy_v0(): + """Autoaugment policy that was used in AutoAugment Paper.""" + # Each tuple is an augmentation operation of the form + # (operation, probability, magnitude). Each element in policy is a + # sub-policy that will be applied sequentially on the image. + policy = [ + [('Equalize', 0.8, 1), ('ShearY', 0.8, 4)], + [('Color', 0.4, 9), ('Equalize', 0.6, 3)], + [('Color', 0.4, 1), ('Rotate', 0.6, 8)], + [('Solarize', 0.8, 3), ('Equalize', 0.4, 7)], + [('Solarize', 0.4, 2), ('Solarize', 0.6, 2)], + [('Color', 0.2, 0), ('Equalize', 0.8, 8)], + [('Equalize', 0.4, 8), ('SolarizeAdd', 0.8, 3)], + [('ShearX', 0.2, 9), ('Rotate', 0.6, 8)], + [('Color', 0.6, 1), ('Equalize', 1.0, 2)], + [('Invert', 0.4, 9), ('Rotate', 0.6, 0)], + [('Equalize', 1.0, 9), ('ShearY', 0.6, 3)], + [('Color', 0.4, 7), ('Equalize', 0.6, 0)], + [('Posterize', 0.4, 6), ('AutoContrast', 0.4, 7)], + [('Solarize', 0.6, 8), ('Color', 0.6, 9)], + [('Solarize', 0.2, 4), ('Rotate', 0.8, 9)], + [('Rotate', 1.0, 7), ('TranslateY', 0.8, 9)], + [('ShearX', 0.0, 0), ('Solarize', 0.8, 4)], + [('ShearY', 0.8, 0), ('Color', 0.6, 4)], + [('Color', 1.0, 0), ('Rotate', 0.6, 2)], + [('Equalize', 0.8, 4), ('Equalize', 0.0, 8)], + [('Equalize', 1.0, 4), ('AutoContrast', 0.6, 2)], + [('ShearY', 0.4, 7), ('SolarizeAdd', 0.6, 7)], + [('Posterize', 0.8, 2), ('Solarize', 0.6, 10)], + [('Solarize', 0.6, 8), ('Equalize', 0.6, 1)], + [('Color', 0.8, 6), ('Rotate', 0.4, 5)], + ] + return policy + + +def policy_vtest(): + """Autoaugment test policy for debugging.""" + # Each tuple is an augmentation operation of the form + # (operation, probability, magnitude). Each element in policy is a + # sub-policy that will be applied sequentially on the image. + policy = [ + [('TranslateX', 1.0, 4), ('Equalize', 1.0, 10)], + ] + return policy + + +def blend(image1, image2, factor): + """Blend image1 and image2 using 'factor'. + Factor can be above 0.0. A value of 0.0 means only image1 is used. + A value of 1.0 means only image2 is used. A value between 0.0 and + 1.0 means we linearly interpolate the pixel values between the two + images. A value greater than 1.0 "extrapolates" the difference + between the two pixel values, and we clip the results to values + between 0 and 255. + Args: + image1: An image Tensor of type uint8. + image2: An image Tensor of type uint8. + factor: A floating point value above 0.0. + Returns: + A blended image Tensor of type uint8. + """ + if factor == 0.0: + return tf.convert_to_tensor(image1) + if factor == 1.0: + return tf.convert_to_tensor(image2) + + image1 = tf.to_float(image1) + image2 = tf.to_float(image2) + + difference = image2 - image1 + scaled = factor * difference + + # Do addition in float. + temp = tf.to_float(image1) + scaled + + # Interpolate + if factor > 0.0 and factor < 1.0: + # Interpolation means we always stay within 0 and 255. + return tf.cast(temp, tf.uint8) + + # Extrapolate: + # + # We need to clip and then cast. + return tf.cast(tf.clip_by_value(temp, 0.0, 255.0), tf.uint8) + + +def cutout(image, pad_size, replace=0): + """Apply cutout (https://arxiv.org/abs/1708.04552) to image. + This operation applies a (2*pad_size x 2*pad_size) mask of zeros to + a random location within `img`. The pixel values filled in will be of the + value `replace`. The located where the mask will be applied is randomly + chosen uniformly over the whole image. + Args: + image: An image Tensor of type uint8. + pad_size: Specifies how big the zero mask that will be generated is that + is applied to the image. The mask will be of size + (2*pad_size x 2*pad_size). + replace: What pixel value to fill in the image in the area that has + the cutout mask applied to it. + Returns: + An image Tensor that is of type uint8. + """ + image_height = tf.shape(image)[0] + image_width = tf.shape(image)[1] + + # Sample the center location in the image where the zero mask will be applied. + cutout_center_height = tf.random_uniform( + shape=[], minval=0, maxval=image_height, + dtype=tf.int32) + + cutout_center_width = tf.random_uniform( + shape=[], minval=0, maxval=image_width, + dtype=tf.int32) + + lower_pad = tf.maximum(0, cutout_center_height - pad_size) + upper_pad = tf.maximum(0, image_height - cutout_center_height - pad_size) + left_pad = tf.maximum(0, cutout_center_width - pad_size) + right_pad = tf.maximum(0, image_width - cutout_center_width - pad_size) + + cutout_shape = [image_height - (lower_pad + upper_pad), + image_width - (left_pad + right_pad)] + padding_dims = [[lower_pad, upper_pad], [left_pad, right_pad]] + mask = tf.pad( + tf.zeros(cutout_shape, dtype=image.dtype), + padding_dims, constant_values=1) + mask = tf.expand_dims(mask, -1) + mask = tf.tile(mask, [1, 1, 3]) + image = tf.where( + tf.equal(mask, 0), + tf.ones_like(image, dtype=image.dtype) * replace, + image) + return image + + +def solarize(image, threshold=128): + # For each pixel in the image, select the pixel + # if the value is less than the threshold. + # Otherwise, subtract 255 from the pixel. + return tf.where(image < threshold, image, 255 - image) + + +def solarize_add(image, addition=0, threshold=128): + # For each pixel in the image less than threshold + # we add 'addition' amount to it and then clip the + # pixel value to be between 0 and 255. The value + # of 'addition' is between -128 and 128. + added_image = tf.cast(image, tf.int64) + addition + added_image = tf.cast(tf.clip_by_value(added_image, 0, 255), tf.uint8) + return tf.where(image < threshold, added_image, image) + + +def color(image, factor): + """Equivalent of PIL Color.""" + degenerate = tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image)) + return blend(degenerate, image, factor) + + +def contrast(image, factor): + """Equivalent of PIL Contrast.""" + degenerate = tf.image.rgb_to_grayscale(image) + # Cast before calling tf.histogram. + degenerate = tf.cast(degenerate, tf.int32) + + # Compute the grayscale histogram, then compute the mean pixel value, + # and create a constant image size of that value. Use that as the + # blending degenerate target of the original image. + hist = tf.histogram_fixed_width(degenerate, [0, 255], nbins=256) + mean = tf.reduce_sum(tf.cast(hist, tf.float32)) / 256.0 + degenerate = tf.ones_like(degenerate, dtype=tf.float32) * mean + degenerate = tf.clip_by_value(degenerate, 0.0, 255.0) + degenerate = tf.image.grayscale_to_rgb(tf.cast(degenerate, tf.uint8)) + return blend(degenerate, image, factor) + + +def brightness(image, factor): + """Equivalent of PIL Brightness.""" + degenerate = tf.zeros_like(image) + return blend(degenerate, image, factor) + + +def posterize(image, bits): + """Equivalent of PIL Posterize.""" + shift = 8 - bits + return tf.bitwise.left_shift(tf.bitwise.right_shift(image, shift), shift) + + +def rotate(image, degrees, replace): + """Rotates the image by degrees either clockwise or counterclockwise. + Args: + image: An image Tensor of type uint8. + degrees: Float, a scalar angle in degrees to rotate all images by. If + degrees is positive the image will be rotated clockwise otherwise it will + be rotated counterclockwise. + replace: A one or three value 1D tensor to fill empty pixels caused by + the rotate operation. + Returns: + The rotated version of image. + """ + # Convert from degrees to radians. + degrees_to_radians = math.pi / 180.0 + radians = degrees * degrees_to_radians + + # In practice, we should randomize the rotation degrees by flipping + # it negatively half the time, but that's done on 'degrees' outside + # of the function. + image = contrib_image.rotate(wrap(image), radians) + return unwrap(image, replace) + + +def translate_x(image, pixels, replace): + """Equivalent of PIL Translate in X dimension.""" + image = contrib_image.translate(wrap(image), [-pixels, 0]) + return unwrap(image, replace) + + +def translate_y(image, pixels, replace): + """Equivalent of PIL Translate in Y dimension.""" + image = contrib_image.translate(wrap(image), [0, -pixels]) + return unwrap(image, replace) + + +def shear_x(image, level, replace): + """Equivalent of PIL Shearing in X dimension.""" + # Shear parallel to x axis is a projective transform + # with a matrix form of: + # [1 level + # 0 1]. + image = contrib_image.transform( + wrap(image), [1., level, 0., 0., 1., 0., 0., 0.]) + return unwrap(image, replace) + + +def shear_y(image, level, replace): + """Equivalent of PIL Shearing in Y dimension.""" + # Shear parallel to y axis is a projective transform + # with a matrix form of: + # [1 0 + # level 1]. + image = contrib_image.transform( + wrap(image), [1., 0., 0., level, 1., 0., 0., 0.]) + return unwrap(image, replace) + + +def autocontrast(image): + """Implements Autocontrast function from PIL using TF ops. + Args: + image: A 3D uint8 tensor. + Returns: + The image after it has had autocontrast applied to it and will be of type + uint8. + """ + + def scale_channel(image): + """Scale the 2D image using the autocontrast rule.""" + # A possibly cheaper version can be done using cumsum/unique_with_counts + # over the histogram values, rather than iterating over the entire image. + # to compute mins and maxes. + lo = tf.to_float(tf.reduce_min(image)) + hi = tf.to_float(tf.reduce_max(image)) + + # Scale the image, making the lowest value 0 and the highest value 255. + def scale_values(im): + scale = 255.0 / (hi - lo) + offset = -lo * scale + im = tf.to_float(im) * scale + offset + im = tf.clip_by_value(im, 0.0, 255.0) + return tf.cast(im, tf.uint8) + + result = tf.cond(hi > lo, lambda: scale_values(image), lambda: image) + return result + + # Assumes RGB for now. Scales each channel independently + # and then stacks the result. + s1 = scale_channel(image[:, :, 0]) + s2 = scale_channel(image[:, :, 1]) + s3 = scale_channel(image[:, :, 2]) + image = tf.stack([s1, s2, s3], 2) + return image + + +def sharpness(image, factor): + """Implements Sharpness function from PIL using TF ops.""" + orig_image = image + image = tf.cast(image, tf.float32) + # Make image 4D for conv operation. + image = tf.expand_dims(image, 0) + # SMOOTH PIL Kernel. + kernel = tf.constant( + [[1, 1, 1], [1, 5, 1], [1, 1, 1]], dtype=tf.float32, + shape=[3, 3, 1, 1]) / 13. + # Tile across channel dimension. + kernel = tf.tile(kernel, [1, 1, 3, 1]) + strides = [1, 1, 1, 1] + with tf.device('/cpu:0'): + # Some augmentation that uses depth-wise conv will cause crashing when + # training on GPU. See ((internal link)) for details. + degenerate = tf.nn.depthwise_conv2d( + image, kernel, strides, padding='VALID', rate=[1, 1]) + degenerate = tf.clip_by_value(degenerate, 0.0, 255.0) + degenerate = tf.squeeze(tf.cast(degenerate, tf.uint8), [0]) + + # For the borders of the resulting image, fill in the values of the + # original image. + mask = tf.ones_like(degenerate) + padded_mask = tf.pad(mask, [[1, 1], [1, 1], [0, 0]]) + padded_degenerate = tf.pad(degenerate, [[1, 1], [1, 1], [0, 0]]) + result = tf.where(tf.equal(padded_mask, 1), padded_degenerate, orig_image) + + # Blend the final result. + return blend(result, orig_image, factor) + + +def equalize(image): + """Implements Equalize function from PIL using TF ops.""" + def scale_channel(im, c): + """Scale the data in the channel to implement equalize.""" + im = tf.cast(im[:, :, c], tf.int32) + # Compute the histogram of the image channel. + histo = tf.histogram_fixed_width(im, [0, 255], nbins=256) + + # For the purposes of computing the step, filter out the nonzeros. + nonzero = tf.where(tf.not_equal(histo, 0)) + nonzero_histo = tf.reshape(tf.gather(histo, nonzero), [-1]) + step = (tf.reduce_sum(nonzero_histo) - nonzero_histo[-1]) // 255 + + def build_lut(histo, step): + # Compute the cumulative sum, shifting by step // 2 + # and then normalization by step. + lut = (tf.cumsum(histo) + (step // 2)) // step + # Shift lut, prepending with 0. + lut = tf.concat([[0], lut[:-1]], 0) + # Clip the counts to be in range. This is done + # in the C code for image.point. + return tf.clip_by_value(lut, 0, 255) + + # If step is zero, return the original image. Otherwise, build + # lut from the full histogram and step and then index from it. + result = tf.cond(tf.equal(step, 0), + lambda: im, + lambda: tf.gather(build_lut(histo, step), im)) + + return tf.cast(result, tf.uint8) + + # Assumes RGB for now. Scales each channel independently + # and then stacks the result. + s1 = scale_channel(image, 0) + s2 = scale_channel(image, 1) + s3 = scale_channel(image, 2) + image = tf.stack([s1, s2, s3], 2) + return image + + +def invert(image): + """Inverts the image pixels.""" + image = tf.convert_to_tensor(image) + return 255 - image + + +def wrap(image): + """Returns 'image' with an extra channel set to all 1s.""" + shape = tf.shape(image) + extended_channel = tf.ones([shape[0], shape[1], 1], image.dtype) + extended = tf.concat([image, extended_channel], 2) + return extended + + +def unwrap(image, replace): + """Unwraps an image produced by wrap. + Where there is a 0 in the last channel for every spatial position, + the rest of the three channels in that spatial dimension are grayed + (set to 128). Operations like translate and shear on a wrapped + Tensor will leave 0s in empty locations. Some transformations look + at the intensity of values to do preprocessing, and we want these + empty pixels to assume the 'average' value, rather than pure black. + Args: + image: A 3D Image Tensor with 4 channels. + replace: A one or three value 1D tensor to fill empty pixels. + Returns: + image: A 3D image Tensor with 3 channels. + """ + image_shape = tf.shape(image) + # Flatten the spatial dimensions. + flattened_image = tf.reshape(image, [-1, image_shape[2]]) + + # Find all pixels where the last channel is zero. + alpha_channel = flattened_image[:, 3] + + replace = tf.concat([replace, tf.ones([1], image.dtype)], 0) + + # Where they are zero, fill them in with 'replace'. + flattened_image = tf.where( + tf.equal(alpha_channel, 0), + tf.ones_like(flattened_image, dtype=image.dtype) * replace, + flattened_image) + + image = tf.reshape(flattened_image, image_shape) + image = tf.slice(image, [0, 0, 0], [image_shape[0], image_shape[1], 3]) + return image + + +NAME_TO_FUNC = { + 'AutoContrast': autocontrast, + 'Equalize': equalize, + 'Invert': invert, + 'Rotate': rotate, + 'Posterize': posterize, + 'Solarize': solarize, + 'SolarizeAdd': solarize_add, + 'Color': color, + 'Contrast': contrast, + 'Brightness': brightness, + 'Sharpness': sharpness, + 'ShearX': shear_x, + 'ShearY': shear_y, + 'TranslateX': translate_x, + 'TranslateY': translate_y, + 'Cutout': cutout, +} + + +def _randomly_negate_tensor(tensor): + """With 50% prob turn the tensor negative.""" + should_flip = tf.cast(tf.floor(tf.random_uniform([]) + 0.5), tf.bool) + final_tensor = tf.cond(should_flip, lambda: tensor, lambda: -tensor) + return final_tensor + + +def _rotate_level_to_arg(level): + level = (level/_MAX_LEVEL) * 30. + level = _randomly_negate_tensor(level) + return (level,) + + +def _shrink_level_to_arg(level): + """Converts level to ratio by which we shrink the image content.""" + if level == 0: + return (1.0,) # if level is zero, do not shrink the image + # Maximum shrinking ratio is 2.9. + level = 2. / (_MAX_LEVEL / level) + 0.9 + return (level,) + + +def _enhance_level_to_arg(level): + return ((level/_MAX_LEVEL) * 1.8 + 0.1,) + + +def _shear_level_to_arg(level): + level = (level/_MAX_LEVEL) * 0.3 + # Flip level to negative with 50% chance. + level = _randomly_negate_tensor(level) + return (level,) + + +def _translate_level_to_arg(level, translate_const): + level = (level/_MAX_LEVEL) * float(translate_const) + # Flip level to negative with 50% chance. + level = _randomly_negate_tensor(level) + return (level,) + + +def level_to_arg(hparams): + return { + 'AutoContrast': lambda level: (), + 'Equalize': lambda level: (), + 'Invert': lambda level: (), + 'Rotate': _rotate_level_to_arg, + 'Posterize': lambda level: (int((level/_MAX_LEVEL) * 4),), + 'Solarize': lambda level: (int((level/_MAX_LEVEL) * 256),), + 'SolarizeAdd': lambda level: (int((level/_MAX_LEVEL) * 110),), + 'Color': _enhance_level_to_arg, + 'Contrast': _enhance_level_to_arg, + 'Brightness': _enhance_level_to_arg, + 'Sharpness': _enhance_level_to_arg, + 'ShearX': _shear_level_to_arg, + 'ShearY': _shear_level_to_arg, + 'Cutout': lambda level: (int((level/_MAX_LEVEL) * hparams.cutout_const),), + 'TranslateX': lambda level: _translate_level_to_arg( + level, hparams.translate_const), + 'TranslateY': lambda level: _translate_level_to_arg( + level, hparams.translate_const), + # pylint:enable=g-long-lambda + } + + +def _parse_policy_info(name, prob, level, replace_value, augmentation_hparams): + """Return the function that corresponds to `name` and update `level` param.""" + func = NAME_TO_FUNC[name] + args = level_to_arg(augmentation_hparams)[name](level) + + # Check to see if prob is passed into function. This is used for operations + # where we alter bboxes independently. + # pytype:disable=wrong-arg-types + if 'prob' in inspect.getfullargspec(func).args: + args = tuple([prob] + list(args)) + # pytype:enable=wrong-arg-types + + # Add in replace arg if it is required for the function that is being called. + # pytype:disable=wrong-arg-types + if 'replace' in inspect.getfullargspec(func).args: + # Make sure replace is the final argument + assert 'replace' == inspect.getfullargspec(func).args[-1] + args = tuple(list(args) + [replace_value]) + # pytype:enable=wrong-arg-types + + return (func, prob, args) + + +def _apply_func_with_prob(func, image, args, prob): + """Apply `func` to image w/ `args` as input with probability `prob`.""" + assert isinstance(args, tuple) + + # If prob is a function argument, then this randomness is being handled + # inside the function, so make sure it is always called. + # pytype:disable=wrong-arg-types + if 'prob' in inspect.getfullargspec(func).args: + prob = 1.0 + # pytype:enable=wrong-arg-types + + # Apply the function with probability `prob`. + should_apply_op = tf.cast( + tf.floor(tf.random_uniform([], dtype=tf.float32) + prob), tf.bool) + augmented_image = tf.cond( + should_apply_op, + lambda: func(image, *args), + lambda: image) + return augmented_image + + +def select_and_apply_random_policy(policies, image): + """Select a random policy from `policies` and apply it to `image`.""" + policy_to_select = tf.random_uniform([], maxval=len(policies), dtype=tf.int32) + # Note that using tf.case instead of tf.conds would result in significantly + # larger graphs and would even break export for some larger policies. + for (i, policy) in enumerate(policies): + image = tf.cond( + tf.equal(i, policy_to_select), + lambda selected_policy=policy: selected_policy(image), + lambda: image) + return image + + +def build_and_apply_nas_policy(policies, image, + augmentation_hparams): + """Build a policy from the given policies passed in and apply to image. + Args: + policies: list of lists of tuples in the form `(func, prob, level)`, `func` + is a string name of the augmentation function, `prob` is the probability + of applying the `func` operation, `level` is the input argument for + `func`. + image: tf.Tensor that the resulting policy will be applied to. + augmentation_hparams: Hparams associated with the NAS learned policy. + Returns: + A version of image that now has data augmentation applied to it based on + the `policies` pass into the function. + """ + replace_value = [128, 128, 128] + + # func is the string name of the augmentation function, prob is the + # probability of applying the operation and level is the parameter associated + # with the tf op. + + # tf_policies are functions that take in an image and return an augmented + # image. + tf_policies = [] + for policy in policies: + tf_policy = [] + # Link string name to the correct python function and make sure the correct + # argument is passed into that function. + for policy_info in policy: + policy_info = list(policy_info) + [replace_value, augmentation_hparams] + + tf_policy.append(_parse_policy_info(*policy_info)) + # Now build the tf policy that will apply the augmentation procedue + # on image. + def make_final_policy(tf_policy_): + def final_policy(image_): + for func, prob, args in tf_policy_: + image_ = _apply_func_with_prob( + func, image_, args, prob) + return image_ + return final_policy + tf_policies.append(make_final_policy(tf_policy)) + + augmented_image = select_and_apply_random_policy( + tf_policies, image) + return augmented_image + + +def distort_image_with_autoaugment(image, augmentation_name): + """Applies the AutoAugment policy to `image`. + AutoAugment is from the paper: https://arxiv.org/abs/1805.09501. + Args: + image: `Tensor` of shape [height, width, 3] representing an image. + augmentation_name: The name of the AutoAugment policy to use. The available + options are `v0` and `test`. `v0` is the policy used for + all of the results in the paper and was found to achieve the best results + on the COCO dataset. `v1`, `v2` and `v3` are additional good policies + found on the COCO dataset that have slight variation in what operations + were used during the search procedure along with how many operations are + applied in parallel to a single image (2 vs 3). + Returns: + A tuple containing the augmented versions of `image`. + """ + available_policies = {'v0': policy_v0, + 'test': policy_vtest} + if augmentation_name not in available_policies: + raise ValueError('Invalid augmentation_name: {}'.format(augmentation_name)) + + policy = available_policies[augmentation_name]() + # Hparams that will be used for AutoAugment. + augmentation_hparams = HParams( + cutout_const=100, translate_const=250) + + return build_and_apply_nas_policy(policy, image, augmentation_hparams) + + +def distort_image_with_randaugment(image, num_layers, magnitude): + """Applies the RandAugment policy to `image`. + RandAugment is from the paper https://arxiv.org/abs/1909.13719, + Args: + image: `Tensor` of shape [height, width, 3] representing an image. + num_layers: Integer, the number of augmentation transformations to apply + sequentially to an image. Represented as (N) in the paper. Usually best + values will be in the range [1, 3]. + magnitude: Integer, shared magnitude across all augmentation operations. + Represented as (M) in the paper. Usually best values are in the range + [5, 30]. + Returns: + The augmented version of `image`. + """ + replace_value = [128] * 3 + tf.logging.info('Using RandAug.') + augmentation_hparams = HParams( + cutout_const=40, translate_const=100) + available_ops = [ + 'AutoContrast', 'Equalize', 'Invert', 'Rotate', 'Posterize', + 'Solarize', 'Color', 'Contrast', 'Brightness', 'Sharpness', + 'ShearX', 'ShearY', 'TranslateX', 'TranslateY', 'Cutout', 'SolarizeAdd'] + + for layer_num in range(num_layers): + op_to_select = tf.random_uniform( + [], maxval=len(available_ops), dtype=tf.int32) + random_magnitude = float(magnitude) + with tf.name_scope('randaug_layer_{}'.format(layer_num)): + for (i, op_name) in enumerate(available_ops): + prob = tf.random_uniform([], minval=0.2, maxval=0.8, dtype=tf.float32) + func, _, args = _parse_policy_info(op_name, prob, random_magnitude, + replace_value, augmentation_hparams) + image = tf.cond( + tf.equal(i, op_to_select), + lambda selected_func=func, selected_args=args: selected_func( + image, *selected_args), + # pylint:enable=g-long-lambda + lambda: image) + return image diff --git a/big_vision/pp/builder.py b/big_vision/pp/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..c612ddbbdd5fdbf831984ed9df39a9a7cfbe5d55 --- /dev/null +++ b/big_vision/pp/builder.py @@ -0,0 +1,81 @@ +# Copyright 2024 Big Vision 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. + +"""Preprocessing builder.""" + +from absl import logging +from big_vision.pp import registry +import tensorflow as tf + + +def get_preprocess_fn(pp_pipeline, log_data=True): + """Transform an input string into the preprocessing function. + + The minilanguage is as follows: + + fn1|fn2(arg, arg2,...)|... + + And describes the successive application of the various `fn`s to the input, + where each function can optionally have one or more arguments, which are + either positional or key/value, as dictated by the `fn`. + + The output preprocessing function expects a dictionary as input. This + dictionary should have a key "image" that corresponds to a 3D tensor + (height x width x channel). + + Args: + pp_pipeline: A string describing the pre-processing pipeline. If empty or + None, no preprocessing will be executed. + log_data: Whether to log the data before and after preprocessing. Can also + be a string to show in the log for debugging, for example dataset name. + + Returns: + preprocessing function. + + Raises: + ValueError: if preprocessing function name is unknown + """ + + names, ops = [], [] + if pp_pipeline: + for op_spec in pp_pipeline.split("|"): + if not op_spec: continue # Skip empty section instead of error. + try: + ops.append(registry.Registry.lookup(f"preprocess_ops.{op_spec}")()) + names.append(registry.parse_name(op_spec)[0]) + except SyntaxError as err: + raise ValueError(f"Syntax error on: {op_spec}") from err + + def _preprocess_fn(data): + """The preprocessing function that is returned.""" + nonlocal log_data + + # Apply all the individual steps in sequence. + if log_data: + logging.info("Data before pre-processing (%s):\n%s", log_data, data) + for name, op in zip(names, ops): + with tf.name_scope(name): + data = op(data) + + # Validate input + if not isinstance(data, dict): + raise ValueError("Argument `data` must be a dictionary, " + "not %s" % str(type(data))) + + if log_data: + logging.info("Data after pre-processing (%s):\n%s", log_data, data) + log_data = False # For eager&pygrain: only log first one of each pipeline. + return data + + return _preprocess_fn diff --git a/big_vision/pp/builder_test.py b/big_vision/pp/builder_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f3a75cc05417a7f3ace70f97583d2e7b1ae4c432 --- /dev/null +++ b/big_vision/pp/builder_test.py @@ -0,0 +1,72 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for builder.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from big_vision.pp import builder +from big_vision.pp import ops_general # pylint: disable=unused-import +from big_vision.pp import ops_image # pylint: disable=unused-import +import numpy as np +import tensorflow.compat.v1 as tf + + +class BuilderTest(tf.test.TestCase): + + def testSingle(self): + pp_fn = builder.get_preprocess_fn("resize(256)") + x = np.random.randint(0, 256, [640, 480, 3]) + image = pp_fn({"image": x})["image"] + self.assertEqual(image.numpy().shape, (256, 256, 3)) + + def testEmpty(self): + pp_fn = builder.get_preprocess_fn("||inception_crop|||resize(256)||") + + # Typical image input + x = np.random.randint(0, 256, [640, 480, 3]) + image = pp_fn({"image": x})["image"] + self.assertEqual(image.numpy().shape, (256, 256, 3)) + + def testPreprocessingPipeline(self): + pp_str = ("inception_crop|resize(256)|resize((256, 256))|" + "central_crop((80, 120))|flip_lr|value_range(0,1)|" + "value_range(-1,1)") + pp_fn = builder.get_preprocess_fn(pp_str) + + # Typical image input + x = np.random.randint(0, 256, [640, 480, 3]) + image = pp_fn({"image": x})["image"] + self.assertEqual(image.numpy().shape, (80, 120, 3)) + self.assertLessEqual(np.max(image.numpy()), 1) + self.assertGreaterEqual(np.min(image.numpy()), -1) + + def testNumArgsException(self): + + x = np.random.randint(0, 256, [640, 480, 3]) + for pp_str in [ + "inception_crop(1)", + "resize()", + "resize(1, 1, 1)" + "flip_lr(1)", + "central_crop()", + ]: + with self.assertRaises(BaseException): + builder.get_preprocess_fn(pp_str)(x) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/ops_general.py b/big_vision/pp/ops_general.py new file mode 100644 index 0000000000000000000000000000000000000000..32622cba59143fc96285ab281ac8ad356f50bd73 --- /dev/null +++ b/big_vision/pp/ops_general.py @@ -0,0 +1,453 @@ +# Copyright 2024 Big Vision 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. + +"""Generic tensor preprocessing ops. + +All preprocessing ops should return a data processing functors. A data +is represented as a dictionary of (TF) tensors. The functors output a modified +dictionary. +""" + +import collections + +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import big_vision.utils as bv_utils +import jax +import numpy as np +import tensorflow as tf + + +@Registry.register("preprocess_ops.value_range") +@utils.InKeyOutKey() +def get_value_range(vmin=-1, vmax=1, in_min=0, in_max=255.0, clip_values=False): + """Transforms a [in_min,in_max] image to [vmin,vmax] range. + + Input ranges in_min/in_max can be equal-size lists to rescale the invidudal + channels independently. + + Args: + vmin: A scalar. Output max value. + vmax: A scalar. Output min value. + in_min: A scalar or a list of input min values to scale. If a list, the + length should match to the number of channels in the image. + in_max: A scalar or a list of input max values to scale. If a list, the + length should match to the number of channels in the image. + clip_values: Whether to clip the output values to the provided ranges. + + Returns: + A function to rescale the values. + """ + + def _value_range(image): + """Scales values in given range.""" + in_min_t = tf.constant(in_min, tf.float32) + in_max_t = tf.constant(in_max, tf.float32) + image = tf.cast(image, tf.float32) + image = (image - in_min_t) / (in_max_t - in_min_t) + image = vmin + image * (vmax - vmin) + if clip_values: + image = tf.clip_by_value(image, vmin, vmax) + return image + + return _value_range + + +@Registry.register("preprocess_ops.lookup") +@utils.InKeyOutKey() +def get_lookup(mapping, npzkey="fnames", sep=None): + """Map string to number.""" + + # For NumPy files, we use the `npzkey` array in that file as the list of + # strings which are mapped to their index in that array. + # This is especially useful when other data (eg precomputed predictions) + # goes along with this mapping, to have everything in one place (the npz). + if mapping.endswith(".npz"): + with tf.io.gfile.GFile(mapping, "rb") as f: + keys = np.array(np.load(f, allow_pickle=False)[npzkey]) + vals = np.arange(len(keys)) + + # Otherwise, we simply use the file as a text file, with either of: + # - a string per line, mapped to its line-number + # - a pair, separated by `sep` per line, first value being the string, second + # value being the integer that the string is mapped to. + else: + with tf.io.gfile.GFile(mapping, "r") as f: + buf = f.read() + if sep is None: # values are the line numbers + keys = buf.splitlines() + vals = np.arange(len(keys)) + else: # each line is keyval, also make val int + keys, vals = zip(*[l.split(sep) for l in buf.splitlines()]) + vals = [int(v) for v in vals] + + def _do_the_mapping(needle): + """Map string to number.""" + with tf.init_scope(): # (Originally added for performance reasons.) + table = tf.lookup.StaticHashTable( + tf.lookup.KeyValueTensorInitializer(keys, vals), -1) + return table.lookup(needle) + + return _do_the_mapping + + +@Registry.register("preprocess_ops.onehot") +def get_onehot(depth, + key="labels", + key_result=None, + multi=True, + on=1.0, + off=0.0): + """One-hot encodes the input. + + Args: + depth: Length of the one-hot vector (how many classes). + key: Key of the data to be one-hot encoded. + key_result: Key under which to store the result (same as `key` if None). + multi: If there are multiple labels, whether to merge them into the same + "multi-hot" vector (True) or keep them as an extra dimension (False). + on: Value to fill in for the positive label (default: 1). + off: Value to fill in for negative labels (default: 0). + + Returns: + Data dictionary. + """ + + def _onehot(data): + # When there's more than one label, this is significantly more efficient + # than using tf.one_hot followed by tf.reduce_max; we tested. + labels = data[key] + labels = tf.cast(labels, tf.int64) # both scatter and one_hot expect this + if labels.shape.rank > 0 and multi: + x = tf.scatter_nd(labels[:, None], tf.ones(tf.shape(labels)[0]), (depth,)) + x = tf.clip_by_value(x, 0, 1) * (on - off) + off + else: + x = tf.one_hot(labels, depth, on_value=on, off_value=off) + data[key_result or key] = x + return data + + return _onehot + + +@Registry.register("preprocess_ops.keep") +def get_keep(*keys): + """Keeps only the given keys.""" + + def _keep(data): + return {k: v for k, v in data.items() if k in keys} + + return _keep + + +@Registry.register("preprocess_ops.drop") +def get_drop(*keys): + """Drops the given keys.""" + + def _drop(data): + return {k: v for k, v in data.items() if k not in keys} + + return _drop + + +@Registry.register("preprocess_ops.copy") +def get_copy(inkey, outkey): + """Copies value of `inkey` into `outkey`.""" + + def _copy(data): + # A "semi-deep" copy. deepcopy doesn't work when tf tensors are part of the + # game. What we want, is to only copy the python structure (dicts, lists) + # and keep tensors as they are, since we never modify them in-place anyways. + # The following achieves exactly that. + data[outkey] = jax.tree.map(lambda x: x, data[inkey]) + return data + + return _copy + + +@Registry.register("preprocess_ops.squeeze_last_dim") +@utils.InKeyOutKey() +def get_squeeze_last_dim(): + def _squeeze_last_dim(x): + return tf.squeeze(x, axis=-1) + return _squeeze_last_dim + + +@Registry.register("preprocess_ops.concat") +def get_concat(inkeys, outkey=None, axis=-1): + """Concatenates elements along some axis.""" + + def _concat(data): + data[outkey or inkeys[0]] = tf.concat([data[k] for k in inkeys], axis) + return data + + return _concat + + +@Registry.register("preprocess_ops.rag_tensor") +@utils.InKeyOutKey() +def get_rag_tensor(): + """Converts the specified feature to ragged tensor.""" + + def rag_tensor(raw_tensor): + # Note: Add one more dimension as `from_tensor` requires at least rank 2. + return tf.RaggedTensor.from_tensor(raw_tensor[None]) + + return rag_tensor + + +@Registry.register("preprocess_ops.pad_to_shape") +@utils.InKeyOutKey() +def get_pad_to_shape(shape, pad_value=0, where="after"): + """Pads tensor to specified `shape`.""" + + def _pads(cur, tgt): + if tgt is None: + return [0, 0] + diff = tgt - cur + return { + "before": [diff, 0], + "after": [0, diff], + "both": [diff // 2, diff - diff // 2], + }[where] + + def _pad_to_shape(x): + assert len(x.shape.as_list()) == len(shape) + paddings = [_pads(tgt=shape[i], cur=tf.shape(x)[i]) + for i in range(len(shape))] + constant_value = tf.constant(pad_value, x.dtype) + ret = tf.pad(x, paddings, constant_values=constant_value) + ret.set_shape(shape) + return ret + + return _pad_to_shape + + +@Registry.register("preprocess_ops.flatten") +def get_flatten(): + """Flattens the keys of data with separator '/'.""" + + def flatten(data): + flat, _ = bv_utils.tree_flatten_with_names(data) + return dict(flat) + + return flatten + + +@Registry.register("preprocess_ops.reshape") +@utils.InKeyOutKey() +def get_reshape(new_shape): + """Reshapes tensor to a given new shape. + + Args: + new_shape: new shape for the tensor. + + Returns: + A function for reshaping a tensor. + + """ + + def _reshape(tensor): + """Reshapes a tensor to a given shape.""" + dtype = tensor.dtype + tensor = tf.reshape(tensor, new_shape) + return tf.cast(tensor, dtype) + + return _reshape + + +@Registry.register("preprocess_ops.setdefault") +def get_setdefault(key, value): + """If `key` is an empty tensor or missing, set it to `value`.""" + def _setdefault(data): + x = data.get(key, tf.constant(value)) + v = tf.constant(value, dtype=x.dtype) + v = tf.broadcast_to(v, [s or 1 for s in x.shape]) + data[key] = tf.cond(tf.size(x) > 0, lambda: x, lambda: v) + return data + return _setdefault + + +@Registry.register("preprocess_ops.choice") +def get_choice(n="single", key=None, fewer_ok=False, inkey=None, outkey=None): + """Chooses the same `n` random entries of all `keys`. + + Args: + n: how many entries to randomly sample (without repeat). Possible values: + - int: that many entries (or fewer if there's fewer, see `fewer_ok`.) + - "single": The string "single" only chooses one and drop the leading dim. + - [min, max]: A pair means randomly take between min/max examples (incl.). + key: str or list of str: See Note. + fewer_ok: whether to fail when there's fewer than `n` elements to choose + from (and hence set static shape to `n`), or whether to allow it. + (and hence have unknown static shape). + inkey: str or list of str: See Note. + outkey: str or list of str: See Note. + + Note: + If key/inkey/outkey is a list, then the same random entries are chosen for + all of the keys. Other than that, they function the same as InKeyOutKey. + + The outkey can also contain the placeholder `{key}` that'll be . + + Examples: + choice(key="alt_text/text") + choice(n=128, key=["patches", "positions"]) + choice(inkey=["questions_i18n", "answers_i18n"], outkey=["q", "a"]) + + Returns: + The pp op. + """ + + # Normalize keys: + inkeys = utils.maybe_repeat(inkey or key, 1) + outkeys = utils.maybe_repeat(outkey or key, 1) + outkeys = [ok.format(key=ik) for ok, ik in zip(outkeys, inkeys)] + + # Let's DRY on this condition and give it a name. + is_varlen = isinstance(n, (list, tuple)) + min_n = n[0] if is_varlen else 1 if n == "single" else n + + def _choice(data): + nitems = tf.shape(data[inkeys[0]])[0] + + # Sanity check that all keys have same leading dimension, and that is at + # least as large as the minimum requested output. + lengths = [tf.shape(data[k])[0] for k in inkeys] + checks = [tf.debugging.assert_equal(l, nitems) for l in lengths] + if not fewer_ok: # Since we check for all-same, a single suffices here. + checks.append(tf.debugging.assert_greater_equal(nitems, min_n)) + with tf.control_dependencies(checks): + nitems = tf.identity(nitems) + + if n == "single": + index = tf.random.uniform([], 0, nitems, dtype=tf.int32) + else: + # Subsample by shuffling and taking first n, but... + indices = tf.random.shuffle(tf.range(nitems)) + end = n + if is_varlen: + end = tf.random.uniform([], n[0], n[1] + 1, dtype=tf.int32) + # ...keep the order while subsampling (it might have a meaning, eg boxes) + indices = tf.sort(indices[:end]) + + for ik, ok in zip(inkeys, outkeys): + if n == "single": + result = data[ik][index] + else: + result = tf.gather(data[ik], indices, axis=0) + if not is_varlen: # Give static shape when we can. + result = tf.ensure_shape(result, [n] + [None] * (result.ndim - 1)) + data[ok] = result + + return data + return _choice + + +def _shuffled_index(count, nitems, seed): + """Returns index from a shuffled sequence (items only repeat after epoch).""" + nitems = tf.cast(nitems, count.dtype) + item_epoch, item_offset = (count // nitems, count % nitems) + shuffled_indices = tf.random.experimental.stateless_shuffle( + tf.range(nitems), seed=tf.random.fold_in(seed, item_epoch)) + return shuffled_indices[item_offset] + + +@Registry.register("preprocess_ops.choice_no_replacement") +def get_choice_no_replacement(key=None, inkey=None, outkey=None): + """Chooses the same random (no replacement) entry of all `keys`. + + Note: Consider using this for iterating over small datasets with a small + number of epochs. It differs from `choice(n='single')` in that if an example, + as identified by its `_id` field, is seen N times then it will cycled through + all the inkeys values before repeating them. Additionally each repetition uses + a different order. + + Caveats: requires dataset to provide a _id field and uses host RAM to keep a + counter how often each id is seen. It is also not robust to preemptions. + + Args: + key: str or list of str: See Note. + inkey: str or list of str: See Note. + outkey: str or list of str: See Note. + + Note: + If key/inkey/outkey is a list, then the same random entries are chosen for + all of the keys. Other than that, they function the same as InKeyOutKey. + + The outkey can also contain the placeholder `{key}` that'll be replaced + by the inkey name. + + Examples: + choice(key="alt_text/text") + choice(key=["patches", "positions"]) + choice(inkey=["questions_i18n", "answers_i18n"], outkey=["q", "a"]) + + Returns: + The pp op. + """ + # Normalize keys: + inkeys = utils.maybe_repeat(inkey or key, 1) + outkeys = utils.maybe_repeat(outkey or key, 1) + outkeys = [ok.format(key=ik) for ok, ik in zip(outkeys, inkeys)] + + # TODO: Ideally the data pipeline should provide us with an epoch + # counter. For now count how often we see a given example id and don't worry + # on memory consumption. Counter returns 0 the first time an example is seen. + counter = collections.defaultdict(lambda: -1) + def _seen_count(example_id): + example_id = example_id.item() + counter[example_id] += 1 + return counter[example_id] + + # We need a seed to deterministically decide on a shuffled sequence and use + # the number of times an example was seen to iterate through it. The seed + # should be different for every instance of a create preprocessing function + # but it has to be fixed for each instance. + seed = tf.random.uniform( + [2], minval=tf.int32.min, maxval=tf.int32.max, dtype=tf.int32) + + def _choice(data): + nitems = tf.shape(data[inkeys[0]])[0] + + # Sanity check that all keys have same leading dimension. + checks = [ + tf.debugging.assert_equal(tf.shape(data[k])[0], nitems) + for k in inkeys + ] + with tf.control_dependencies(checks): + nitems = tf.identity(nitems) + + # Using the seed, example id and the number of times an example was seen + # pick an `index` such that items are only repeated after all items are seen + # an equal number of times. E.g. it could return indexes from this sequence: + # [0, 1, 2, 1, 2, 0, 2, 0, 1, 0, 2, 1, ...]. + count = tf.numpy_function( + _seen_count, (data["_id"],), Tout=tf.int64, stateful=True) + count = tf.cast(count, tf.int32) + nitems = tf.cast(nitems, tf.int32) + shuffle_epoch = count // nitems + shuffle_offset = count % nitems + + example_seed = tf.random.fold_in(seed, data["_id"]) + shuffle_seed = tf.random.fold_in(example_seed, shuffle_epoch) + shuffle = tf.random.experimental.stateless_shuffle( + tf.range(nitems), seed=shuffle_seed) + index = shuffle[shuffle_offset] + + # Select item[index] for all keys. + for ik, ok in zip(inkeys, outkeys): + data[ok] = data[ik][index] + return data + + return _choice diff --git a/big_vision/pp/ops_general_test.py b/big_vision/pp/ops_general_test.py new file mode 100644 index 0000000000000000000000000000000000000000..89f616e1690c6e83aff818cf0fff540dcad073fd --- /dev/null +++ b/big_vision/pp/ops_general_test.py @@ -0,0 +1,236 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for ops_general.""" + +import copy + +import big_vision.pp.ops_general as pp +import numpy as np +import tensorflow as tf + + +class PreprocessOpsTest(tf.test.TestCase): + + def tfrun(self, ppfn, data): + # Run once as standalone, as could happen eg in colab. + yield {k: np.array(v) for k, v in ppfn(copy.deepcopy(data)).items()} + + # And then once again as part of tfdata pipeline. + # You'd be surprised how much these two differ! + tfdata = tf.data.Dataset.from_tensors(copy.deepcopy(data)) + for npdata in tfdata.map(ppfn).as_numpy_iterator(): + yield npdata + + def test_value_range(self): + img = tf.random.uniform((640, 480, 3), 0, 255, tf.int32) + data = {"image": tf.cast(img, tf.uint8)} + for out in self.tfrun(pp.get_value_range(-0.5, 0.5), data): + self.assertLessEqual(np.max(out["image"]), 0.5) + self.assertGreaterEqual(np.min(out["image"]), -0.5) + + def test_value_range_custom_input_range(self): + img = tf.random.uniform((640, 480, 3), 0, 255, tf.int32) + data = {"image": tf.cast(img, tf.uint8)} + for out in self.tfrun(pp.get_value_range(-0.5, 0.5, -256, 255, True), data): + self.assertLessEqual(np.max(out["image"]), 0.5) + self.assertGreaterEqual(np.min(out["image"]), 0.0) + + def test_get_keep_drop(self): + data = {"image": 1, "labels": 2, "something": 3} + + for data_keep in self.tfrun(pp.get_keep("image", "labels"), data): + self.assertAllEqual(set(data_keep.keys()), {"image", "labels"}) + + for data_drop in self.tfrun(pp.get_drop("image", "labels"), data): + self.assertAllEqual(set(data_drop.keys()), {"something"}) + + def test_onehot(self): + data = {"labels": tf.constant(2, dtype=tf.int64)} + for out in self.tfrun(pp.get_onehot(4, "labels", multi=True), data): + self.assertAllClose(out["labels"], [0., 0., 1., 0.]) + + def test_onehot_multi(self): + data = {"labels": tf.constant([2, 3, 0], dtype=tf.int64)} + for out in self.tfrun(pp.get_onehot(4, "labels", multi=False), data): + self.assertAllClose(out["labels"], [ + [0., 0., 1., 0.], + [0., 0., 0., 1.], + [1., 0., 0., 0.]]) + + for out in self.tfrun(pp.get_onehot(4, "labels", multi=True), data): + self.assertAllClose(out["labels"], [1., 0., 1., 1.]) + + def test_onehot_2d(self): + data = {"labels": tf.constant([[2, 3], [0, 1]], dtype=tf.int64)} + for out in self.tfrun(pp.get_onehot(4, "labels", multi=False), data): + self.assertAllClose(out["labels"], [ + [[0., 0., 1., 0.], [0., 0., 0., 1.]], + [[1., 0., 0., 0.], [0., 1., 0., 0.]]]) + + def test_onehot_smoothing(self): + data = {"labels": tf.constant([2, 3, 0], dtype=tf.int64)} + for out in self.tfrun( + pp.get_onehot(4, "labels", multi=False, on=0.8, off=0.1), data): + self.assertAllClose(out["labels"], [ + [0.1, 0.1, 0.8, 0.1], + [0.1, 0.1, 0.1, 0.8], + [0.8, 0.1, 0.1, 0.1]]) + + for out in self.tfrun( + pp.get_onehot(4, "labels", multi=True, on=0.8, off=0.1), data): + self.assertAllClose(out["labels"], [0.8, 0.1, 0.8, 0.8]) + + def test_squeeze_last_dim(self): + data = {"image": tf.constant(np.zeros((32, 32, 3, 1)))} + for out in self.tfrun(pp.get_squeeze_last_dim(), data): + self.assertAllEqual(out["image"].shape, [32, 32, 3]) + + def test_pad_to_shape(self): + desired_shape = (8, 10) + for input_shape in [(8, 4), (8, 3), (8, 10), (8, 1)]: + data = {"x": tf.ones(input_shape, dtype=tf.float32)} + for out in self.tfrun( + pp.get_pad_to_shape(desired_shape, pad_value=-1, key="x"), data): + self.assertEqual( + tf.reduce_sum(out["x"]), + 2 * np.prod(input_shape) - np.prod(desired_shape)) + + def test_pad_to_shape_none(self): + data = {"x": tf.ones((8, 4), dtype=tf.float32)} + for out in self.tfrun( + pp.get_pad_to_shape((None, 6), pad_value=-1, key="x"), data): + self.assertEqual(out["x"].shape, (8, 6)) + self.assertEqual(tf.reduce_sum(out["x"]), 8*4 - 8*2) + + def test_pad_to_shape_which_side(self): + data = {"x": tf.ones((8, 4), dtype=tf.float32)} + for where, idxs in [("before", [0]), ("both", [0, -1]), ("after", [-1])]: + for out in self.tfrun( + pp.get_pad_to_shape((8, 6), key="x", where=where), data): + self.assertEqual(out["x"].shape, (8, 6)) + self.assertEqual(tf.reduce_sum(out["x"]), 8*4) + for i in idxs: + self.assertEqual(out["x"][0, i], 0) + + def test_flatten(self): + d = {"a": {"b": tf.constant([1, 2, 3])}, "c": "str"} + self.assertEqual(pp.get_flatten()(d), { + "a/b": tf.constant([1, 2, 3]), + "c": "str" + }) + + def test_reshape(self): + data = {"image": tf.constant(np.zeros((8, 32 * 32 * 3)))} + for out in self.tfrun(pp.get_reshape(new_shape=(8, 32, 32, 3)), data): + self.assertAllEqual(out["image"].shape, [8, 32, 32, 3]) + + def test_setdefault(self): + data = { + "empty_image": tf.zeros([0, 0, 0]), + "image": tf.constant(np.arange(9).reshape(3, 3)), + "empty_text": tf.zeros([0], tf.string), + "text": tf.constant(["Hello", "World"], tf.string), + } + for out in self.tfrun(pp.get_setdefault("empty_image", 1), data): + self.assertAllEqual(out["empty_image"], np.array([[[1]]])) + for out in self.tfrun(pp.get_setdefault("image", 1), data): + self.assertAllEqual(out["image"], data["image"]) + for out in self.tfrun(pp.get_setdefault("empty_text", "Lucas"), data): + self.assertAllEqual(out["empty_text"], np.array(["Lucas"])) + for out in self.tfrun(pp.get_setdefault("text", "Lucas"), data): + self.assertAllEqual(out["text"], data["text"]) + + def _data_for_choice(self): + return { + "one_f32": tf.constant([0.42], tf.float32), + "two_f32": tf.constant([3.14, 0.42], tf.float32), + "one_str": tf.constant(["Hi"], tf.string), + "two_str": tf.constant(["Hi", "Lucas"], tf.string), + "one_vec": tf.reshape(tf.range(2, dtype=tf.float32), (1, 2)), + "two_vec": tf.reshape(tf.range(4, dtype=tf.float32), (2, 2)), + } + + def test_choice(self): + # Test for the default call (n="single") + data = self._data_for_choice() + self.assertEqual( + pp.get_choice(inkey="one_f32", outkey="choice")(data)["choice"], 0.42) + self.assertEqual( + pp.get_choice(inkey="one_str", outkey="choice")(data)["choice"], "Hi") + self.assertIn( + pp.get_choice(inkey="two_f32", outkey="choice")(data)["choice"], + [3.14, 0.42]) + self.assertIn( + pp.get_choice(inkey="two_str", outkey="choice")(data)["choice"], + ["Hi", "Lucas"]) + + def test_choice_nmax(self): + # n == nelems should be identity (and keep ordering!) + data = self._data_for_choice() + for k in ("one_f32", "one_str", "one_vec"): + for out in self.tfrun(pp.get_choice(n=1, key=[k]), data): + self.assertAllEqual(out[k], data[k]) + for out in self.tfrun(pp.get_choice(n=[1, 1], key=[k]), data): + self.assertAllEqual(out[k], data[k]) + for k in ("two_f32", "two_str", "two_vec"): + for out in self.tfrun(pp.get_choice(n=2, key=[k]), data): + self.assertAllEqual(out[k], data[k]) + for out in self.tfrun(pp.get_choice(n=[2, 2], key=[k]), data): + self.assertAllEqual(out[k], data[k]) + + def test_choice_n(self): + # n < nelems should be one of them: + data = self._data_for_choice() + for k in ("two_f32", "two_str"): + for out in self.tfrun(pp.get_choice(n=1, key=[k]), data): + self.assertIn(out[k], data[k]) + + # Special testing for vectors. + for out in self.tfrun(pp.get_choice(n=1, key=["two_vec"]), data): + self.assertTrue(tf.logical_or( + tf.reduce_all(out["two_vec"][0] == data["two_vec"][0]), + tf.reduce_all(out["two_vec"][0] == data["two_vec"][1]), + )) + + def test_choice_multi(self): + # Select consistently across multiple keys. + data = self._data_for_choice() + op = pp.get_choice(n=1, key=["two_f32", "two_str"]) + for out in self.tfrun(op, data): + self.assertTrue(tf.logical_or( + tf.logical_and( + tf.reduce_all(out["two_f32"][0] == data["two_f32"][0]), + tf.reduce_all(out["two_str"][0] == data["two_str"][0]), + ), + tf.logical_and( + tf.reduce_all(out["two_f32"][0] == data["two_f32"][1]), + tf.reduce_all(out["two_str"][0] == data["two_str"][1]), + ), + )) + + def test_choice_n_range(self): + # n < nelems should be one of them: + data = self._data_for_choice() + for k in ("two_f32", "two_str", "two_vec"): + for out in self.tfrun(pp.get_choice(n=[1, 2], key=[k]), data): + self.assertTrue(tf.reduce_any([ + tf.reduce_all(out[k] == data[k][0:1]), + tf.reduce_all(out[k] == data[k][1:2]), + tf.reduce_all(out[k] == data[k][0:2]), + ])) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/ops_image.py b/big_vision/pp/ops_image.py new file mode 100644 index 0000000000000000000000000000000000000000..bdc55a5659c3e0df0c209edef798bbd5c6a7f623 --- /dev/null +++ b/big_vision/pp/ops_image.py @@ -0,0 +1,361 @@ +# Copyright 2024 Big Vision 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. + +"""Image-centric preprocessing ops. + +All preprocessing ops should return a data processing functors. A data +is represented as a dictionary of (TF) tensors. The functors output a modified +dictionary. + +The key named "image" is commonly used for the image, and is a 3D tensor of +shape (height x width x channels). +""" + +from big_vision.pp import utils +from big_vision.pp.registry import Registry + +import tensorflow as tf + + +@Registry.register("preprocess_ops.decode") +@utils.InKeyOutKey() +def get_decode(channels=3, precise=False): + """Decode an encoded image string, see tf.io.decode_image. + + Args: + channels: see tf.io.decode_image. + precise: if False, use default TF image decoding algorithm. + If True, change DCT method for JPEG decoding to match PIL/cv2/PyTorch. + See also (internal link) for a concrete example. + + Returns: + The decoded image. + """ + + def _decode(image): + if precise: + return tf.image.decode_jpeg( # Also supports png btw. + image, channels=channels, dct_method="INTEGER_ACCURATE") + else: + return tf.io.decode_image( + image, channels=channels, expand_animations=False) + + return _decode + + +@Registry.register("preprocess_ops.resize") +@utils.InKeyOutKey() +def get_resize(size, method="bilinear", antialias=False): + """Resizes image to a given size. + + Args: + size: either an integer H, where H is both the new height and width + of the resized image, or a list or tuple [H, W] of integers, where H and W + are new image"s height and width respectively. + method: resize method, see tf.image.resize docs for options. + antialias: see tf.image.resize. Ideally set to True for all new configs. + + Returns: + A function for resizing an image. + + """ + size = utils.maybe_repeat(size, 2) + + def _resize(image): + """Resizes image to a given size.""" + # Note: use TF-2 version of tf.image.resize as the version in TF-1 is + # buggy: https://github.com/tensorflow/tensorflow/issues/6720. + # In particular it was not equivariant with rotation and lead to the network + # to learn a shortcut in self-supervised rotation task, if rotation was + # applied after resize. + dtype = image.dtype + tf_dtype = tf.type_spec_from_value(image).dtype + image = tf.image.resize(image, size, method=method, antialias=antialias) + return tf.cast(tf.clip_by_value(image, tf_dtype.min, tf_dtype.max), dtype) + + return _resize + + +# This functionality is used by resize_small and resize_long. But we're not +# registering it as a pp op yet, as there is no need for it. However, it can +# probably be slightly generalized into "scale augmentation" eventually. +def _resize_factor(image, factor, method="area", antialias=True): + """Resizes the image by a (float) `factor`, keeping the aspect ratio fixed.""" + h, w = tf.shape(image)[0], tf.shape(image)[1] + + h = tf.cast(tf.round(tf.cast(h, tf.float32) * factor), tf.int32) + w = tf.cast(tf.round(tf.cast(w, tf.float32) * factor), tf.int32) + + dtype = image.dtype + tf_dtype = tf.type_spec_from_value(image).dtype + image = tf.image.resize(image, (h, w), method=method, antialias=antialias) + return tf.cast(tf.clip_by_value(image, tf_dtype.min, tf_dtype.max), dtype) + + +@Registry.register("preprocess_ops.resize_small") +@utils.InKeyOutKey() +def get_resize_small(smaller_size, method="area", antialias=False): + """Resizes the smaller side to `smaller_size` keeping aspect ratio. + + Args: + smaller_size: an integer, that represents a new size of the smaller side of + an input image. + method: the resize method. `area` is a meaningful, bwd-compat default. + antialias: see tf.image.resize. Ideally set to True for all new configs. + + Returns: + A function, that resizes an image and preserves its aspect ratio. + + Note: + backwards-compat for "area"+antialias tested here: + (internal link) + """ + + def _resize_small(image): # pylint: disable=missing-docstring + h, w = tf.shape(image)[0], tf.shape(image)[1] + factor = ( + tf.cast(smaller_size, tf.float32) / + tf.cast(tf.minimum(h, w), tf.float32)) + return _resize_factor(image, factor, method=method, antialias=antialias) + return _resize_small + + +@Registry.register("preprocess_ops.resize_long") +@utils.InKeyOutKey() +def get_resize_long(longer_size, method="area", antialias=True): + """Resizes the longer side to `longer_size` keeping aspect ratio. + + Args: + longer_size: an integer, that represents a new size of the longer side of + an input image. + method: the resize method. `area` is a meaningful, bwd-compat default. + antialias: see tf.image.resize. Ideally set to True for all new configs. + + Returns: + A function, that resizes an image and preserves its aspect ratio. + """ + + def _resize_long(image): # pylint: disable=missing-docstring + h, w = tf.shape(image)[0], tf.shape(image)[1] + factor = ( + tf.cast(longer_size, tf.float32) / + tf.cast(tf.maximum(h, w), tf.float32)) + return _resize_factor(image, factor, method=method, antialias=antialias) + return _resize_long + + +@Registry.register("preprocess_ops.inception_crop") +@utils.InKeyOutKey() +def get_inception_crop(size=None, area_min=5, area_max=100, + method="bilinear", antialias=False): + """Makes inception-style image crop. + + Inception-style crop is a random image crop (its size and aspect ratio are + random) that was used for training Inception models, see + https://www.cs.unc.edu/~wliu/papers/GoogLeNet.pdf. + + Args: + size: Resize image to [size, size] after crop. + area_min: minimal crop area. + area_max: maximal crop area. + method: rezied method, see tf.image.resize docs for options. + antialias: see tf.image.resize. Ideally set to True for all new configs. + + Returns: + A function, that applies inception crop. + """ + + def _inception_crop(image): # pylint: disable=missing-docstring + begin, crop_size, _ = tf.image.sample_distorted_bounding_box( + tf.shape(image), + tf.zeros([0, 0, 4], tf.float32), + area_range=(area_min / 100, area_max / 100), + min_object_covered=0, # Don't enforce a minimum area. + use_image_if_no_bounding_boxes=True) + crop = tf.slice(image, begin, crop_size) + # Unfortunately, the above operation loses the depth-dimension. So we need + # to restore it the manual way. + crop.set_shape([None, None, image.shape[-1]]) + if size: + crop = get_resize(size, method, antialias)({"image": crop})["image"] + return crop + + return _inception_crop + + +@Registry.register("preprocess_ops.decode_jpeg_and_inception_crop") +@utils.InKeyOutKey() +def get_decode_jpeg_and_inception_crop(size=None, area_min=5, area_max=100, + ratio_min=0.75, ratio_max=1.33, + method="bilinear", antialias=False): + """Decode jpeg string and make inception-style image crop. + + Inception-style crop is a random image crop (its size and aspect ratio are + random) that was used for training Inception models, see + https://www.cs.unc.edu/~wliu/papers/GoogLeNet.pdf. + + Args: + size: Resize image to [size, size] after crop. + area_min: minimal crop area. + area_max: maximal crop area. + ratio_min: minimal aspect ratio. + ratio_max: maximal aspect ratio. + method: rezied method, see tf.image.resize docs for options. + antialias: see tf.image.resize. Ideally set to True for all new configs. + + Returns: + A function, that applies inception crop. + """ + + def _inception_crop(image_data): # pylint: disable=missing-docstring + shape = tf.image.extract_jpeg_shape(image_data) + begin, crop_size, _ = tf.image.sample_distorted_bounding_box( + shape, + tf.zeros([0, 0, 4], tf.float32), + area_range=(area_min / 100, area_max / 100), + aspect_ratio_range=(ratio_min, ratio_max), + min_object_covered=0, # Don't enforce a minimum area. + use_image_if_no_bounding_boxes=True) + + # Crop the image to the specified bounding box. + offset_y, offset_x, _ = tf.unstack(begin) + target_height, target_width, _ = tf.unstack(crop_size) + crop_window = tf.stack([offset_y, offset_x, target_height, target_width]) + image = tf.image.decode_and_crop_jpeg(image_data, crop_window, channels=3) + + if size: + image = get_resize(size, method, antialias)({"image": image})["image"] + + return image + + return _inception_crop + + +@Registry.register("preprocess_ops.random_crop") +@utils.InKeyOutKey() +def get_random_crop(crop_size): + """Makes a random crop of a given size. + + Args: + crop_size: either an integer H, where H is both the height and width of the + random crop, or a list or tuple [H, W] of integers, where H and W are + height and width of the random crop respectively. + + Returns: + A function, that applies random crop. + """ + crop_size = utils.maybe_repeat(crop_size, 2) + + def _crop(image): + return tf.image.random_crop(image, (*crop_size, image.shape[-1])) + + return _crop + + +@Registry.register("preprocess_ops.central_crop") +@utils.InKeyOutKey() +def get_central_crop(crop_size=None): + """Makes central crop of a given size. + + Args: + crop_size: either an integer H, where H is both the height and width of the + central crop, or a list or tuple [H, W] of integers, where H and W are + height and width of the central crop respectively. If `crop_size` is not + specified, then the largest possible center crop will be taken. + + Returns: + A function, that applies central crop. + """ + if crop_size: + crop_size = utils.maybe_repeat(crop_size, 2) + + def _crop(image): + if crop_size: + h, w = crop_size[0], crop_size[1] + else: + h = w = tf.minimum(tf.shape(image)[0], tf.shape(image)[1]) + dy = (tf.shape(image)[0] - h) // 2 + dx = (tf.shape(image)[1] - w) // 2 + return tf.image.crop_to_bounding_box(image, dy, dx, h, w) + + return _crop + + +@Registry.register("preprocess_ops.flip_lr") +@utils.InKeyOutKey() +def get_random_flip_lr(): + """Flips an image horizontally with probability 50%.""" + + def _random_flip_lr_pp(image): + return tf.image.random_flip_left_right(image) + + return _random_flip_lr_pp + + +@Registry.register("preprocess_ops.vgg_value_range") +@utils.InKeyOutKey() +def get_vgg_value_range( + mean=(0.485 * 255, 0.456 * 255, 0.406 * 255), + std=(0.229 * 255, 0.224 * 255, 0.225 * 255), +): + """VGG-style preprocessing, subtracts mean and divides by stddev. + + This preprocessing is very common for ImageNet pre-trained models since VGG, + and to this day the standard for models coming from most PyTorch codes. + + Args: + mean: Tuple of values to be subtracted. Default to widespread VGG values. + std: Tuple of values to be divided by. Default to widespread VGG values. + + Returns: + A function to rescale the values. + """ + mean = tf.constant(mean, tf.float32) + std = tf.constant(std, tf.float32) + + def _vgg_value_range(image): + return (tf.cast(image, tf.float32) - mean) / std + return _vgg_value_range + + +@Registry.register("preprocess_ops.clip_value_range") +@utils.InKeyOutKey() +def get_clip_value_range(): + mean = (0.48145466 * 255, 0.4578275 * 255, 0.40821073 * 255) + std = (0.26862954 * 255, 0.26130258 * 255, 0.27577711 * 255) + + def _clip_value_range(image): + return (tf.cast(image, tf.float32) - mean) / std + return _clip_value_range + + +@Registry.register("preprocess_ops.convert_to_video") +@utils.InKeyOutKey() +def get_convert_to_video(num_frames): + """Converts an image to a video with zero padded frames. + + Args: + num_frames: total number of frames that the video should have. + + Returns: + A function for converting an image to a video. + """ + + def _convert_to_video(image): + return tf.pad( + tf.expand_dims(image, axis=0), + [[0, num_frames - 1], [0, 0], [0, 0], [0, 0]], + ) + + return _convert_to_video diff --git a/big_vision/pp/ops_image_test.py b/big_vision/pp/ops_image_test.py new file mode 100644 index 0000000000000000000000000000000000000000..080fe673cf90f83b405106dd057870ee8e8f76a2 --- /dev/null +++ b/big_vision/pp/ops_image_test.py @@ -0,0 +1,82 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for ops_image.""" + +import copy +import io + +import big_vision.pp.ops_image as pp +import matplotlib.pyplot as plt +import numpy as np +import tensorflow as tf + + +def get_image_data(): + img = tf.random.uniform((640, 480, 3), 0, 255, tf.int32) # Can't ask uint8!? + return {"image": tf.cast(img, tf.uint8)} + + +class PreprocessOpsTest(tf.test.TestCase): + + def tfrun(self, ppfn, data): + # Run once as standalone, as could happen eg in colab. + yield {k: np.array(v) for k, v in ppfn(copy.deepcopy(data)).items()} + + # And then once again as part of tfdata pipeline. + # You'd be surprised how much these two differ! + tfdata = tf.data.Dataset.from_tensors(copy.deepcopy(data)) + for npdata in tfdata.map(ppfn).as_numpy_iterator(): + yield npdata + + def test_resize(self): + for data in self.tfrun(pp.get_resize([120, 80]), get_image_data()): + self.assertEqual(data["image"].shape, (120, 80, 3)) + + def test_resize_small(self): + for data in self.tfrun(pp.get_resize_small(240), get_image_data()): + self.assertEqual(data["image"].shape, (320, 240, 3)) + + def test_resize_long(self): + for data in self.tfrun(pp.get_resize_long(320), get_image_data()): + self.assertEqual(data["image"].shape, (320, 240, 3)) + + def test_inception_crop(self): + for data in self.tfrun(pp.get_inception_crop(), get_image_data()): + self.assertEqual(data["image"].shape[-1], 3) + + def test_decode_jpeg_and_inception_crop(self): + f = io.BytesIO() + plt.imsave(f, get_image_data()["image"].numpy(), format="jpg") + data = {"image": tf.cast(f.getvalue(), tf.string)} + for data in self.tfrun(pp.get_decode_jpeg_and_inception_crop(), data): + self.assertEqual(data["image"].shape[-1], 3) + + def test_random_crop(self): + for data in self.tfrun(pp.get_random_crop([120, 80]), get_image_data()): + self.assertEqual(data["image"].shape, (120, 80, 3)) + + def test_central_crop(self): + for data in self.tfrun(pp.get_central_crop([20, 80]), get_image_data()): + self.assertEqual(data["image"].shape, (20, 80, 3)) + + def test_random_flip_lr(self): + data_orig = get_image_data() + for data in self.tfrun(pp.get_random_flip_lr(), data_orig): + self.assertTrue( + np.all(data_orig["image"].numpy() == data["image"]) or + np.all(data_orig["image"].numpy() == data["image"][:, ::-1])) + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/ops_text.py b/big_vision/pp/ops_text.py new file mode 100644 index 0000000000000000000000000000000000000000..02172507f7f191ac0af16fb2446275e5e1a03440 --- /dev/null +++ b/big_vision/pp/ops_text.py @@ -0,0 +1,375 @@ +# Copyright 2024 Big Vision 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. + +"""Text-centric preprocessing ops. + +All preprocessing ops should return a data processing functors. A data +is represented as a dictionary of (TF) tensors. The functors output a modified +dictionary. + +A commonly used key for the tokenized output is "labels". +""" +import functools +import importlib + +from absl import logging +from big_vision.datasets.imagenet import class_names as imagenet_class_names +from big_vision.pp import ops_general +from big_vision.pp import tokenizer as bv_tok +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import tensorflow as tf + +from tensorflow.io import gfile + +import sentencepiece +SPProcessor = sentencepiece.SentencePieceProcessor + +import os +os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] = 'python' +import sentencepiece.sentencepiece_model_pb2 +del os.environ['PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION'] +SPModelProto = sentencepiece.sentencepiece_model_pb2.ModelProto + + +# TODO: b/lbeyer - softly introduce and move to new tokenizer API. + +KNOWN_TOKENIZERS = { + "mc4": # used in multilingual models (mT5, PaLI), vocab_size=250_000 + "gs://t5-data/vocabs/mc4.250000.100extra/sentencepiece.model", + "cc_all": # vocab_size=32_000 + "gs://t5-data/vocabs/cc_all.32000/sentencepiece.model", + "c4_en": # vocab_size=32_000 + "gs://t5-data/vocabs/cc_en.32000/sentencepiece.model", + "t5": # same as cc_all, but with 100 extra dummy tokens used by T5 models + "gs://t5-data/vocabs/cc_all.32000.100extra/sentencepiece.model", + "mt5": # same as mc4, but with 100 extra dummy tokens used by T5 models + "gs://t5-data/vocabs/mc4.250000.100extra/sentencepiece.model", +} + + +def create_tokenizer(model="c4_en", add_eos=True, add_bos=False): + """Creates a tokenizer which can be used in tfds.""" + logging.info("Creating tokenizer: %s", model) + with gfile.GFile(KNOWN_TOKENIZERS.get(model, model), "rb") as f: + model = f.read() + + # Lazy import of tensorflow_text so it is an optional dependency for + # the users of this file. + import tensorflow_text + return tensorflow_text.SentencepieceTokenizer( + model=model, add_eos=add_eos, add_bos=add_bos + ) + + +def tokenize(input_text, tokenizer, max_len, *, pad_value, force_eos, + multi_text=False): + """Tokenizes string, and adds `pad_value` if longer than `max_len`.""" + + def pad(tokens): + # Truncate/pad to max_len. + if force_eos: + tokens = tf.cond( + tf.shape(tokens)[0] >= max_len, + lambda: tf.concat( + # For too long, cut them off, but do keep the final EOS token. + [tokens[:max_len - 1], tokens[-1:]], axis=0), + lambda: tf.pad( + tokens, [(0, max_len - tf.shape(tokens)[0])], + constant_values=pad_value), + ) + else: + tokens = tokens[:max_len] + tokens = tf.pad( + tokens, [(0, max_len - tf.shape(tokens)[0])], + constant_values=pad_value) + tokens.set_shape([max_len]) + return tokens + + tokens = tokenizer.tokenize(input_text) + + if multi_text: + tokens = tokens.to_tensor(pad_value) # tf.RaggedTensor to tf.Tensor + tokens = tf.reshape(tokens, [-1, tf.shape(tokens)[-1]]) + tokens = tf.map_fn(pad, tokens) # `map_fn` only maps on axis 0 + + final_shape = tf.concat([tf.shape(input_text), [max_len]], axis=0) + return tf.reshape(tokens, final_shape) + else: + return pad(tokens) + + +@Registry.register("preprocess_ops.tokenize") +@utils.InKeyOutKey(indefault=None, outdefault="labels") +def get_pp_tokenize( + max_len, + eos, + model="c4_en", + lower=True, + sample_if_multi=True, + pad_value="", + add_bos=False +): + """Tokenizes a text. + + Let's assume max_len=3 and id("")=1, id("a")=2, then we have + + 1. `eos="none", pad_value=0`: + - "a" -> [2, 0, 0] + - "aa" -> [2, 2, 0] + - "aaa" -> [2, 2, 2] + + 2. `eos="yes", pad_value=0`: + - "a" -> [2, 1, 0] + - "aa" -> [2, 2, 1] + - "aaa" -> [2, 2, 2] + + This is usually used with generative models that need to learn when to + properly predict a "" (when the sentence is finished) and when to + abstain (when the sentence is truncated). + + 3. `eos="sticky", pad_value=0`: + - "a" -> [2, 1, 0] + - "aa" -> [2, 2, 1] + - "aaa" -> [2, 2, 1] + + 4. `eos="sticky", pad_value=1`: + - "a" -> [2, 1, 1] + - "aa" -> [2, 2, 1] + - "aaa" -> [2, 2, 1] + + This is traditionally used with contrastive models that use the last token + for embeddings, similarly to "cls" tokens in BERT-style models. + + Args: + max_len: maximum length of the tokenized text. + eos: Whether to add an "" (end of sentence) token and whether to keep it + when the sequence is longer than `max_len - 1`. See examples above for + details. Valid values: "none", "yes", "sticky". + model: a path to the pretrained sentencepiece model. + lower: lowercase the text before tokenizing. + sample_if_multi: If there's more than one, randomly pick one if this is + True; otherwise pick all texts and keep the input's batch shape in result. + pad_value: which token to pad the sequence with. If a string (for example + `""`), tokenize it and use its first token. Note that there is no + guarantee to have any padding at the end of the sentence, if the sentence + is longer than `max_len`. + add_bos: adds beginning of sentence symbol. + + Returns: + an op that outputs tokenized text. + """ + + if eos not in ("yes", "none", "sticky"): + raise ValueError(f"Invalid value for eos: '{eos}'.") + + tokenizer = create_tokenizer(model, add_eos=eos != "none", add_bos=add_bos) + + if isinstance(pad_value, str): + pad_value = tokenizer.string_to_id(pad_value) + + def _pp_tokenize(txt): + if sample_if_multi and tf.convert_to_tensor(txt).ndim: + # TODO: I wish this code-path could die. + logging.warning("sample_if_multi is deprecated and will be removed." + "Call `choice` (and maybe `setdefault`) instead.") + txt = ops_general.get_choice(key="t")( + ops_general.get_setdefault("t", "")({"t": txt}))["t"] + + if lower: + txt = tf.strings.lower(txt) if sample_if_multi else tf.map_fn( + tf.strings.lower, txt) + + return tokenize( + txt, + tokenizer, + max_len, + pad_value=pad_value, + force_eos=eos == "sticky", + multi_text=not sample_if_multi) + + return _pp_tokenize + + +@Registry.register("preprocess_ops.coco_captions") +def get_coco_captions(outkey="captions"): + """Extracts coco's captions from nested dict.""" + + def _pp_coco_captions(data): + data[outkey] = data["captions"]["text"] + return data + + return _pp_coco_captions + + +@Registry.register("preprocess_ops.clip_i1k_label_names") +@utils.InKeyOutKey(indefault="label", outdefault="labels") +def get_pp_clip_i1k_label_names(): + """Convert i1k label numbers to strings, using CLIP's class names.""" + + def _pp_imagenet_labels(label): + return tf.gather(imagenet_class_names.CLIP_IMAGENET_CLASS_NAMES, label) + + return _pp_imagenet_labels + + +@Registry.register("preprocess_ops.lower") +@utils.InKeyOutKey(indefault="text", outdefault="text") +def get_lower(): + """Lowercases text feature.""" + + def _pp_lower(text): + return tf.strings.lower(text) + + return _pp_lower + + +def _add_pieces(model_bytes, extra_pieces): + """Adds extra pieces to sentencpiece model specified by `model_bytes`.""" + + model = SPProcessor() + model.LoadFromSerializedProto(model_bytes) + unk_idx = model.PieceToId("") + assert model.IdToPiece(unk_idx) == "", model.IdToPiece(unk_idx) + + model_proto = SPModelProto.FromString(model_bytes) + idx_to_updated_piece = {} + for piece in extra_pieces: + # The SentencePieceModel proto stores whitespaces as the special + # character '▁'. We perform the conversion here. + piece = piece.replace(" ", "▁") + spiece = model_proto.SentencePiece( + piece=piece, + # We set the highest score to force priority on user defined tokens. + score=0.0, + type=model_proto.SentencePiece().Type.USER_DEFINED, + ) + existing_idx = model.PieceToId(piece) + if (existing_idx != unk_idx) ^ (piece == ""): + idx_to_updated_piece[existing_idx] = spiece + logging.info("Updating token at idx %d: %s", existing_idx, spiece.piece) + else: + model_proto.pieces.append(spiece) + + # Replace duplicated pieces with updated ones. + updated_pieces = [ + idx_to_updated_piece.get(i, piece) + for i, piece in enumerate(model_proto.pieces) + ] + del model_proto.pieces[:] + model_proto.pieces.extend(updated_pieces) + + return model_proto.SerializeToString() + + +def _iterable(x): + if isinstance(x, tf.RaggedTensor): + return True + if getattr(x, "ndim", 0) > 1: # np, jnp + return True + if isinstance(x, (list, tuple)) and not isinstance(x[0], (int, float)): + return True + return False + + +@Registry.register("tokenizers.sp") +class SentencepieceTokenizer(bv_tok.Tokenizer): + """Wraps a `tftext.SentencepieceTokenizer`. + + If you plan to use this tokenizer, please familiarize yourself with the test + cases first. This is likely to save you a lot of troubles down the road, trust + me! + """ + + def __init__(self, model, tokensets=()): + with gfile.GFile(KNOWN_TOKENIZERS.get(model, model), "rb") as f: + model_bytes = f.read() + extras = bv_tok.get_extra_tokens(tokensets) + model_bytes = _add_pieces(model_bytes, extras) + self._tok_sp = SPProcessor() + self._tok_sp.LoadFromSerializedProto(model_bytes) + self.extras = {self._tok_sp.PieceToId(x): x for x in extras} + + def to_int(self, text, *, bos=False, eos=False): + def _single(s): + return ( + ([self.bos_token] if bos else []) + + self._tok_sp.EncodeAsIds(s) + + ([self.eos_token] if eos else []) + ) + if isinstance(text, str): + return _single(text) + return type(text)([_single(s) for s in text]) + + def to_str(self, tokens, *, stop_at_eos=True): + def _single(toks): + toks = [int(t) for t in toks] # We really need this for DecodeIds. + if stop_at_eos: + try: # The SentencePiece strips eos, but does not stop at it, so we do. + toks = toks[:toks.index(self.eos_token)] + except ValueError: # No eos token found, nothing to do. + pass + return self._tok_sp.DecodeIds(toks) + if _iterable(tokens): + return [_single(toks) for toks in tokens] + return _single(tokens) + + def _check_known(self, piece): + if (id_ := self._tok_sp.PieceToId(piece)) == self._tok_sp.unk_id(): + logging.error("Piece '%s' is not known (unk=%s)!", piece, id_) + return id_ + + def to_piece(self, idx): + return self._tok_sp.IdToPiece(int(idx)) + + @property + def pad_token(self): + return self._tok_sp.pad_id() + + @property + def eos_token(self): + return self._tok_sp.eos_id() + + @property + def bos_token(self): + return self._tok_sp.bos_id() + + @property + def vocab_size(self): + return self._tok_sp.GetPieceSize() + + # For the _tf_op variants, we need a lot of wrapping boilerplate. + + def to_int_tf_op(self, text, *, bos=False, eos=False): + text = tf.convert_to_tensor(text) + if text.ndim == 0: + def fn(txt): + string = txt.numpy().decode() + return tf.constant(self.to_int(string, bos=bos, eos=eos), tf.int32) + return tf.py_function(fn, [text], tf.int32) + else: + def fn(txt): + strings = [s.decode() for s in txt.numpy().tolist()] + toks = self.to_int(strings, bos=bos, eos=eos) + return tf.ragged.constant(toks) + out_type = tf.RaggedTensorSpec([tf.shape(text)[0], None], tf.int32) + return tf.py_function(fn, [text], Tout=out_type) + + def to_str_tf_op(self, tokens, *, stop_at_eos=True): + def single(t): + fn = functools.partial(self.to_str, stop_at_eos=stop_at_eos) + return tf.numpy_function(fn, [t], tf.string, stateful=False) + if _iterable(tokens): + return tf.map_fn(single, tokens, tf.string) + return single(tokens) diff --git a/big_vision/pp/ops_text_test.py b/big_vision/pp/ops_text_test.py new file mode 100644 index 0000000000000000000000000000000000000000..5d9e7aaa90d4bd3382d60820492fdc5bb37a838d --- /dev/null +++ b/big_vision/pp/ops_text_test.py @@ -0,0 +1,159 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for ops_text.""" + +import copy + +from absl.testing import parameterized +import big_vision.pp.ops_text as pp +from big_vision.pp.registry import Registry +import numpy as np +import tensorflow as tf + + +class PyToTfWrapper: + """Allows to use `to_{int,str}_tf()` via `to_{int,str}()`.""" + + def __init__(self, tok): + self.tok = tok + self.bos_token = tok.bos_token + self.eos_token = tok.eos_token + self.vocab_size = tok.vocab_size + + def to_int(self, text, *, bos=False, eos=False): + ret = self.tok.to_int_tf_op(text, bos=bos, eos=eos) + if isinstance(ret, tf.RaggedTensor): + return [t.numpy().tolist() for t in ret] + return ret.numpy().tolist() + + def to_str(self, tokens, stop_at_eos=True): + ret = self.tok.to_str_tf_op( + tf.ragged.constant(tokens), + stop_at_eos=stop_at_eos, + ) + if ret.ndim == 0: + return ret.numpy().decode() + return [t.numpy().decode() for t in ret] + + +class PpOpsTest(tf.test.TestCase, parameterized.TestCase): + + def tfrun(self, ppfn, data): + # Run once as standalone, as could happen eg in colab. + yield {k: np.array(v) for k, v in ppfn(copy.deepcopy(data)).items()} + + # And then once again as part of tfdata pipeline. + # You'd be surprised how much these two differ! + tfdata = tf.data.Dataset.from_tensors(copy.deepcopy(data)) + for npdata in tfdata.map(ppfn).as_numpy_iterator(): + yield npdata + + def testtok(self): + # https://github.com/google/sentencepiece/blob/master/python/test/test_model.model + return "test_model.model" # Should we just commit it? It's 200kB + + def test_get_pp_clip_i1k_label_names(self): + op = pp.get_pp_clip_i1k_label_names() + labels = op({"label": tf.constant([0, 1])})["labels"].numpy().tolist() + self.assertAllEqual(labels, ["tench", "goldfish"]) + + @parameterized.parameters((b"Hello world ScAlAr!", b"hello world scalar!"), + (["Decoded Array!"], ["decoded array!"]), + ([b"aA", "bB"], [b"aa", "bb"])) + def test_get_lower(self, inputs, expected_output): + op = pp.get_lower() + out = op({"text": tf.constant(inputs)}) + self.assertAllEqual(out["text"].numpy(), np.array(expected_output)) + + @parameterized.named_parameters( + ("py", False), + ("tf", True), + ) + def test_sentencepiece_tokenizer(self, wrap_tok): + tok = pp.SentencepieceTokenizer(self.testtok()) + if wrap_tok: + tok = PyToTfWrapper(tok) + self.assertEqual(tok.vocab_size, 1000) + bos, eos = tok.bos_token, tok.eos_token + self.assertEqual(bos, 1) + self.assertEqual(eos, 2) + # Note: test model does NOT have a token (similar to e.g. "mistral"). + # `.to_int()` wraps `.to_int_tf_ops` which is thus also tested + self.assertEqual(tok.to_int("blah"), [80, 180, 60]) + self.assertEqual(tok.to_int("blah", bos=True), [bos, 80, 180, 60]) + self.assertEqual(tok.to_int("blah", eos=True), [80, 180, 60, eos]) + self.assertEqual( + tok.to_int("blah", bos=True, eos=True), [bos, 80, 180, 60, eos] + ) + self.assertEqual( + tok.to_int(["blah", "blah blah"]), + [[80, 180, 60], [80, 180, 60, 80, 180, 60]], + ) + # inverse of above + # `.to_str()` wraps `.to_str_tf_ops` which is thus also tested + self.assertEqual(tok.to_str([80, 180, 60]), "blah") + self.assertEqual(tok.to_str([1, 80, 180, 60]), "blah") + self.assertEqual(tok.to_str([80, 180, 60, 2]), "blah") + self.assertEqual( + tok.to_str([[80, 180, 60], [80, 180, 60, 80, 180, 60]]), + ["blah", "blah blah"], + ) + + def test_sentencepiece_tokenizer_tf_op_ndarray_input(self): + tok = pp.SentencepieceTokenizer(self.testtok()) + bos, eos = tok.bos_token, tok.eos_token + arr = np.array([[bos, 80, 180, 60, eos]] * 2, dtype=np.int32) + self.assertEqual(tok.to_str_tf_op(arr).numpy().tolist(), [b"blah"] * 2) + + def test_sentencepiece_tokenizer_tokensets(self): + tok = pp.SentencepieceTokenizer(self.testtok(), tokensets=["loc"]) + self.assertEqual(tok.vocab_size, 2024) + self.assertEqual( + tok.to_int("blah"), [80, 180, 60, 1000, 2023] + ) + + def test_sentencepiece_stop_at_eos(self): + tok = pp.SentencepieceTokenizer(self.testtok()) + self.assertEqual(tok.to_str([80, 180, 60], stop_at_eos=False), "blah") + eos = tok.eos_token + self.assertEqual(tok.to_str([80, eos, 180, 60], stop_at_eos=False), "blah") + self.assertEqual(tok.to_str([80, eos, 180, 60], stop_at_eos=True), "b") + self.assertEqual( + tok.to_str([[80, eos, 180, 60], [80, 180, eos, 60]], stop_at_eos=True), + ["b", "bla"] + ) + + def test_sentencepiece_extra_tokens(self): + tok = pp.SentencepieceTokenizer(self.testtok()) + self.assertEqual(tok.to_str([1, 80, 180, 60, 2], stop_at_eos=False), "blah") + tok = pp.SentencepieceTokenizer( + self.testtok(), tokensets=["sp_extra_tokens"] + ) + self.assertEqual(tok.vocab_size, 1001) # Also added the token. + self.assertEqual( + tok.to_str([1, 80, 180, 60, 2], stop_at_eos=False), " blah" + ) + + +@Registry.register("tokensets.sp_extra_tokens") +def _get_sp_extra_tokens(): + # For sentencepiece, adding these tokens will make them visible when decoding. + # If a token is not found (e.g. "" is not found in "mistral"), then it is + # added to the vocabulary, increasing the vocab_size accordingly. + return ["", "", ""] + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/proj/clippo/download_unifont.sh b/big_vision/pp/proj/clippo/download_unifont.sh new file mode 100644 index 0000000000000000000000000000000000000000..44251749e0c8171318f49e75b9b725d29d06c53a --- /dev/null +++ b/big_vision/pp/proj/clippo/download_unifont.sh @@ -0,0 +1,21 @@ +# Copyright 2022 Big Vision 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. + +#!/bin/bash +# This is intended to be run from the big_vision repository root: +# +# bash big_vision/pp/proj/clippo/download_unifont.sh +wget https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont-9.0.06.hex.gz https://unifoundry.com/pub/unifont/unifont-9.0.06/font-builds/unifont_upper-9.0.06.hex.gz +gunzip unifont-9.0.06.hex.gz unifont_upper-9.0.06.hex.gz +mv unifont-9.0.06.hex unifont_upper-9.0.06.hex big_vision/pp/proj/clippo/ \ No newline at end of file diff --git a/big_vision/pp/proj/clippo/pp_ops.py b/big_vision/pp/proj/clippo/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..e92050dc5f3371c730913637be191c0c63bf5423 --- /dev/null +++ b/big_vision/pp/proj/clippo/pp_ops.py @@ -0,0 +1,153 @@ +# Copyright 2022 Big Vision 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. + +"""Preprocessing functions for CLIP with Pixels Only (CLIPPO).""" +from absl import logging +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import numpy as np +import tensorflow as tf + + +@Registry.register("preprocess_ops.render_unifont") +@utils.InKeyOutKey(indefault="texts", outdefault="image") +def get_pp_render_text(image_size: int, font_size: int = 16, max_chars=768, + background_brightness=127, text_brightness=0, + lower=True, monospace=False, spacing=1, min_width=4, + resize_method="area"): + """Renders text as image, using binary Unifont. + + Largely based on Jeffrey Sorensen's text rendering implementation. + + Args: + image_size: Width/height of output image. + font_size: Font size to use. Recommended to leave at 16, as this requires + no resizing, and is safe. + max_chars: Maximum inpute characters to render, to make faster. + background_brightness: (r, g, b) of background pixels. + text_brightness: (r, g, b) of text pixels. + lower: whether to lowercase. + monospace: if False, text characters are horizontally trimmed according to + `spacing` and `minwidth` args. + spacing: # pixels between each letter. + min_width: Minimum width of each letter. Useful to make sure e.g. spaces and + full stops aren't collapsed to nothing. + resize_method: resize method to use if fontsize != 16. + + Returns: + Function which renders text as an image. + """ + bit_embedding = np.zeros((0x200000, 32), dtype=np.uint8) + colpattern = {64: range(32), + 32: sorted(tuple(range(0, 32, 4)) + tuple(range(2, 32, 4)))} + + unifont_path = "big_vision/pp/proj/clippo/unifont-9.0.06.hex" + unifont_upper_path = "big_vision/pp/proj/clippo/unifont_upper-9.0.06.hex" + + with tf.io.gfile.GFile(unifont_path) as f: + for line in f: + row = int(line[0:4], 16) + hexbits = line[5:-1] + bit_embedding[row, colpattern[len(hexbits)]] = bytearray.fromhex(hexbits) + + with tf.io.gfile.GFile(unifont_upper_path) as f: + for line in f: + row = int(line[0:6], 16) + hexbits = line[7:-1] + bit_embedding[row, colpattern[len(hexbits)]] = bytearray.fromhex(hexbits) + + params = tf.constant(bit_embedding, dtype=tf.uint8) + + def trim_letter(letter): + """Remove white space based on the letter size.""" + v = tf.reduce_max(letter, axis=0) + has_pixels = tf.reshape(tf.where(v), (-1,), name="RS5") + no_pixels = tf.equal(tf.reduce_max(v), 0) + first = tf.cond(no_pixels, lambda: tf.constant(0, tf.int64), + lambda: has_pixels[0]) + last = tf.cond(no_pixels, lambda: tf.constant(0, tf.int64), + lambda: has_pixels[-1]) + + first = tf.maximum(first - spacing, 0) + last = tf.maximum(last + spacing, first + min_width) + return tf.RaggedTensor.from_tensor(tf.transpose(letter[:, first:last])) + + def to_image(rendered, width, height=None): + """Makes a nice square image from a long string of rendered charcaters.""" + height = height or width + max_letter_width = tf.reduce_max(rendered.row_lengths(1)) + row_lengths = tf.cast(tf.cumsum(rendered.row_lengths(1)), tf.float32) + div = tf.cast(width - max_letter_width, tf.float32) # For rounding errors. + row_idx = tf.cast(tf.floor(row_lengths / div), tf.int64) + row_idx = tf.RaggedTensor.from_value_rowids(tf.range(tf.shape(rendered)[0]), + row_idx) + trimmed = tf.gather(rendered, row_idx, axis=0) + trimmed = trimmed.merge_dims(1, 2) + trimmed = trimmed.to_tensor(default_value=0) + trimmed = tf.transpose(trimmed, (0, 2, 1)) + trimmed = tf.reshape(trimmed, (-1, tf.shape(trimmed)[-1]), name="RS4") + trimmed = trimmed[:height] + + wpad = width - tf.shape(trimmed)[1] + hpad = height - tf.shape(trimmed)[0] + padded = tf.pad(trimmed, [[0, hpad], [0, wpad]]) + tf.assert_equal(tf.shape(padded), tf.constant((height, width))) + return tf.ensure_shape(padded, (width, height)) + + def render(text): + if lower: + text = tf.strings.lower(text) + text = tf.reshape(text, (-1,))[0] + ids = tf.strings.unicode_decode(text, "UTF-8") + if max_chars: + ids = ids[:max_chars] + embed = tf.nn.embedding_lookup(params, ids) # Get the letters + # Each letter is 32 uint8s, but we want binary 16x16 grid. + # The following does that in a rather hard to parse way. + vertical = tf.reshape(embed, [1, -1]) + repl = tf.reshape(tf.transpose(tf.tile(vertical, multiples=[8, 1])), [-1]) + ones = tf.ones_like(repl) + index = tf.cumsum(ones, exclusive=True) + sevens = tf.cast(tf.fill(tf.shape(repl), 7), tf.uint8) + moded = tf.bitwise.bitwise_and(index, sevens) + shifted = tf.bitwise.right_shift(repl, + tf.bitwise.bitwise_xor(moded, sevens)) + anded = tf.bitwise.bitwise_and(shifted, ones) + # And finally, letters; binary, 0 = background, 1 = letter. + letters = tf.reshape(anded, [tf.shape(ids)[0], 16, 16]) + + if font_size != 16: + logging.warning("The unifont text rendering function is highly optimized " + "for font size 16; using font size %i might lead to " + "suboptimal rendering and might degrade performance.", + font_size) + letters = tf.image.resize(letters[..., None], (font_size, font_size), + method=resize_method, antialias=True) + letters = tf.squeeze(letters, axis=-1) + + if monospace: + letters = tf.RaggedTensor.from_tensor(tf.transpose(letters, (0, 2, 1))) + else: + letters = tf.RaggedTensor.from_tensor(letters) + signature = tf.RaggedTensorSpec(shape=(None, font_size), ragged_rank=1, + dtype=letters.dtype) + letters = tf.map_fn(trim_letter, letters, fn_output_signature=signature) + + img = to_image(letters, image_size)[..., None] # A nice square image. + img *= (text_brightness - background_brightness) # Rescale value range. + img += background_brightness + + return tf.image.grayscale_to_rgb(tf.cast(img, tf.uint8)) + + return render diff --git a/big_vision/pp/proj/flaxformer/bert_ops.py b/big_vision/pp/proj/flaxformer/bert_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..bcc76ef30b1187b178a9a1b309f188fee4468971 --- /dev/null +++ b/big_vision/pp/proj/flaxformer/bert_ops.py @@ -0,0 +1,86 @@ +# Copyright 2024 Big Vision 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. + +"""BERT-related preprocessing ops (using WordPiece tokenizer).""" + +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import tensorflow as tf +import tensorflow_text + + +# Internally using +# BasicTokenizer +# https://github.com/tensorflow/text/blob/df5250d6cf1069990df4bf55154867391ab5381a/tensorflow_text/python/ops/bert_tokenizer.py#L67 +# WordpieceTokenizer +# https://github.com/tensorflow/text/blob/master/tensorflow_text/python/ops/wordpiece_tokenizer.py +def _create_bert_tokenizer(vocab_path): + """Returns cls_token id and tokenizer to use in a tf.Dataset.map function.""" + # Create tokenizer inside a tf.init_scope so the vocab is only loaded from + # disk once per dataset iterator (see: http://(internal link)). + # TODO: Make a local copy of vocab if creating many iterators. + with tf.init_scope(): + tokenizer = tensorflow_text.BertTokenizer( + vocab_path, + token_out_type=tf.int32, + lower_case=True, + ) + + with tf.io.gfile.GFile(vocab_path) as f: + vocab = f.read().split("\n") + cls_token = vocab.index("[CLS]") + + return cls_token, tokenizer + + +@Registry.register("preprocess_ops.bert_tokenize") +@utils.InKeyOutKey(indefault=None, outdefault="labels") +def get_pp_bert_tokenize(vocab_path, max_len, sample_if_multi=True): + """Extracts tokens with tensorflow_text.BertTokenizer. + + Args: + vocab_path: Path to a file containing the vocabulry for the WordPiece + tokenizer. It's the "vocab.txt" file in the zip file downloaded from + the original repo https://github.com/google-research/bert + max_len: Number of tokens after tokenization. + sample_if_multi: Whether the first text should be taken (if set to `False`), + or whether a random text should be tokenized. + + Returns: + A preprocessing Op. + """ + + cls_token, tokenizer = _create_bert_tokenizer(vocab_path) + + def _pp_bert_tokenize(labels): + + labels = tf.reshape(labels, (-1,)) + labels = tf.concat([labels, [""]], axis=0) + if sample_if_multi: + num_texts = tf.maximum(tf.shape(labels)[0] - 1, 1) # Don't sample "". + txt = labels[tf.random.uniform([], 0, num_texts, dtype=tf.int32)] + else: + txt = labels[0] # Always works, since we append "" earlier on. + + token_ids = tokenizer.tokenize(txt[None]) + padded_token_ids, mask = tensorflow_text.pad_model_inputs( + token_ids, max_len - 1) + del mask # Recovered from zero padding in model. + count = tf.shape(padded_token_ids)[0] + padded_token_ids = tf.concat( + [tf.fill([count, 1], cls_token), padded_token_ids], axis=1) + return padded_token_ids[0] + + return _pp_bert_tokenize + \ No newline at end of file diff --git a/big_vision/pp/proj/flaxformer/bert_ops_test.py b/big_vision/pp/proj/flaxformer/bert_ops_test.py new file mode 100644 index 0000000000000000000000000000000000000000..3df58abd8e4f3712424ccc496d7ed39aae71cdcf --- /dev/null +++ b/big_vision/pp/proj/flaxformer/bert_ops_test.py @@ -0,0 +1,69 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for bert_ops.""" + +import tempfile + +from big_vision import input_pipeline +import big_vision.pp.builder as pp_builder +import big_vision.pp.ops_general # pylint: disable=unused-import +from big_vision.pp.proj.flaxformer import bert_ops # pylint: disable=unused-import +import tensorflow as tf + + +# BERT vocabulary for testing. +_BERT_VOCAB = [ + "[PAD]", + "[UNK]", + "more", + "than", + "one", + "[CLS]", + "[SEP]", +] + + +def _create_ds(pp_str, tensor_slices, num_examples): + return input_pipeline.make_for_inference( + tf.data.Dataset.from_tensor_slices(tensor_slices), + num_ex_per_process=[num_examples], + preprocess_fn=pp_builder.get_preprocess_fn(pp_str), + batch_size=num_examples, + )[0] + + +class BertOpsTest(tf.test.TestCase): + + def test_tokenize(self): + inkey = "texts" + vocab_path = f"{tempfile.mkdtemp()}/vocab.txt" + with open(vocab_path, "w") as f: + f.write("\n".join(_BERT_VOCAB)) + pp_str = ( + f"bert_tokenize(inkey='{inkey}', vocab_path='{vocab_path}', max_len=5)" + f"|keep('labels')" + ) + tensor_slices = { + inkey: tf.ragged.constant([["one more"], ["more than one"], [""]]) + } + ds = _create_ds(pp_str, tensor_slices, 3) + self.assertAllEqual( + next(iter(ds))["labels"], + [[5, 4, 2, 0, 0], [5, 2, 3, 4, 0], [5, 0, 0, 0, 0]], + ) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/proj/givt/pp_ops.py b/big_vision/pp/proj/givt/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..882bc93c45e4a4fd30d061d7b51a6295241653d6 --- /dev/null +++ b/big_vision/pp/proj/givt/pp_ops.py @@ -0,0 +1,36 @@ +# Copyright 2024 Big Vision 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. + +"""GIVT-specific preprocessing ops.""" + +from big_vision.pp import registry +from big_vision.pp import utils +import tensorflow as tf + + +@registry.Registry.register("preprocess_ops.bin_nyu_depth") +@utils.InKeyOutKey(indefault="labels", outdefault="labels") +def get_bin_nyu_depth(min_depth=0.001, max_depth=10.0, num_bins=256): + """Binning of NYU depth for UViM in preprocessing rather than model.""" + + def _bin_depth(labels): # pylint: disable=missing-docstring + labels = (labels - min_depth) / (max_depth - min_depth) + labels *= num_bins + labels = tf.cast(tf.floor(labels), tf.int32) + labels = tf.minimum(labels, num_bins - 1) + labels = tf.maximum(labels, 0) + return labels + + return _bin_depth + diff --git a/big_vision/pp/proj/paligemma/ops.py b/big_vision/pp/proj/paligemma/ops.py new file mode 100644 index 0000000000000000000000000000000000000000..a49a4b57d64570850460041a13edf8493fde8d28 --- /dev/null +++ b/big_vision/pp/proj/paligemma/ops.py @@ -0,0 +1,190 @@ +# Copyright 2024 Big Vision 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. + +"""pp ops.""" + +import functools +import string + +from big_vision.pp import ops_text +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import big_vision.pp.tokenizer as bv_tok +import numpy as np +import tensorflow as tf + + +@Registry.register('tokenizers.gemma') +def get_tokenizer_gemma( + tokensets=(), + model='gs://big_vision/gemma_tokenizer.model', +): + # See (internal link) for colab playground. + return ops_text.SentencepieceTokenizer(model=model, tokensets=tokensets) + + +@functools.cache +def tokenize_constant(model, text, bos='no', eos='no', length=None): + """Tokenize a constant string, with memoization.""" + assert eos in ('no', 'yes', 'sticky') + assert bos in ('no', 'yes') + tokenizer = bv_tok.get_tokenizer(model) + tokens = tokenizer.to_int( + text, bos=bos == 'yes', eos=eos in ('yes', 'sticky')) + + if length is None: + return tokens + + if len(tokens) > length: + if eos == 'sticky': + return np.r_[tokens[:length-1], tokens[-1]] + else: + return tokens[:length] + else: + return np.pad(tokens, [(0, length - len(tokens))], + constant_values=tokenizer.pad_token) + + +@Registry.register('preprocess_ops.tolen') +@utils.InKeyOutKey(indefault=None, outdefault=None, with_data=True) +def get_tolen(length, *, sticky_end=False, pad_value=None, pad_key=None): + """Gets token to a fixed length.""" + def _tolen(x, data): + if not length: + return x + + xlen = tf.shape(x)[0] + + if sticky_end: + trunc_fn = lambda: tf.concat([x[:length - 1], x[-1:]], axis=0) + else: + trunc_fn = lambda: x[:length] + + # Potentially get the pad value from a data key (to be tokenizer agnostic). + pad_value_ = pad_value + if pad_key: + pad_value_ = data[pad_key] + # If coming from a previous tokenization op, it's probably 1D; take first. + if getattr(pad_value_, 'ndim', 0) == 1: + pad_value_ = pad_value_[0] + assert pad_value_ is not None, 'Need either pad_value or pad_key.' + + pad_fn = lambda: tf.pad(x, [(0, length - xlen)], constant_values=pad_value_) + out = tf.cond(xlen >= length, trunc_fn, pad_fn) + out.set_shape([length]) + return out + return _tolen + + +@Registry.register('preprocess_ops.tok') +def get_tokenize(model, length=None, *, bos='no', eos='no', + text=None, key=None, inkey=None, outkey=None): + """Tokenizes and optionally truncates/pads a string.""" + + assert eos in ('no', 'yes', 'sticky') + assert bos in ('no', 'yes') + outkey_ = outkey or key + inkey_ = inkey or key + + if text is not None: + assert inkey is None, 'Either inkey or text, not both.' + tokens = tokenize_constant(model, text, bos=bos, eos=eos, length=length) + def _pp_tokenize_text(data): + data[outkey_] = tokens + return data + return _pp_tokenize_text + + tokenizer = bv_tok.get_tokenizer(model) + + def _pp_tokenize(data): + assert getattr(data[inkey_], 'ndim', 0) == 0, ( + f'Can only tokenize single string ({inkey_}, {data[inkey_].ndim}-D)') + + toks = tokenizer.to_int_tf_op( + data[inkey_], bos=bos == 'yes', eos=eos in ('yes', 'sticky')) + + tolen = get_tolen( + length, sticky_end=eos == 'sticky', + pad_value=bv_tok.get_tokenizer(model).pad_token, + key='tmp', + ) + toks = tolen({'tmp': toks})['tmp'] + + data[outkey_] = toks + return data + return _pp_tokenize + + +@Registry.register('preprocess_ops.masked_concat') +def get_masked_concat(keys, outkey='text', **masks): + assert all(len(keys) == len(m) for m in masks.values()), (keys, masks) + def _masked_concat(data): + data[outkey] = tf.concat([data[k] for k in keys], axis=0) + for mask_name, mask_vals in masks.items(): + m = [tf.fill(tf.shape(data[k]), v) for k, v in zip(keys, mask_vals)] + data[mask_name] = tf.concat(m, axis=0) + return data + return _masked_concat + + +@Registry.register('preprocess_ops.strfmt') +def get_strfmt(template, outkey='text'): + """Formats a string template with content form the data dict.""" + + def _template(data): + outputs = [] + parts = string.Formatter().parse(template) + for (literal_text, field_name, format_spec, conversion) in parts: + # For now, we keep it simple and don't support fancy format specs. + # But we can add support to that via py_func as soon as we need it. + assert not format_spec and not conversion + outputs.append(tf.constant(literal_text)) + if field_name: + value = data[field_name] + # Convert any non-strings (numbers, vectors) to a string. + if tf.convert_to_tensor(value).dtype != tf.string: + value = tf.strings.format('{}', value, summarize=-1) + outputs.append(value) + data[outkey] = tf.strings.join(outputs) + return data + + return _template + + +@Registry.register('preprocess_ops.strjoin') +@utils.InKeyOutKey() +def get_strjoin(glue): + def _strjoin(x): + return tf.strings.reduce_join(x, separator=glue) + return _strjoin + + +@Registry.register('preprocess_ops.majority') +@utils.InKeyOutKey() +def get_majority(): + def _majority(x): + val, _, count = tf.unique_with_counts(x) # Sadly, stablesorted. + return val[tf.argmax(count)] + return _majority + + +@Registry.register('preprocess_ops.getidx') +def getidx(inkey, index_key, outkey=None): + """Indexes a tensor and stores result in outkey.""" + def _getidx(data): + idx = data[index_key] + array = data[inkey] + data[outkey or inkey] = array[idx] + return data + return _getidx diff --git a/big_vision/pp/proj/paligemma/robustness.py b/big_vision/pp/proj/paligemma/robustness.py new file mode 100644 index 0000000000000000000000000000000000000000..e40ed0cc481ff6e5da6349885d3d5a6f50d84da0 --- /dev/null +++ b/big_vision/pp/proj/paligemma/robustness.py @@ -0,0 +1,72 @@ +# Copyright 2024 Big Vision 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. + +"""pp ops.""" + +import math + +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import tensorflow as tf + + +@Registry.register("preprocess_ops.resize_r") +@utils.InKeyOutKey() +def get_resize_r(size): + """Like standard `resize` but randomize some of its parameters.""" + size = utils.maybe_repeat(size, 2) + + # Sadly TF won't let us pass symbolic arguments, so we need to pre-create all + # variants of function calls we'd like to randomize over... + resize_fns = [ + lambda x, m=m, a=a: tf.image.resize(x, size, method=m, antialias=a) + for m in ["bilinear", "bicubic", "lanczos3", "area", "mitchellcubic"] + for a in [True, False] + ] + + def _resize_r(image): + """Resizes image to a given size.""" + dtype = image.dtype + tf_dtype = tf.type_spec_from_value(image).dtype + ifn = tf.random.uniform((), 0, len(resize_fns), tf.int32) + image = tf.switch_case(ifn, [lambda fn=fn: fn(image) for fn in resize_fns]) + return tf.cast(tf.clip_by_value(image, tf_dtype.min, tf_dtype.max), dtype) + + return _resize_r + + +@Registry.register("preprocess_ops.random_jpeg") +@utils.InKeyOutKey() +def get_random_jpeg(p): + """With probability `p`, randomly encode-decode as jpeg.""" + + fns = [ + lambda x: tf.image.adjust_jpeg_quality( + x, dct_method="INTEGER_FAST", + jpeg_quality=tf.random.uniform((), 75, 96, dtype=tf.int32), + ), + lambda x: tf.image.adjust_jpeg_quality( + x, dct_method="INTEGER_ACCURATE", + jpeg_quality=tf.random.uniform((), 75, 96, dtype=tf.int32), + ), + ] + + def _random_jpeg(image): + """Resizes image to a given size.""" + funcs = [lambda: image] + [lambda fn=fn: fn(image) for fn in fns] + logits = [math.log(prob) for prob in [1 - p] + [p / len(fns)] * len(fns)] + fn_idx = tf.random.categorical([logits], 1, dtype=tf.int32)[0, 0] + return tf.switch_case(fn_idx, funcs) + + return _random_jpeg diff --git a/big_vision/pp/proj/paligemma/sciqa_ops.py b/big_vision/pp/proj/paligemma/sciqa_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..845e0c0dadadb662231053ebdee64f24979ffc7d --- /dev/null +++ b/big_vision/pp/proj/paligemma/sciqa_ops.py @@ -0,0 +1,65 @@ +# Copyright 2024 Big Vision 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. + +"""pp ops.""" + +from big_vision.pp.registry import Registry +import tensorflow as tf + + +@Registry.register('preprocess_ops.sci_qa_choices_shuffle') +def sci_qa_choices_shuffle( + choice_str_inkey='choices', + ans_inkey='answer', + indexed_choices_outkey='indexed_choices', + indexed_answer_outkey='indexed_answer', +): + """Random shuffle the sci_qa's choice on the fly. + + Args: + choice_str_inkey: the original choice list from + sciqa,e.g['apple','banana',..] + ans_inkey: the original answer from sciqa e.g. 1 + indexed_choices_outkey: shuffled choice (with index suffix concat to string) + e.g."(A) banana, (B) apple" + indexed_answer_outkey: shuffled answer with abc index, e,g + 1(original)->2(shuffled)->'B' (alphabet index) + + Returns: + """ + def _template(data): + alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + abc_tensor = tf.constant([f'({a})' for a in alphabet]) + abcans_tensor = tf.constant([f'{a}' for a in alphabet]) + choices = data[choice_str_inkey] + indices = tf.range(len(choices)) + # Shuffle the indices + shuffled_indices = tf.random.shuffle(indices) + # Use the shuffled indices to shuffle the tensor + shuffled_tensor = tf.gather(choices, shuffled_indices) + + abc_tensor = tf.gather(abc_tensor, indices) + + data[indexed_choices_outkey] = tf.strings.reduce_join( + tf.strings.join([abc_tensor, shuffled_tensor], separator=' '), + separator=', ', + ) + + answer_tensor = data[ans_inkey] + new_ans_indice = tf.where(tf.equal(shuffled_indices, answer_tensor)) + new_ans_indice = tf.gather(abcans_tensor, new_ans_indice) + data[indexed_answer_outkey] = tf.strings.reduce_join(new_ans_indice) + return data + + return _template diff --git a/big_vision/pp/proj/paligemma/segmentation.py b/big_vision/pp/proj/paligemma/segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..7b062acff63e75fb9b7a7ca975ca043c99e7a973 --- /dev/null +++ b/big_vision/pp/proj/paligemma/segmentation.py @@ -0,0 +1,160 @@ +# Copyright 2024 Big Vision 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. + +"""Segmentation-related ops.""" + +import functools + +from big_vision.pp import registry +import numpy as np +import tensorflow as tf + +from tensorflow.io import gfile + + +_KNOWN_MODELS = { + 'oi': 'gs://big_vision/paligemma/vae-oid.npz', +} + + +@functools.cache +def get_checkpoint(model): + with gfile.GFile(_KNOWN_MODELS.get(model, model), 'rb') as f: + return dict(np.load(f)) + + +@registry.Registry.register('preprocess_ops.refcoco_mask2str') +def get_refcoco_mask2str(model='oi'): + """Returns op for tokenizing a mask.""" + + seg_tokens = tf.constant(['' % i for i in range(128)]) + loc_tokens = tf.constant(['' % i for i in range(1024)]) + checkpoint = get_checkpoint(model) + + def refcoco_mask2str(data): + + mask = data['objects/mask'] + tf.ensure_shape(mask, [None, None, 3]) # requires choice() + sentence = data['objects/refs/sentence'] + tf.ensure_shape(sentence, []) # requires choice() + bbox = data['objects/bbox'] + tf.ensure_shape(bbox, [4]) # requires choice() + + h = tf.cast(tf.shape(mask)[0], tf.float32) + w = tf.cast(tf.shape(mask)[1], tf.float32) + y1 = tf.cast(tf.round(h * bbox[0]), tf.int32) + x1 = tf.cast(tf.round(w * bbox[1]), tf.int32) + y2 = tf.cast(tf.round(h * bbox[2]), tf.int32) + x2 = tf.cast(tf.round(w * bbox[3]), tf.int32) + + assert mask.dtype == tf.uint8, mask.dtype + mask = tf.image.resize( + mask[None, y1:y2, x1:x2, :1], + [64, 64], + method='bilinear', + antialias=True, + ) / 255.0 + + mask_indices = encode_to_codebook_indices(checkpoint, mask)[0] + mask_string = tf.strings.reduce_join(tf.gather(seg_tokens, mask_indices)) + + binned_loc = tf.cast(tf.round(bbox * 1023), tf.int32) + binned_loc = tf.clip_by_value(binned_loc, 0, 1023) + loc_string = tf.strings.reduce_join(tf.gather(loc_tokens, binned_loc)) + + data['prefix'] = sentence + data['suffix'] = tf.strings.join([loc_string, mask_string]) + + return data + + return refcoco_mask2str + + +# Based on https://arxiv.org/abs/2301.02229. + +NUM_DOWNSAMPLE_LAYERS = 4 +NUM_RES_BLOCKS = 2 + + +def encode_to_codebook_indices(checkpoint, masks): + """Encode a batch of binary segmentation masks into 16 tokens each. + + Based on code from https://arxiv.org/abs/2301.02229 + + Args: + checkpoint: model weights from PyTorch model. + masks: Must be in range `[0..1]`, and of shape `[None, 64, 64, 1]`. + + Returns: + A tensor of shape `[None, 16]` with elements in `range(128)`. + """ + + # We require that the input masks are already resized to 64x64. + x = tf.ensure_shape(masks, [None, 64, 64, 1]) + x = _norm(x) + + for n in range(NUM_DOWNSAMPLE_LAYERS): + x = _conv_tf( + checkpoint, x, strides=2, padding='SAME', layer_name=f'encoder.{2*n}' + ) + x = tf.nn.relu(x) + + for n in range(NUM_RES_BLOCKS): + x = _resblock_tf(checkpoint, x, layer_name=f'encoder.{8+n}.net') + + x = _conv_tf( + checkpoint, x, strides=1, padding='SAME', layer_name='encoder.10' + ) + + return _get_codebook_indices(checkpoint, x) + + +def _norm(x): + return 2.0 * (x - 0.5) + + +def _conv_tf(checkpoint, x, strides, padding, layer_name): + kernel = checkpoint[layer_name + '.weight'] + kernel = np.transpose(kernel, (2, 3, 1, 0)) + bias = checkpoint[layer_name + '.bias'] + return tf.nn.conv2d(x, kernel, strides=strides, padding=padding) + bias + + +def _resblock_tf(checkpoint, x, layer_name): + """Apply a residual block of the mask encoder.""" + original_x = x + x = _conv_tf( + checkpoint, x, padding='SAME', strides=1, layer_name=layer_name + '.0' + ) + x = tf.nn.relu(x) + x = _conv_tf( + checkpoint, x, padding='SAME', strides=1, layer_name=layer_name + '.2' + ) + x = tf.nn.relu(x) + x = _conv_tf( + checkpoint, x, padding='SAME', strides=1, layer_name=layer_name + '.4' + ) + return x + original_x + + +def _get_codebook_indices(checkpoint, encoder_output): + embeddings = checkpoint['_vq_vae._embedding'] + flat_input = tf.reshape(encoder_output, [-1, embeddings.shape[1]]) + distances = ( + tf.reduce_sum(flat_input**2, axis=1, keepdims=True) + + tf.reduce_sum(embeddings**2, axis=1) + - 2 * tf.matmul(flat_input, embeddings.T) + ) + indices = tf.argmin(distances, axis=1) + return tf.reshape(indices, [-1, 16]) diff --git a/big_vision/pp/proj/paligemma/video.py b/big_vision/pp/proj/paligemma/video.py new file mode 100644 index 0000000000000000000000000000000000000000..d353d0c64faac0020a5fc3cf0ef29d71724ea766 --- /dev/null +++ b/big_vision/pp/proj/paligemma/video.py @@ -0,0 +1,103 @@ +# Copyright 2024 Big Vision 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. + +"""Preprocessing for videos.""" + +from big_vision.pp import utils +from big_vision.pp.registry import Registry + +import tensorflow as tf + + +@Registry.register('preprocess_ops.video_decode') +def video_decode(res): + """Preprocessing.""" + + def _pp_per_image(img): + # decode + return tf.image.resize(tf.io.decode_jpeg(img), (res, res)) + + def _pp(data): + images = data['episodic_images'] + # resize + images = tf.map_fn(_pp_per_image, images, fn_output_signature=tf.float32) + # rescale + images = 2 * (images / 255.) - 1.0 + data['image'] = images + return data + + return _pp + + +@Registry.register('preprocess_ops.video_ensure_shape') +def video_ensure_shape(key, shape): + """Preprocessing.""" + def _video_ensure_shape(data): + data[key] = tf.ensure_shape(data[key], shape) + return data + + return _video_ensure_shape + + +@Registry.register('preprocess_ops.video_replicate_img') +def video_replicate_img(replicas, num_frames): + """Ensure that for short videos, we have the correct number of frames. + + We replicate and select. + + Args: + replicas: num_replicas before selection. Should be less than num_frames. + num_frames: number of frames + + Returns: + _replicate_img: preprocessing function + """ + + def _replicate_img(data): + # visual analogies + query + image = data['image'] + image = tf.tile(image, [replicas, 1, 1, 1]) + data['image'] = image[:num_frames] + return data + + return _replicate_img + + +@Registry.register('preprocess_ops.video_choice') +@utils.InKeyOutKey() +def video_choice(empty_fallback=None): + """Randomly takes one entry out of a tensor after flattening.""" + + def _choice(x): + x = tf.reshape(x, (-1,)) # Ensure it's a 1D array + + # Append the fallback value so we gracefully handle empty cases. + x0 = tf.zeros(1, x.dtype) if empty_fallback is None else [empty_fallback] + x = tf.concat([x, x0], axis=0) + + num_choices = tf.maximum(tf.shape(x)[0] - 1, 1) # Don't sample x0. + return x[tf.random.uniform([], 0, num_choices, dtype=tf.int32)] + + return _choice + + +@Registry.register('preprocess_ops.stack_images') +def stack_images(inkeys=(), outkey='image'): + + def _pp(data): + images = tf.stack([data[inkey] for inkey in inkeys]) + data[outkey] = images + return data + + return _pp diff --git a/big_vision/pp/proj/paligemma/widgetcap.py b/big_vision/pp/proj/paligemma/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..bfd7b82b977871abbabed42306f73ba13ad38df5 --- /dev/null +++ b/big_vision/pp/proj/paligemma/widgetcap.py @@ -0,0 +1,36 @@ +# Copyright 2024 Big Vision 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. + +"""Widgetcap pp ops.""" + +from big_vision.pp.registry import Registry +import tensorflow as tf + + +@Registry.register("preprocess_ops.draw_bbox") +def get_draw_bbox(image_key="image", bbox_key="bbox"): + """Draw a single bounding box.""" + + def _draw_bbox(data): + """Draw a single bounding box.""" + image = tf.cast(data[image_key], tf.float32) + image = tf.image.draw_bounding_boxes( + tf.expand_dims(image, 0), + tf.reshape(data[bbox_key], [1, 1, 4]), + tf.constant([255, 0, 0], dtype=tf.float32, shape=[1, 3]), + ) + data[image_key] = tf.squeeze(image) + return data + + return _draw_bbox diff --git a/big_vision/pp/proj/uvim/pp_ops.py b/big_vision/pp/proj/uvim/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..929de6e1da161fe450a477cc69c3a42677350373 --- /dev/null +++ b/big_vision/pp/proj/uvim/pp_ops.py @@ -0,0 +1,206 @@ +# Copyright 2022 Big Vision 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. + +"""Preprocessing ops.""" +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import numpy as np +import tensorflow as tf + + +@Registry.register("preprocess_ops.rgb_to_grayscale_to_rgb") +@utils.InKeyOutKey(indefault="image", outdefault="image") +def get_rgb_to_grayscale_to_rgb(): + def _rgb_to_grayscale_to_rgb(image): + return tf.image.grayscale_to_rgb(tf.image.rgb_to_grayscale(image)) + return _rgb_to_grayscale_to_rgb + + +@Registry.register("preprocess_ops.nyu_eval_crop") +def get_nyu_eval_crop(): + """Crops labels and image to valid eval area.""" + # crop_h = slice(45, 471) + # crop_w = slice(41, 601) + crop_h_start = 54 + crop_h_size = 426 + crop_w_start = 41 + crop_w_size = 560 + + def _pp(data): + tf.debugging.assert_equal(tf.shape(data["labels"]), (480, 640, 1)) + tf.debugging.assert_equal(tf.shape(data["image"]), (480, 640, 3)) + data["labels"] = tf.slice(data["labels"], + [crop_h_start, crop_w_start, 0], + [crop_h_size, crop_w_size, -1]) + data["image"] = tf.slice(data["image"], + [crop_h_start, crop_w_start, 0], + [crop_h_size, crop_w_size, -1]) + return data + return _pp + + +@Registry.register("preprocess_ops.nyu_depth") +@utils.InKeyOutKey(indefault="depth", outdefault="labels") +def get_nyu_depth(): + """Preprocesses NYU depth data.""" + def _pp(depth): + return tf.expand_dims(tf.cast(depth, tf.float32), -1) + return _pp + + +@Registry.register("preprocess_ops.coco_panoptic") +def get_coco_panoptic_pp(): + """COCO-panoptic: produces a mask with labels and a mask with instance ids. + + Instance channel will have values between 1 and N, and -1 for non-annotated + pixels. + + Returns: + COCO panoptic preprocessign op. + """ + def _coco_panoptic(data): + instance_ids = tf.cast(data["panoptic_objects"]["id"], tf.int32) + instance_labels = tf.cast(data["panoptic_objects"]["label"], tf.int32) + + # Convert image with ids split in 3 channels into a an integer id. + id_mask = tf.einsum( + "hwc,c->hw", + tf.cast(data["panoptic_image"], tf.int32), + tf.constant([1, 256, 256**2], tf.int32)) + + # Broadcast into N boolean masks one per instance_id. + n_masks = tf.cast( + id_mask[:, :, None] == instance_ids[None, None, :], tf.int32) + + # Merge into a semantic and an instance id mask. + # Note: pixels which do not belong to any mask, will have value=-1 + # which creates an empty one_hot masks. + # Number instances starting at 1 (0 is treated specially by make_canonical). + instance_idx = tf.range(tf.shape(instance_ids)[-1]) + instances = tf.einsum("hwc,c->hw", n_masks, instance_idx + 1) + semantics = tf.einsum("hwc,c->hw", n_masks, instance_labels + 1) + + data["instances"] = instances[:, :, None] + data["semantics"] = semantics[:, :, None] + return data + + return _coco_panoptic + + +@Registry.register("preprocess_ops.make_canonical") +@utils.InKeyOutKey(indefault="labels", outdefault="labels") +def get_make_canonical(random=False, main_sort_axis="y"): + """Makes id mask ordered from left to right based on the center of mass.""" + # By convention, instances are in the last channel. + def _make_canonical(image): + """Op.""" + instimg = image[..., -1] + + # Compute binary instance masks. Note, we do not touch 0 and neg. ids. + ids = tf.unique(tf.reshape(instimg, [-1])).y + ids = ids[ids > 0] + n_masks = tf.cast( + instimg[None, :, :] == ids[:, None, None], tf.int32) + + if not random: + f = lambda x: tf.reduce_mean(tf.cast(tf.where(x), tf.float32), axis=0) + centers = tf.map_fn(f, tf.cast(n_masks, tf.int64), dtype=tf.float32) + centers = tf.reshape(centers, (tf.shape(centers)[0], 2)) + major = {"y": 0, "x": 1}[main_sort_axis] + perm = tf.argsort( + centers[:, 1 - major] + + tf.cast(tf.shape(instimg)[major], tf.float32) * centers[:, major]) + n_masks = tf.gather(n_masks, perm) + else: + n_masks = tf.random.shuffle(n_masks) + + idx = tf.range(tf.shape(ids)[0]) + can_mask = tf.einsum("chw,c->hw", n_masks, idx + 2) - 1 + # Now, all 0 and neg. ids have collapsed to -1. Thus, we recover 0 id from + # the original mask. + can_mask = tf.where(instimg == 0, 0, can_mask) + return tf.concat([image[..., :-1], can_mask[..., None]], axis=-1) + + return _make_canonical + + +@Registry.register("preprocess_ops.inception_box") +def get_inception_box( + *, area=(0.05, 1.0), aspect=(0.75, 1.33), min_obj_cover=0.0, + outkey="box", inkey="image"): + """Creates an inception style bounding box which can be used to crop.""" + def _inception_box(data): + _, _, box = tf.image.sample_distorted_bounding_box( + tf.shape(data[inkey]), + area_range=area, + aspect_ratio_range=aspect, + min_object_covered=min_obj_cover, + bounding_boxes=(data["objects"]["bbox"][None, :, :] + if min_obj_cover else tf.zeros([0, 0, 4])), + use_image_if_no_bounding_boxes=True) + # bbox is [[[y0,x0,y1,x1]]] + data[outkey] = (box[0, 0, :2], box[0, 0, 2:] - box[0, 0, :2]) + return data + return _inception_box + + +@Registry.register("preprocess_ops.crop_box") +@utils.InKeyOutKey(with_data=True) +def get_crop_box(*, boxkey="box"): + """Crops an image according to bounding box in `boxkey`.""" + def _crop_box(image, data): + shape = tf.shape(image)[:-1] + begin, size = data[boxkey] + begin = tf.cast(begin * tf.cast(shape, tf.float32), tf.int32) + size = tf.cast(size * tf.cast(shape, tf.float32), tf.int32) + begin = tf.concat([begin, tf.constant((0,))], axis=0) + size = tf.concat([size, tf.constant((-1,))], axis=0) + crop = tf.slice(image, begin, size) + # Unfortunately, the above operation loses the depth-dimension. So we need + # to restore it the manual way. + crop.set_shape([None, None, image.shape[-1]]) + return crop + return _crop_box + + +@Registry.register("preprocess_ops.randu") +def get_randu(key): + """Creates a random uniform float [0, 1) in `key`.""" + def _randu(data): + data[key] = tf.random.uniform([]) + return data + return _randu + + +@Registry.register("preprocess_ops.det_fliplr") +@utils.InKeyOutKey(with_data=True) +def get_det_fliplr(*, randkey="fliplr"): + """Flips an image horizontally based on `randkey`.""" + # NOTE: we could unify this with regular flip when randkey=None. + def _det_fliplr(orig_image, data): + flip_image = tf.image.flip_left_right(orig_image) + flip = tf.cast(data[randkey] > 0.5, orig_image.dtype) + return flip_image * flip + orig_image * (1 - flip) + return _det_fliplr + + +@Registry.register("preprocess_ops.strong_hash") +@utils.InKeyOutKey(indefault="tfds_id", outdefault="tfds_id") +def get_strong_hash(): + """Preprocessing that hashes a string.""" + def _strong_hash(string): + return tf.strings.to_hash_bucket_strong( + string, + np.iinfo(int).max, [3714561454027272724, 8800639020734831960]) + return _strong_hash diff --git a/big_vision/pp/proj/uvim/pp_ops_test.py b/big_vision/pp/proj/uvim/pp_ops_test.py new file mode 100644 index 0000000000000000000000000000000000000000..78c065e372788ee77f03c53aadb444614a391ebc --- /dev/null +++ b/big_vision/pp/proj/uvim/pp_ops_test.py @@ -0,0 +1,128 @@ +# Copyright 2022 Big Vision 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. + +"""Tests for pp_ops.""" +import copy + +from big_vision.pp.proj.uvim import pp_ops as pp +import numpy as np +import tensorflow as tf + + +def get_image_data(dtype=tf.uint8): + img = tf.random.uniform((640, 320, 3), 0, 255, tf.int32) # Can't ask uint8!? + return {"image": tf.cast(img, dtype)} + + +class PreprocessOpsTest(tf.test.TestCase): + + def tfrun(self, ppfn, data={}): # pylint: disable=dangerous-default-value + # Run once as standalone, as could happen eg in colab. + yield {k: np.array(v) for k, v in ppfn(copy.deepcopy(data)).items()} + + if not data: # tf.data doesn't like completely empty dict...k + data = {"dummy": 0.0} + + # And then once again as part of tfdata pipeline. + # You'd be surprised how much these two differ! + tfdata = tf.data.Dataset.from_tensors(copy.deepcopy(data)) + for npdata in tfdata.map(ppfn).as_numpy_iterator(): + yield npdata + + def test_randu(self): + for output in self.tfrun(pp.get_randu("flip")): + self.assertEqual(output["flip"].shape, ()) + self.assertAllGreaterEqual(output["flip"], 0.0) + self.assertAllLessEqual(output["flip"], 1.0) + + def test_det_flip_lr(self): + # Test both dtypes to make it can be applied correctly to both. + for dtype in [tf.uint8, tf.float32]: + image_data = get_image_data(dtype) + for out in self.tfrun(pp.get_det_fliplr(randkey="rand"), + {"rand": 0.1, **image_data}): + self.assertTrue(np.all(image_data["image"] == out["image"])) + self.assertEqual(out["image"].dtype, dtype) + for out in self.tfrun(pp.get_det_fliplr(randkey="rand"), + {"rand": 0.6, **image_data}): + self.assertTrue(np.all(image_data["image"][:, ::-1, :] == out["image"])) + self.assertEqual(out["image"].dtype, dtype) + + def test_inception_box(self): + for out in self.tfrun(pp.get_inception_box(), get_image_data()): + self.assertEqual(out["box"][0].shape, (2,)) + self.assertEqual(out["box"][1].shape, (2,)) + + def test_crop_box(self): + data = get_image_data() + data["box"] = (tf.constant([0.5, 0.4]), tf.constant([0.25, 0.3])) + for out in self.tfrun(pp.get_crop_box(), data): + self.assertEqual(out["image"].shape, (160, 96, 3)) + self.assertAllEqual( + data["image"][320:320 + 160, 128:128 + 96], + out["image"]) + + def test_make_canonical(self): + orig = np.array([ + [1, 0, 3, 3, -1], + [1, 0, 3, 3, -1], + [1, 0, 2, 2, 2], + [1, 0, 0, -1, -1] + ], np.int32)[:, :, None] + expected = np.array([ + [2, 0, 1, 1, -1], + [2, 0, 1, 1, -1], + [2, 0, 3, 3, 3], + [2, 0, 0, -1, -1] + ], np.int32)[:, :, None] + for out in self.tfrun(pp.get_make_canonical(), {"labels": orig}): + self.assertTrue(np.all(out["labels"] == expected)) + + # Test it only affects last channel. + for out in self.tfrun(pp.get_make_canonical(), + {"labels": tf.tile(orig, (1, 1, 3))}): + self.assertAllEqual(out["labels"][..., 0], orig[..., 0]) + self.assertAllEqual(out["labels"][..., 1], orig[..., 0]) + self.assertAllEqual(out["labels"][..., 2], expected[..., 0]) + + def test_nyu_depth(self): + image = tf.zeros((5, 7, 3), dtype=tf.uint8) + depth = tf.zeros((5, 7), dtype=tf.float16) + data = { + "image": image, + "depth": depth + } + output = pp.get_nyu_depth()(data) + self.assertEqual(output["image"].shape, (5, 7, 3)) + self.assertEqual(output["image"].dtype, tf.uint8) + self.assertEqual(output["labels"].shape, (5, 7, 1)) + self.assertEqual(output["labels"].dtype, tf.float32) + + def test_nyu_eval_crop(self): + image = tf.zeros((480, 640, 3), dtype=tf.uint8) + depth = tf.zeros((480, 640), dtype=tf.float16) + data = { + "image": image, + "depth": depth + } + data = pp.get_nyu_depth()(data) + output = pp.get_nyu_eval_crop()(data) + self.assertEqual(output["image"].shape, (426, 560, 3)) + self.assertEqual(output["image"].dtype, tf.uint8) + self.assertEqual(output["labels"].shape, (426, 560, 1)) + self.assertEqual(output["labels"].dtype, tf.float32) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/pp/registry.py b/big_vision/pp/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..f5c7d996d756be16ba68a5fcb143f23129e1249d --- /dev/null +++ b/big_vision/pp/registry.py @@ -0,0 +1,163 @@ +# Copyright 2024 Big Vision 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. + +"""Global Registry for big_vision pp ops. + +Author: Joan Puigcerver (jpuigcerver@) +""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +import ast +import contextlib +import functools + + +def parse_name(string_to_parse): + """Parses input to the registry's lookup function. + + Args: + string_to_parse: can be either an arbitrary name or function call + (optionally with positional and keyword arguments). + e.g. "multiclass", "resnet50_v2(filters_factor=8)". + + Returns: + A tuple of input name, argument tuple and a keyword argument dictionary. + Examples: + "multiclass" -> ("multiclass", (), {}) + "resnet50_v2(9, filters_factor=4)" -> + ("resnet50_v2", (9,), {"filters_factor": 4}) + + Author: Joan Puigcerver (jpuigcerver@) + """ + expr = ast.parse(string_to_parse, mode="eval").body # pytype: disable=attribute-error + if not isinstance(expr, (ast.Attribute, ast.Call, ast.Name)): + raise ValueError( + "The given string should be a name or a call, but a {} was parsed from " + "the string {!r}".format(type(expr), string_to_parse)) + + # Notes: + # name="some_name" -> type(expr) = ast.Name + # name="module.some_name" -> type(expr) = ast.Attribute + # name="some_name()" -> type(expr) = ast.Call + # name="module.some_name()" -> type(expr) = ast.Call + + if isinstance(expr, ast.Name): + return string_to_parse, (), {} + elif isinstance(expr, ast.Attribute): + return string_to_parse, (), {} + + def _get_func_name(expr): + if isinstance(expr, ast.Attribute): + return _get_func_name(expr.value) + "." + expr.attr + elif isinstance(expr, ast.Name): + return expr.id + else: + raise ValueError( + "Type {!r} is not supported in a function name, the string to parse " + "was {!r}".format(type(expr), string_to_parse)) + + def _get_func_args_and_kwargs(call): + args = tuple([ast.literal_eval(arg) for arg in call.args]) + kwargs = { + kwarg.arg: ast.literal_eval(kwarg.value) for kwarg in call.keywords + } + return args, kwargs + + func_name = _get_func_name(expr.func) + func_args, func_kwargs = _get_func_args_and_kwargs(expr) + + return func_name, func_args, func_kwargs + + +class Registry(object): + """Implements global Registry. + + Authors: Joan Puigcerver (jpuigcerver@), Alexander Kolesnikov (akolesnikov@) + """ + + _GLOBAL_REGISTRY = {} + + @staticmethod + def global_registry(): + return Registry._GLOBAL_REGISTRY + + @staticmethod + def register(name, replace=False): + """Creates a function that registers its input.""" + + def _register(item): + if name in Registry.global_registry() and not replace: + raise KeyError("The name {!r} was already registered.".format(name)) + + Registry.global_registry()[name] = item + return item + + return _register + + @staticmethod + def lookup(lookup_string, kwargs_extra=None): + """Lookup a name in the registry.""" + + try: + name, args, kwargs = parse_name(lookup_string) + except ValueError as e: + raise ValueError(f"Error parsing:\n{lookup_string}") from e + if kwargs_extra: + kwargs.update(kwargs_extra) + item = Registry.global_registry()[name] + return functools.partial(item, *args, **kwargs) + + @staticmethod + def knows(lookup_string): + try: + name, _, _ = parse_name(lookup_string) + except ValueError as e: + raise ValueError(f"Error parsing:\n{lookup_string}") from e + return name in Registry.global_registry() + + +@contextlib.contextmanager +def temporary_ops(**kw): + """Registers specified pp ops for use in a `with` block. + + Example use: + + with pp_registry.remporary_ops( + pow=lambda alpha: lambda d: {k: v**alpha for k, v in d.items()}): + pp = pp_builder.get_preprocess_fn("pow(alpha=2.0)|pow(alpha=0.5)") + features = pp(features) + + Args: + **kw: Names are preprocess string function names to be used to specify the + preprocess function. Values are functions that can be called with params + (e.g. the `alpha` param in above example) and return functions to be used + to transform features. + + Yields: + A context manager to be used in a `with` statement. + """ + reg = Registry.global_registry() + kw = {f"preprocess_ops.{k}": v for k, v in kw.items()} + for k in kw: + assert k not in reg + for k, v in kw.items(): + reg[k] = v + try: + yield + finally: + for k in kw: + del reg[k] diff --git a/big_vision/pp/registry_test.py b/big_vision/pp/registry_test.py new file mode 100644 index 0000000000000000000000000000000000000000..2296e7de91ce0495bade59e8e65417384507e58e --- /dev/null +++ b/big_vision/pp/registry_test.py @@ -0,0 +1,128 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for registry.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from unittest import mock + +from absl.testing import absltest +from big_vision.pp import registry + + +class RegistryTest(absltest.TestCase): + + def setUp(self): + super(RegistryTest, self).setUp() + # Mock global registry in each test to keep them isolated and allow for + # concurrent tests. + self.addCleanup(mock.patch.stopall) + self.global_registry = dict() + self.mocked_method = mock.patch.object( + registry.Registry, "global_registry", + return_value=self.global_registry).start() + + def test_parse_name(self): + name, args, kwargs = registry.parse_name("f") + self.assertEqual(name, "f") + self.assertEqual(args, ()) + self.assertEqual(kwargs, {}) + + name, args, kwargs = registry.parse_name("f()") + self.assertEqual(name, "f") + self.assertEqual(args, ()) + self.assertEqual(kwargs, {}) + + name, args, kwargs = registry.parse_name("func(a=0,b=1,c='s')") + self.assertEqual(name, "func") + self.assertEqual(args, ()) + self.assertEqual(kwargs, {"a": 0, "b": 1, "c": "s"}) + + name, args, kwargs = registry.parse_name("func(1,'foo',3)") + self.assertEqual(name, "func") + self.assertEqual(args, (1, "foo", 3)) + self.assertEqual(kwargs, {}) + + name, args, kwargs = registry.parse_name("func(1,'2',a=3,foo='bar')") + self.assertEqual(name, "func") + self.assertEqual(args, (1, "2")) + self.assertEqual(kwargs, {"a": 3, "foo": "bar"}) + + name, args, kwargs = registry.parse_name("foo.bar.func(a=0,b=(1),c='s')") + self.assertEqual(name, "foo.bar.func") + self.assertEqual(kwargs, dict(a=0, b=1, c="s")) + + with self.assertRaises(SyntaxError): + registry.parse_name("func(0") + with self.assertRaises(SyntaxError): + registry.parse_name("func(a=0,,b=0)") + with self.assertRaises(SyntaxError): + registry.parse_name("func(a=0,b==1,c='s')") + with self.assertRaises(ValueError): + registry.parse_name("func(a=0,b=undefined_name,c='s')") + + def test_register(self): + # pylint: disable=unused-variable + @registry.Registry.register("func1") + def func1(): + pass + + self.assertLen(registry.Registry.global_registry(), 1) + + def test_lookup_function(self): + + @registry.Registry.register("func1") + def func1(arg1, arg2, arg3): # pylint: disable=unused-variable + return arg1, arg2, arg3 + + self.assertTrue(callable(registry.Registry.lookup("func1"))) + self.assertEqual(registry.Registry.lookup("func1")(1, 2, 3), (1, 2, 3)) + self.assertEqual( + registry.Registry.lookup("func1(arg3=9)")(1, 2), (1, 2, 9)) + self.assertEqual( + registry.Registry.lookup("func1(arg2=9,arg1=99)")(arg3=3), (99, 9, 3)) + self.assertEqual( + registry.Registry.lookup("func1(arg2=9,arg1=99)")(arg1=1, arg3=3), + (1, 9, 3)) + + self.assertEqual( + registry.Registry.lookup("func1(1)")(1, 2), (1, 1, 2)) + self.assertEqual( + registry.Registry.lookup("func1(1)")(arg3=3, arg2=2), (1, 2, 3)) + self.assertEqual( + registry.Registry.lookup("func1(1, 2)")(3), (1, 2, 3)) + self.assertEqual( + registry.Registry.lookup("func1(1, 2)")(arg3=3), (1, 2, 3)) + self.assertEqual( + registry.Registry.lookup("func1(1, arg2=2)")(arg3=3), (1, 2, 3)) + self.assertEqual( + registry.Registry.lookup("func1(1, arg3=2)")(arg2=3), (1, 3, 2)) + self.assertEqual( + registry.Registry.lookup("func1(1, arg3=2)")(3), (1, 3, 2)) + + with self.assertRaises(TypeError): + registry.Registry.lookup("func1(1, arg2=2)")(3) + with self.assertRaises(TypeError): + registry.Registry.lookup("func1(1, arg3=3)")(arg3=3) + with self.assertRaises(TypeError): + registry.Registry.lookup("func1(1, arg3=3)")(arg1=3) + with self.assertRaises(SyntaxError): + registry.Registry.lookup("func1(arg1=1, 3)")(arg2=3) + + +if __name__ == "__main__": + absltest.main() diff --git a/big_vision/pp/tokenizer.py b/big_vision/pp/tokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..681494e436aacd48d5d720e07d2df1a80c704eb2 --- /dev/null +++ b/big_vision/pp/tokenizer.py @@ -0,0 +1,103 @@ +# Copyright 2024 Big Vision 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. + +"""The tokenizer API for big_vision, and central registration place.""" +import functools +import importlib +from typing import Protocol + +from absl import logging +from big_vision.pp import registry +import big_vision.utils as u +import numpy as np + + +class Tokenizer(Protocol): + """Just to unify on the API as we now have mmany different ones.""" + + def to_int(self, text, *, bos=False, eos=False): + """Tokenizes `text` into a list of integer tokens. + + Args: + text: can be a single string, or a list of strings. + bos: Whether a beginning-of-sentence token should be prepended. + eos: Whether an end-of-sentence token should be appended. + + Returns: + List or list-of-list of tokens. + """ + + def to_int_tf_op(self, text, *, bos=False, eos=False): + """Same as `to_int()`, but as TF ops to be used in pp.""" + + def to_str(self, tokens, *, stop_at_eos=True): + """Inverse of `to_int()`. + + Args: + tokens: list of tokens, or list of lists of tokens. + stop_at_eos: remove everything that may come after the first EOS. + + Returns: + A string (if `tokens` is a list of tokens), or a list of strings. + Note that most tokenizers strip select few control tokens like + eos/bos/pad/unk from the output string. + """ + + def to_str_tf_op(self, tokens, *, stop_at_eos=True): + """Same as `to_str()`, but as TF ops to be used in pp.""" + + @property + def pad_token(self): + """Token id of padding token.""" + + @property + def eos_token(self): + """Token id of end-of-sentence token.""" + + @property + def bos_token(self): + """Token id of beginning-of-sentence token.""" + + @property + def vocab_size(self): + """Returns the size of the vocabulary.""" + + +@functools.cache +def get_tokenizer(name): + with u.chrono.log_timing(f"z/secs/tokenizer/{name}"): + if not registry.Registry.knows(f"tokenizers.{name}"): + raw_name, *_ = registry.parse_name(name) + logging.info("Tokenizer %s not registered, " + "trying import big_vision.pp.%s", name, raw_name) + importlib.import_module(f"big_vision.pp.{raw_name}") + + return registry.Registry.lookup(f"tokenizers.{name}")() + + +def get_extra_tokens(tokensets): + extra_tokens = [] + for tokenset in tokensets: + extra_tokens.extend(registry.Registry.lookup(f"tokensets.{tokenset}")()) + return list(np.unique(extra_tokens)) # Preserves order. Dups make no sense. + + +@registry.Registry.register("tokensets.loc") +def _get_loc1024(n=1024): + return [f"" for i in range(n)] + + +@registry.Registry.register("tokensets.seg") +def _get_seg(n=128): + return [f"" for i in range(n)] diff --git a/big_vision/pp/utils.py b/big_vision/pp/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee834560246549c71f0a6d9785694fd1507ca9b --- /dev/null +++ b/big_vision/pp/utils.py @@ -0,0 +1,53 @@ +# Copyright 2024 Big Vision 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. + +"""Preprocessing utils.""" + +from collections import abc + + +def maybe_repeat(arg, n_reps): + if not isinstance(arg, abc.Sequence) or isinstance(arg, str): + arg = (arg,) * n_reps + return arg + + +class InKeyOutKey(object): + """Decorator for preprocessing ops, which adds `inkey` and `outkey` arguments. + + Note: Only supports single-input single-output ops. + """ + + def __init__(self, indefault="image", outdefault="image", with_data=False): + self.indefault = indefault + self.outdefault = outdefault + self.with_data = with_data + + def __call__(self, orig_get_pp_fn): + + def get_ikok_pp_fn(*args, key=None, + inkey=self.indefault, outkey=self.outdefault, **kw): + + orig_pp_fn = orig_get_pp_fn(*args, **kw) + def _ikok_pp_fn(data): + # Optionally allow the function to get the full data dict as aux input. + if self.with_data: + data[key or outkey] = orig_pp_fn(data[key or inkey], data=data) + else: + data[key or outkey] = orig_pp_fn(data[key or inkey]) + return data + + return _ikok_pp_fn + + return get_ikok_pp_fn diff --git a/big_vision/pp/utils_test.py b/big_vision/pp/utils_test.py new file mode 100644 index 0000000000000000000000000000000000000000..beec18cef62a9638ed143229d7aedc5e218a70b6 --- /dev/null +++ b/big_vision/pp/utils_test.py @@ -0,0 +1,53 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for preprocessing utils.""" + +from __future__ import absolute_import +from __future__ import division +from __future__ import print_function + +from big_vision.pp import utils +import tensorflow.compat.v1 as tf + + +class UtilsTest(tf.test.TestCase): + + def test_maybe_repeat(self): + self.assertEqual((1, 1, 1), utils.maybe_repeat(1, 3)) + self.assertEqual((1, 2), utils.maybe_repeat((1, 2), 2)) + self.assertEqual([1, 2], utils.maybe_repeat([1, 2], 2)) + + def test_inkeyoutkey(self): + @utils.InKeyOutKey() + def get_pp_fn(shift, scale=0): + def _pp_fn(x): + return scale * x + shift + return _pp_fn + + data = {"k_in": 2, "other": 3} + ppfn = get_pp_fn(1, 2, inkey="k_in", outkey="k_out") # pylint: disable=unexpected-keyword-arg + self.assertEqual({"k_in": 2, "k_out": 5, "other": 3}, ppfn(data)) + + data = {"k": 6, "other": 3} + ppfn = get_pp_fn(1, inkey="k", outkey="k") # pylint: disable=unexpected-keyword-arg + self.assertEqual({"k": 1, "other": 3}, ppfn(data)) + + data = {"other": 6, "image": 3} + ppfn = get_pp_fn(5, 2) + self.assertEqual({"other": 6, "image": 11}, ppfn(data)) + + +if __name__ == "__main__": + tf.test.main() diff --git a/big_vision/requirements.txt b/big_vision/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..f910f346b52191617afdc0cebb070cb6c735d38e --- /dev/null +++ b/big_vision/requirements.txt @@ -0,0 +1,18 @@ +numpy>=1.26 +absl-py +git+https://github.com/google/CommonLoopUtils +distrax +editdistance +einops +flax +optax +git+https://github.com/google/flaxformer +git+https://github.com/akolesnikoff/panopticapi.git@mute +overrides +protobuf +sentencepiece +tensorflow-cpu +tfds-nightly +tensorflow-text +tensorflow-gan +pycocoevalcap diff --git a/big_vision/run_tpu.sh b/big_vision/run_tpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..3c3da2e44e7d2829a00188f5e6177ea9d6e3ba4d --- /dev/null +++ b/big_vision/run_tpu.sh @@ -0,0 +1,35 @@ +# Copyright 2024 Big Vision 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. + +#!/bin/bash + +if [ ! -d "bv_venv" ] +then + sudo apt-get update + sudo apt install -y python3-venv + python3 -m venv bv_venv + . bv_venv/bin/activate + + pip install -U pip # Yes, really needed. + # NOTE: doesn't work when in requirements.txt -> cyclic dep + pip install "jax[tpu]>=0.4.25" -f https://storage.googleapis.com/jax-releases/libtpu_releases.html + pip install -r big_vision/requirements.txt +else + . bv_venv/bin/activate +fi + +if [ $# -ne 0 ] +then + env TFDS_DATA_DIR=$TFDS_DATA_DIR BV_JAX_INIT=1 python3 -m "$@" +fi diff --git a/big_vision/sharding.py b/big_vision/sharding.py new file mode 100644 index 0000000000000000000000000000000000000000..be76cb3a1f6b8bc0e494515bac2a54528a53494c --- /dev/null +++ b/big_vision/sharding.py @@ -0,0 +1,197 @@ +# Copyright 2024 Big Vision 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. + +"""Big vision sharding utilities.""" + +from absl import logging + +from big_vision.pp.registry import Registry +import big_vision.utils as u +import flax.linen as nn +import jax +import numpy as np + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def _replicated(mesh): + return NamedSharding(mesh, P()) + + +def _shard_along_axis(mesh, i, axis_name): + return NamedSharding(mesh, P(*((None,) * i + (axis_name,)))) + + +def infer_sharding(params, strategy, mesh): + """Infers `params` sharding based on strategy. + + Args: + params: a pytree of arrays. + strategy: sharding strategy. + mesh: jax device mesh. + + Returns: + A pytree with shardings, that has the same shape as the `tree` argument. + """ + patterns, tactics = zip(*strategy) + + x_with_names, tree_def = u.tree_flatten_with_names(params) + names = tree_def.unflatten(list(zip(*x_with_names))[0]) + + # Follows big_vision conventions: each variable is matched at most once, + # early patterns get matching priority. + mask_trees = u.make_mask_trees(params, patterns) + + specs = jax.tree.map(lambda x: (None,) * x.ndim, params) + + for mask_tree, tactic in zip(mask_trees, tactics): + for op_str in tactic.split("|"): + op = Registry.lookup(f"shardings.{op_str}")() + specs = jax.tree.map( + lambda x, n, match, spec, op=op: op(spec, mesh, n, x) + if match else spec, + params, names, mask_tree, specs, + is_leaf=lambda v: isinstance(v, nn.Partitioned)) + + # Two-level tree_map to prevent it from doing traversal inside the spec. + specs = jax.tree.map(lambda _, spec: P(*spec), nn.unbox(params), specs) + return jax.tree.map(lambda spec: NamedSharding(mesh, spec), specs) + + +# Sharding rules +# +# Each rule needs to be added to the registry, can accept custom args, and +# returns a function that updates the current spec. The arguments are: +# 1. Variable name +# 2. Variable itself (or placeholder with .shape and .dtype properties) +# 3. The current sharing spec. + + +@Registry.register("shardings.replicate") +def replicate(): + """Full replication sharding rule. + + Note full replication is deafult, so this can be skipped and useful to + explicitly state in the config that certrain parameters are replicated. + TODO: can be generalized to support replication over a sub-mesh. + + Returns: + A function that updates the sharding spec. + """ + def _update_spec(cur_spec, mesh, name, x): + del x, mesh + if not all(axis is None for axis in cur_spec): + raise ValueError(f"Inconsistent sharding instructions: " + f"parameter {name} has spec {cur_spec}, " + f"so it can't be fully replicated.") + return cur_spec + return _update_spec + + +@Registry.register("shardings.fsdp") +def fsdp(axis, min_size_to_shard_mb=4): + """FSDP sharding rule. + + Shards the largest dimension that is not sharded already and is divisible + by the total device count. + + Args: + axis: mesh axis name for FSDP, or a collection of names. + min_size_to_shard_mb: minimal tensor size to bother with sharding. + + Returns: + A function that updates the sharding spec. + """ + axis = axis if isinstance(axis, str) else tuple(axis) + axis_tuple = axis if isinstance(axis, tuple) else (axis,) + def _update_spec(cur_spec, mesh, name, x): + shape = x.shape + axis_size = np.prod([mesh.shape[a] for a in axis_tuple]) + + if np.prod(shape) * x.dtype.itemsize <= min_size_to_shard_mb * (2 ** 20): + return cur_spec + + # Partition along largest axis that is divisible and not taken. + idx = np.argsort(shape)[::-1] + for i in idx: + if shape[i] % axis_size == 0: + if cur_spec[i] is None: + return cur_spec[:i] + (axis,) + cur_spec[i+1:] + + logging.info("Failed to apply `fsdp` rule to the parameter %s:%s, as all " + "its dimensions are not divisible by the requested axis: " + "%s:%i, or already occupied by other sharding rules: %s", + name, shape, axis, axis_size, cur_spec) + return cur_spec + return _update_spec + + +@Registry.register("shardings.logical_partitioning") +def logical_partitioning(): + """Manual sharding based on Flax's logical partitioning annotations. + + Uses logical sharding annotations added in model code with + `nn.with_logical_partitioning`. Respects logical to mesh name mapping rules + (typically defined in the dynamic context using + `with nn.logical_axis_rules(rules): ...`). + + Returns: + A function that outputs the sharding spec of `nn.LogicallyPartitioned` boxed + specs. + """ + def _update_spec(cur_spec, mesh, name, x): + del x, name, mesh + if isinstance(cur_spec, nn.LogicallyPartitioned): + return nn.logical_to_mesh_axes(cur_spec.names) + return cur_spec + return _update_spec + + +@Registry.register("shardings.shard_dim") +def shard_dim(axis, dim, ignore_ndim_error=False): + """Shards the given dimension along the given axis. + + Args: + axis: mesh axis name for sharding. + dim: dimension to shard (can be negative). + ignore_ndim_error: if True, a warning error is logged instead of raising an + exception when the given dimension is not compatible with the number of + dimensions of the array. + + Returns: + A function that updates the sharding spec. + """ + def _update_spec(cur_spec, mesh, name, x): + del mesh, x + if np.abs(dim) >= len(cur_spec): + msg = f"Cannot shard_dim({axis}, {dim}): name={name} cur_spec={cur_spec}" + if ignore_ndim_error: + logging.warning(msg) + return cur_spec + else: + raise ValueError(msg) + pos_dim = dim + if pos_dim < 0: + pos_dim += len(cur_spec) + if cur_spec[pos_dim] is not None: + raise ValueError( + f"Already sharded: shard_dim({axis}, {dim}):" + f" name={name} cur_spec={cur_spec}" + ) + new_spec = cur_spec[:pos_dim] + (axis,) + cur_spec[pos_dim + 1 :] + return new_spec + + return _update_spec diff --git a/big_vision/tools/download_tfds_datasets.py b/big_vision/tools/download_tfds_datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..b64c33d51a7cb8df063d15e828e30b07007ff6b0 --- /dev/null +++ b/big_vision/tools/download_tfds_datasets.py @@ -0,0 +1,44 @@ +# Copyright 2024 Big Vision 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. + +"""Download and prepare TFDS datasets for the big_vision codebase. + +This python script covers cifar10, cifar100, oxford_iiit_pet +and oxford_flowers10. + +If you want to integrate other public or custom datasets, please follow: +https://www.tensorflow.org/datasets/catalog/overview +""" + +from absl import app +import tensorflow_datasets as tfds + + +def main(argv): + if len(argv) > 1 and "download_tfds_datasets.py" in argv[0]: + datasets = argv[1:] + else: + datasets = [ + "cifar10", + "cifar100", + "oxford_iiit_pet", + "oxford_flowers102", + "imagenet_v2", + ] + for d in datasets: + tfds.load(name=d, download=True) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/tools/eval_only.py b/big_vision/tools/eval_only.py new file mode 100644 index 0000000000000000000000000000000000000000..abdde4a6c0aa656a2e8ec76ce645982a2a6723b3 --- /dev/null +++ b/big_vision/tools/eval_only.py @@ -0,0 +1,146 @@ +# Copyright 2024 Big Vision 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. + +"""Script that loads a model and only runs evaluators.""" + +from functools import partial +import importlib + +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.utils as u +from clu import parameter_overview +import flax +import flax.jax_utils as flax_utils +import jax +import jax.numpy as jnp +from ml_collections import config_flags +from tensorflow.io import gfile + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def main(argv): + del argv + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info("Workdir: %s", workdir) + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image"]): + importlib.import_module(f"big_vision.pp.{m}") + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + def write_note(note): + if jax.process_index() == 0: + logging.info("NOTE: %s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + u.chrono.inform(measure=mw.measure, write_note=write_note) + + write_note(f"Initializing {config.model_name} model...") + assert config.get("model.reinit") is None, ( + "I don't think you want any part of the model to be re-initialized.") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model_kw = dict(config.get("model", {})) + if "num_classes" in config: # Make it work for regular + image_text. + model_kw["num_classes"] = config.num_classes + model = model_mod.Model(**model_kw) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @partial(jax.jit, backend="cpu") + def init(rng): + input_shapes = config.get("init_shapes", [(1, 224, 224, 3)]) + input_types = config.get("init_types", [jnp.float32] * len(input_shapes)) + dummy_inputs = [jnp.zeros(s, t) for s, t in zip(input_shapes, input_types)] + things = flax.core.unfreeze(model.init(rng, *dummy_inputs)) + return things.get("params", {}) + + with u.chrono.log_timing("z/secs/init"): + params_cpu = init(jax.random.PRNGKey(42)) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + num_params = sum(p.size for p in jax.tree.leaves(params_cpu)) + mw.measure("num_params", num_params) + + # The use-case for not loading an init is testing and debugging. + if config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu = model_mod.load( + params_cpu, config.model_init, config.get("model"), + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview(params_cpu, msg="loaded params") + + write_note("Replicating...") + params_repl = flax_utils.replicate(params_cpu) + + def predict_fn(params, *a, **kw): + return model.apply({"params": params}, *a, **kw) + + evaluators = eval_common.from_config( + config, {"predict": predict_fn, "model": model}, + lambda s: write_note(f"Initializing evaluator: {s}..."), + lambda key, cfg: 1, # Ignore log_steps, always run. + ) + + # Allow running for multiple steps can be useful for couple cases: + # 1. non-deterministic evaluators + # 2. warmup when timing evaluators (eg compile cache etc). + for s in range(config.get("eval_repeats", 1)): + mw.step_start(s) + for (name, evaluator, _, prefix) in evaluators: + write_note(f"{name} evaluation step {s}...") + with u.profile(name, noop=name in config.get("no_profile", [])): + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + u.sync() # sync barrier to get correct measurements + u.chrono.flush_timings() + mw.step_end() + + write_note("Done!") + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + if workdir and flags.FLAGS.cleanup and jax.process_index() == 0: + gfile.rmtree(workdir) + try: # Only need this on the last work-unit, if already empty. + gfile.remove(os.path.join(workdir, "..")) + except tf.errors.OpError: + pass + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/tools/lit_demo/README.md b/big_vision/tools/lit_demo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7c1825219944bbf8141eeea4b8bbca0bc1f6738 --- /dev/null +++ b/big_vision/tools/lit_demo/README.md @@ -0,0 +1,26 @@ +# LiT-Demo + +See https://blog.tensorflow.org/2022/08/jax-on-web-with-tensorflowjs.html + +Demo originally appeared on Twitter +https://twitter.com/AndreasPSteiner/status/1514722383818543106 + +App published at +https://google-research.github.io/vision_transformer/lit + +## Build + +Install packages (tested with node v16.17.0 and yarn 1.22.19) + +```bash +yarn +``` + + +## Run + +The web app will appear on http://localhost:8000 + +``` +node build.js +``` diff --git a/big_vision/tools/lit_demo/build.js b/big_vision/tools/lit_demo/build.js new file mode 100644 index 0000000000000000000000000000000000000000..a44aa8aa7b05c7ecb5acc4f7478a175897f15b9d --- /dev/null +++ b/big_vision/tools/lit_demo/build.js @@ -0,0 +1,39 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +const sassPlugin = require('esbuild-sass-plugin').sassPlugin; + +require('esbuild').serve({ + servedir: 'src', + port: 8000, +}, { + entryPoints: ['src/app.ts'], + bundle: true, + outfile: 'src/index.js', + plugins: [ + sassPlugin({ + filter: /style.scss$/, + type: 'style' + }), + sassPlugin({ + type: 'lit-css', + }), + ], + sourcemap: true, +}).then(() => { + console.log('Serving on port 8000'); +}).catch(() => process.exit(1)); diff --git a/big_vision/tools/lit_demo/package.json b/big_vision/tools/lit_demo/package.json new file mode 100644 index 0000000000000000000000000000000000000000..838cf1ff349f7496fc8edd43aa207b2c00174d20 --- /dev/null +++ b/big_vision/tools/lit_demo/package.json @@ -0,0 +1,54 @@ +{ + "name": "lit-demo", + "version": "0.0.2", + "description": "", + "main": "src/app.ts", + "license": "Apache-2.0", + "private": true, + "engines": { + "node": ">=8.9.0" + }, + "scripts": { + "serve": "node build.js", + "test": "ts-node --skip-ignore --project tsconfig.test.json run_tests.ts" + }, + "devDependencies": { + "@babel/core": "^7.7.5", + "@babel/plugin-transform-runtime": "^7.7.6", + "@babel/polyfill": "^7.10.4", + "@babel/preset-env": "^7.7.6", + "@tensorflow/tfjs-backend-cpu": "^3.15.0", + "@tensorflow/tfjs-backend-webgl": "^3.15.0", + "@tensorflow/tfjs-converter": "3.20.0", + "@tensorflow/tfjs-core": "3.20.0", + "babel-preset-env": "^1.7.0", + "esbuild": "^0.15.5", + "esbuild-sass-plugin": "^2.3.2", + "jasmine": "^3.3.1", + "lit": "^2.3.1", + "naughty-words": "^1.2.0", + "sass": "^1.50.0", + "ts-node": "~5.0.0", + "typescript": "4.1.3" + }, + "resolutions": { + "is-svg": "4.3.1" + }, + "eslintConfig": { + "extends": "google", + "rules": { + "require-jsdoc": 0, + "valid-jsdoc": 0 + }, + "env": { + "es6": true + }, + "parserOptions": { + "ecmaVersion": 8, + "sourceType": "module" + } + }, + "eslintIgnore": [ + "dist/" + ] +} diff --git a/big_vision/tools/lit_demo/src/app.ts b/big_vision/tools/lit_demo/src/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..3fccbc940cd173826dad6c6d25fd11a2c177ce16 --- /dev/null +++ b/big_vision/tools/lit_demo/src/app.ts @@ -0,0 +1,22 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +import {LitDemoApp} from './components/lit-demo-app'; +import './style.scss'; + +// tslint:disable-next-line:no-any +(window as any).LitDemoApp = LitDemoApp; diff --git a/big_vision/tools/lit_demo/src/components/image-carousel.scss b/big_vision/tools/lit_demo/src/components/image-carousel.scss new file mode 100644 index 0000000000000000000000000000000000000000..2da94515d9d3627611c4f3d2b6daf6762916e42d --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/image-carousel.scss @@ -0,0 +1,32 @@ +@import '../style/mixins'; + +.selector { + overflow: scroll; + padding-bottom: 10px; // OS X scroll bar + + .inner { + white-space: nowrap; + + .thumb { + display: inline-block; + + img { + cursor: pointer; + + width: 20vmin; + height: 20vmin; + max-width: 200px; + max-height: 200px; + + @include phone-portrait { + width: 33vmin; + height: 33vmin; + } + + margin: 10px; + + box-shadow: 0 0 10px #888; + } + } + } +} diff --git a/big_vision/tools/lit_demo/src/components/image-carousel.ts b/big_vision/tools/lit_demo/src/components/image-carousel.ts new file mode 100644 index 0000000000000000000000000000000000000000..b392d5b95a3a048deb370fa68cac460bf62f9be2 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/image-carousel.ts @@ -0,0 +1,70 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Carousel of images. + */ + +import {html, LitElement} from 'lit'; + +import {app} from '../lit_demo/app'; +import {getImageUrl} from '../lit_demo/constants'; +import {ImageRow} from '../lit_demo/data'; + +import {customElement} from 'lit/decorators.js'; +import styles from './image-carousel.scss'; + +/** + * Shows multiple images in a horizontal carousel. + * + * Dispatches `'image-select'` event when an image is clicked/tapped. + */ +@customElement('image-carousel') +export class ImageCarousel extends LitElement { + static override styles = [styles]; + + onClick(id: string) { + const event = + new CustomEvent('image-select', {composed: true, detail: {id}}); + this.dispatchEvent(event); + } + + override render() { + const images = app.imageData.rows.map( + (row: ImageRow) => html` +
+ { + this.onClick(row.id); + }} data-id=${row.id} src="${getImageUrl(row.id)}"> +
+ `); + return html` +
+
+ ${images} +
+
+

Select an image 👆 to get started.

+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'image-carousel': ImageCarousel; + } +} diff --git a/big_vision/tools/lit_demo/src/components/image-prompts.scss b/big_vision/tools/lit_demo/src/components/image-prompts.scss new file mode 100644 index 0000000000000000000000000000000000000000..66cd06817ee5d2f5e4af4c392052d357bc4e3456 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/image-prompts.scss @@ -0,0 +1,124 @@ +@import '../style/mixins'; + +.image-prompt { + display: flex; + gap: 1.5em; + align-items: flex-start; + margin-top: 2rem; + + @include phone-portrait { + align-items: center; + flex-direction: column; + gap: 0; + margin-bottom: 5rem; + } + + .left { + display: flex; + flex-direction: column; + + .wrapper { + position: relative; + + .src { + position: absolute; + right: 2rem; + bottom: 2rem; + color: white; + font-size: 1.5rem; + text-shadow: 2px 2px black; + text-decoration: none; + } + } + + .animation { + position: relative; + width: 224px; + height: 15px; + opacity: 0; + + .computing { + text-align: center; + } + } + } + + .right { + display: flex; + flex-grow: 1; + flex-direction: column; + gap: 0.5em; + + .top { + text-align: right; + height: 30px; + } + + .buttons { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 1em; + align-items: center; + } + + .item { + position: relative; + display: flex; + + .pct { + display: inline-block; + margin-right: 1em; + width: 3.5em; + text-align: right; + opacity: 0; + transition: opacity 0.5s; + } + + input { + flex-grow: 1; + max-width: 70vw; + border-radius: 0; + background: transparent; + border: 0; + border-bottom: 1px solid var(--text-fg); + color: var(--text-fg); + outline: none; + + &.toolong { + border-bottom: 1px solid var(--text-red); + color: var(--text-red); + } + } + + .bar { + position: absolute; + display: inline-block; + top: 5%; + left: 0; + z-index: -1; + background: var(--bar-col); + height: 90%; + width: 0; + transition: width 0.5s; + } + } + + .bottom { + display: flex; + flex-wrap: wrap; + justify-content: flex-end; + gap: 1em; + align-items: center; + opacity: 0; + + .tweet { + background: rgb(18, 150, 223); + color: white; + text-decoration: none; + padding: 0px 15px; + border-radius: 16px; + } + } + } +} diff --git a/big_vision/tools/lit_demo/src/components/image-prompts.ts b/big_vision/tools/lit_demo/src/components/image-prompts.ts new file mode 100644 index 0000000000000000000000000000000000000000..823f166518f5011d17d560b6b1e246060118863f --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/image-prompts.ts @@ -0,0 +1,250 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Image and text prompts. + */ + +import {html, LitElement} from 'lit'; +import * as naughtyWords from 'naughty-words'; + +import {app} from '../lit_demo/app'; +import {getBackend} from '../lit_demo/compute'; +import {getImageUrl} from '../lit_demo/constants'; +import {getUrl} from '../lit_demo/url_utils'; + +import {MessageList} from './message-list'; + +import {customElement, query} from 'lit/decorators.js'; +import styles from './image-prompts.scss'; + +const setHref = (anchorEl: HTMLAnchorElement, href:string) => { + anchorEl. href = href; +}; + +const HTML_TEMPLATE = ` + We cannot include the word "{word}" as it is found on the list + naughty-words/{lang}. + We understand blocklists are an imperfect solution but we believe it's + important to ensure these models are not misused, and hope that in this + instance it does not serve to marginalise anybody. If you don't agree, + please reach out via + form link. +`; + + +/** + * Shows image and text prompts, and computes similarities. + * + * Also dispatches some events like `'duplicate'` to the parent. + */ +@customElement('image-prompts') +export class ImagePrompts extends LitElement { + + static override styles = [styles]; + + @query('message-list') + messageList!: MessageList; + @query('.animation') + animation!: HTMLElement; + @query('.bottom') + bottom!: HTMLElement; + + lastPrompts?: string[]; + + constructor(private readonly imageId: string) { + super(); + } + + override firstUpdated() { + if (getBackend() !== 'webgl') { + this.messageList.warning( + 'Please activate WebGL. Running ML demos on ' + + 'CPU will drain your battery in no time...'); + } + } + + onDuplicate() { + this.dispatchEvent(new Event('duplicate')); + } + + onRemove() { + this.remove(); + } + + onClear() { + this.shadowRoot!.querySelectorAll('.prompt').forEach((input: Element) => { + (input as HTMLInputElement).value = ''; + }); + (this.shadowRoot!.querySelector('.prompt') as HTMLInputElement).focus(); + } + + onKeyup(event: KeyboardEvent) { + if (event.key === 'Enter') { + this.onCompute(); + } + } + + async setPrompts(prompts: string[]) { + await this.updateComplete; + this.shadowRoot!.querySelectorAll('.prompt').forEach((input: Element, idx: number) => { + (input as HTMLInputElement).value = prompts[idx] || ''; + }); + } + + getPrompts(): string[] { + return [...this.shadowRoot!.querySelectorAll('.prompt')].map((input: Element) => + (input as HTMLInputElement).value + ); + } + + override render() { + const row = app.imageData.get(this.imageId); + const inputs = row.prompts.split(',').map((prompt: string, idx: number) => { + return html` +
+
+ +
+
+ `; + }); + return html` +
+
+
+ + source +
+
+
✨✨Computing✨✨
+
+
+
+ +
+ + + + +
+ ${inputs} +
+ Model: ? + tweet +
+
+
+ `; + } + + onCompute() { + if (!app.models.ready) { + this.messageList.warning('Model not ready yet.'); + return; + } + + const model = app.models.model!; + const zimgIdx = model.zimgIds!.indexOf(this.imageId); + if (zimgIdx === -1) { + this.messageList.warning('Model is missing this image embedding'); + return; + } + + const texts = this.getPrompts(); + for (const text of texts) { + for (const word of text.toLocaleLowerCase().split(/\s+/g)) { + // tslint:disable-next-line:ban-module-namespace-object-escape + for (const lang of Object.keys(naughtyWords)) { + if (lang === 'default') { + continue; + } + // tslint:disable-next-line:ban-module-namespace-object-escape + const words = (naughtyWords as {[key: string]: string[]})[lang]; + if (words.indexOf(word) !== -1) { + const msg = HTML_TEMPLATE.replace(/\{word\}/g, word).replace(/\{lang\}/g, lang); + this.messageList.warning(msg, {rawHtml: true}); + return; + } + } + } + } + + const compute = () => { + let probs: number[]|undefined; + try { + // ??? how to move into webworker (to avoid freezing UI) ? + // https://github.com/tensorflow/tfjs/issues/102 + probs = model.computeProbabilities(texts, zimgIdx); + } catch (error) { + if ((error as Error).message.toLocaleLowerCase().match(/greater than .* maximum/)) { + this.messageList.warning('Model too large for Browser!'); + return; + } + throw error; + } + this.setProbabilities(probs); + this.lastPrompts = this.getPrompts(); + this.animation.style.opacity = '0'; + }; + + this.animation.style.opacity = '1'; + this.messageList.clear(); + setTimeout(compute, 10); // Give UI some time to update. + } + + setProbabilities(probs: number[]) { + const pcts = [...this.shadowRoot!.querySelectorAll('.pct')] as HTMLElement[]; + const bars = [...this.shadowRoot!.querySelectorAll('.bar')] as HTMLElement[]; + this.hideBottom(); + for(let i = 0; i < Math.max(probs.length, pcts.length, bars.length); i++) { + const prob = probs[i] || 0; + const pct = `${Math.round(prob * 1e3) / 1e1}%`; + bars[i].style.width = pct; + if (prob) { + pcts[i].innerText = pct; + pcts[i].style.opacity = '1'; + } else { + pcts[i].style.opacity = '0'; + } + } + this.updateBottom(); + } + + updateBottom() { + const tweet = this.shadowRoot!.querySelector('.tweet') as HTMLAnchorElement; + const url = getUrl(app.models.model!.name, this.imageId, this.getPrompts()); + const description = app.imageData.get(this.imageId).description; + const text = `LiT matching prompts to an image of "${description}"\n\n#lit_demo\n`; + setHref(tweet, 'https://twitter.com/intent/tweet' + + '?url=' + encodeURIComponent(url) + + '&text=' + encodeURIComponent(text)); + this.bottom.style.opacity = '1'; + const model = this.shadowRoot!.querySelector('.model') as HTMLAnchorElement; + model.innerText = app.models.model!.name; + } + + hideBottom() { + this.bottom.style.opacity = '0'; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'image-prompts': ImagePrompts; + } +} diff --git a/big_vision/tools/lit_demo/src/components/lit-demo-app.scss b/big_vision/tools/lit_demo/src/components/lit-demo-app.scss new file mode 100644 index 0000000000000000000000000000000000000000..884aeb635db195350a8316c95648942be939d4ee --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/lit-demo-app.scss @@ -0,0 +1,3 @@ +.loading-container { + text-align: center; +} diff --git a/big_vision/tools/lit_demo/src/components/lit-demo-app.ts b/big_vision/tools/lit_demo/src/components/lit-demo-app.ts new file mode 100644 index 0000000000000000000000000000000000000000..41f42f484c2ccb79b42858af45d1ddfd3ee304f6 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/lit-demo-app.ts @@ -0,0 +1,127 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Main application. + */ + +import {html, LitElement} from 'lit'; + +import {app} from '../lit_demo/app'; +import {parseUrl, State} from '../lit_demo/url_utils'; + +import './image-carousel'; +import {ImagePrompts} from './image-prompts'; +import './loading-animation'; +import {MessageList} from './message-list'; +import {ModelControls} from './model-controls'; + +import {customElement, property, query} from 'lit/decorators.js'; +import styles from './lit-demo-app.scss'; + +/** + * Main application container. + */ +@customElement('lit-demo-app') +export class LitDemoApp extends LitElement { + + static override styles = [styles]; + + @property({type: Boolean}) + loading: boolean = true; + + @query('message-list') + messageList!: MessageList; + @query('model-controls') + modelControls!: ModelControls; + @query('#examples') + examples!: HTMLElement; + + state?: State; + lingeringWarning?: string; + + constructor() { + super(); + window.onerror = this.onglobalerror.bind(this); + this.load(); + } + + onglobalerror(message: string|Event, source: string|undefined, lineno: number|undefined) { + source = source || ''; + source = source.substring(source.lastIndexOf('/') + 1); + this.messageList.error( + `Javascript error at ${source}:${lineno}
` + + `${message}`, + {rawHtml: true}); + } + + async load() { + await app.load(); + this.loading = false; + try { + this.state = parseUrl(); + } catch (error) { + this.messageList.warning(`Could not parse URL: ${error}`); + } + } + + override updated() { + if (this.state && this.examples) { + this.modelControls.setModel(this.state.modelName); + this.addFromState(this.state); + this.state = undefined; + } + } + + override render() { + return html` + ${this.loading ? html`` : html` + + + `} + + ${this.loading ? html` +
+ +
+ ` : html` +
+
+ `} + `; + } + + onImageSelect(event: CustomEvent) { + this.addImagePrompts(event.detail.id); + } + + addFromState(state: State) { + const imagePrompts = new ImagePrompts(state.imageId); + imagePrompts.setPrompts(state.prompts); + this.examples.insertBefore(imagePrompts, this.examples.childNodes[0]); + } + + addImagePrompts(id: string): ImagePrompts { + const imagePrompts = new ImagePrompts(id); + imagePrompts.addEventListener('duplicate', (event: Event) => { + const duplicated = this.addImagePrompts(id); + duplicated.setPrompts(imagePrompts.getPrompts()); + }); + this.examples.insertBefore(imagePrompts, this.examples.childNodes[0]); + return imagePrompts; + } +} diff --git a/big_vision/tools/lit_demo/src/components/loading-animation.scss b/big_vision/tools/lit_demo/src/components/loading-animation.scss new file mode 100644 index 0000000000000000000000000000000000000000..032be7a31f2a07138f509c7c6a85063c0d25864a --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/loading-animation.scss @@ -0,0 +1,65 @@ +// CC0 from https://loading.io/css/ + +@import '../style/colors'; + +.lds-ellipsis { + display: inline-block; + position: relative; + width: 80px; + height: 80px; + + div { + position: absolute; + top: 33px; + width: 13px; + height: 13px; + border-radius: 50%; + background: var(--text-fg); + animation-timing-function: cubic-bezier(0, 1, 1, 0); + } + + div:nth-child(1) { + left: 8px; + animation: lds-ellipsis1 0.6s infinite; + } + + div:nth-child(2) { + left: 8px; + animation: lds-ellipsis2 0.6s infinite; + } + + div:nth-child(3) { + left: 32px; + animation: lds-ellipsis2 0.6s infinite; + } + + div:nth-child(4) { + left: 56px; + animation: lds-ellipsis3 0.6s infinite; + } +} + +@keyframes lds-ellipsis1 { + 0% { + transform: scale(0); + } + 100% { + transform: scale(1); + } +} +@keyframes lds-ellipsis3 { + 0% { + transform: scale(1); + } + 100% { + transform: scale(0); + } +} +@keyframes lds-ellipsis2 { + 0% { + transform: translate(0, 0); + } + 100% { + transform: translate(24px, 0); + } +} diff --git a/big_vision/tools/lit_demo/src/components/loading-animation.ts b/big_vision/tools/lit_demo/src/components/loading-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..8af9de23b54d3b6b46ad5d9c8a7ac75bd344ac22 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/loading-animation.ts @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Carousel of images. + */ + +import {html, LitElement} from 'lit'; + +import {customElement} from 'lit/decorators.js'; +import styles from './loading-animation.scss'; + +/** + * Shows an animated loading animation. + */ +@customElement('loading-animation') +export class LoadingAnimation extends LitElement { + + static override styles = [styles]; + + override render() { + return html` +
+
+
+
+
+
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'loading-animation': LoadingAnimation; + } +} diff --git a/big_vision/tools/lit_demo/src/components/message-list.scss b/big_vision/tools/lit_demo/src/components/message-list.scss new file mode 100644 index 0000000000000000000000000000000000000000..79aa76465b2120d256c14c3a4583709a050e2be4 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/message-list.scss @@ -0,0 +1,26 @@ +@import '../style/colors'; + +.message { + padding: 0.1rem 0.5rem; + margin-bottom: 1rem; +} + +.warning { + background: var(--warn-bg); + color: var(--warn-fg); +} + +.error { + background: var(--error-bg); + color: var(--error-fg); +} + +.info { + background: var(--note-bg); + color: var(--note-fg); +} + +.close { + float: right; + cursor: pointer; +} diff --git a/big_vision/tools/lit_demo/src/components/message-list.ts b/big_vision/tools/lit_demo/src/components/message-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..542bfb0f9502d55c65ff131c43684246192cb6f7 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/message-list.ts @@ -0,0 +1,97 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview A list of dismissable info/warning/error messages. + */ + +import {html, LitElement} from 'lit'; + +import {unsafeHTML} from 'lit/directives/unsafe-html.js'; + +import {customElement} from 'lit/decorators.js'; +import styles from './message-list.scss'; + +enum MessageType { + INFO = 'info', + WARNING = 'warning', + ERROR = 'error', +} + +interface Message { + message: string; + type: MessageType; + rawHtml: boolean; +} + + +/** + * Shows info/warning/error messages that remain until closed by user. + */ +@customElement('message-list') +export class MessageList extends LitElement { + static override styles = [styles]; + + messages: Message[] = []; + + addMessage(message: Message) { + this.messages.push(message); + this.requestUpdate(); + } + + info(message: string, {rawHtml = false}: {rawHtml?: boolean} = {}) { + this.addMessage({message, type: MessageType.INFO, rawHtml}); + } + + warning(message: string, {rawHtml = false}: {rawHtml?: boolean} = {}) { + this.addMessage({message, type: MessageType.WARNING, rawHtml}); + } + + error(message: string, {rawHtml = false}: {rawHtml?: boolean} = {}) { + this.addMessage({message, type: MessageType.ERROR, rawHtml}); + } + + removeMessage(event: Event, idx: number) { + this.messages.splice(idx, 1); + (event.target! as HTMLElement).closest('.message')!.remove(); + } + + clear() { + this.messages = []; + while (this.firstChild) this.firstChild.remove(); + } + + override render() { + return this.messages.map( + (message: Message, idx: number) => html` +
+ ${ + message.rawHtml ? unsafeHTML(message.message) : + message.message} + { + this.removeMessage(e, idx); + }} class="close">✖ +
+ `); + } +} + +declare global { + interface HTMLElementTagNameMap { + 'message-list': MessageList; + } +} diff --git a/big_vision/tools/lit_demo/src/components/model-controls.scss b/big_vision/tools/lit_demo/src/components/model-controls.scss new file mode 100644 index 0000000000000000000000000000000000000000..a7627c8cb9d15b825cd3a84cdc217eb2b39cfc05 --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/model-controls.scss @@ -0,0 +1,12 @@ +.controls { + margin: 1em 0; + display: flex; + + select { + margin-left: 0.5em; + } + + progress { + margin: 0 1em; + } +} diff --git a/big_vision/tools/lit_demo/src/components/model-controls.ts b/big_vision/tools/lit_demo/src/components/model-controls.ts new file mode 100644 index 0000000000000000000000000000000000000000..40fdb646c436ef7dfd9332835819f0e9534c067b --- /dev/null +++ b/big_vision/tools/lit_demo/src/components/model-controls.ts @@ -0,0 +1,93 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Controls to choose model. + */ + +import {html, LitElement} from 'lit'; + +import {getModels} from '../lit_demo/constants'; +import {app} from '../lit_demo/app'; + +import {customElement, property} from 'lit/decorators.js'; +import styles from './model-controls.scss'; + +/** + * Shows controls for model selection, progress bar, and status text. + */ +@customElement('model-controls') +export class ModelControls extends LitElement { + + static override styles = [styles]; + + @property({attribute: false}) + progress: number = 0; + + @property({attribute: false}) + status: string = 'Initializing...'; + + constructor() { + super(); + app.models.addListener(this.onModelUpdate.bind(this)); + app.models.load(getModels()[0]); + } + + onModelUpdate(progress: number, message?: string) { + this.progress = progress; + if (message) this.status = message; + } + + onModelChange(event: Event) { + const target = event.target as HTMLSelectElement; + const name = target.value; + app.models.load(name).catch((error) => { + this.status = `ERROR loading model "${name}": ${error}`; + }); + } + + async setModel(model: string) { + if (getModels().indexOf(model) === -1) { + throw new Error(`Model "${model}" not found!`); + } + await this.updateComplete; + const dropdown = this.shadowRoot!.querySelector('#model_dropdown') as HTMLSelectElement; + dropdown.value = model; + dropdown.dispatchEvent(new Event('change')); + } + + override render() { + const options = getModels().map((model: string) => + html``); + return html` +
+ + + +
${this.status}
+
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + 'model-controls': ModelControls; + } +} diff --git a/big_vision/tools/lit_demo/src/exports.ts b/big_vision/tools/lit_demo/src/exports.ts new file mode 100644 index 0000000000000000000000000000000000000000..b756e172aa6f89692faf1ba295e38ea14c59ccb0 --- /dev/null +++ b/big_vision/tools/lit_demo/src/exports.ts @@ -0,0 +1,38 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview some useful exports to play around with the models & + * tokenizers. + * + * Simple usage (see ./playground.html for more complete usage example): + * + * model = lit.Model('tiny'); + * model.load(progress => console.log('loading...', progress)); + * console.log(model.computeProbabilities(['a dog', 'a cat'], '0')); + */ + +import {Model} from './lit_demo/compute'; +import {getImageUrl, setBaseUrl} from './lit_demo/constants'; +import {ImageData} from './lit_demo/data'; +import * as tf from '@tensorflow/tfjs-core'; + +// tslint:disable-next-line:no-any Export symbols into global namespace. +(window as any).lit = { Model, getImageUrl, ImageData, setBaseUrl }; +// tslint:disable-next-line:no-any Export symbols into global namespace. +// tslint:disable-next-line:ban-module-namespace-object-escape Export all of TF. +(window as any).tf = tf; diff --git a/big_vision/tools/lit_demo/src/index.html b/big_vision/tools/lit_demo/src/index.html new file mode 100644 index 0000000000000000000000000000000000000000..580e328f1d0efa0b8d682d7d59e20d58b75e2367 --- /dev/null +++ b/big_vision/tools/lit_demo/src/index.html @@ -0,0 +1,80 @@ + + + + + + + Lit Demo App + + + + + + + +

LiT: Zero-Shot Transfer with Locked-image Tuning

+ +

+ This page is an interactive demo of the Google AI blog post + LiT: adding language understanding to image models + – please refer to that page for a detailed explanation of how a LiT model works. + If you're interested in how this demo makes a JAX model run on device in your + browser, check out our other blog post + JAX on the Web with TensorFlow.js. +

+ +

+ Below you can choose an image from a selection and then write free-form + text prompts that are matched to the image. Once you hit return on your + keyboard or press the "compute" button, a text encoder implemented in + TensorFlow.js + will compute embeddings for the provided text on your local device, and the + similarity of these text embeddings to the image embedding will be displayed. +

+ +

+ The prompts can be used to classify an image into multiple categories, listing + each category individually with a prompt "an image of a X". But you can also + probe the model interactively with more detailed prompts, comparing the + different results when small details change in the text. +

+ +

+ Please use this demo responsibly. The models will always compare the image to + the prompts you provide, and it is therefore trivial to construct situations + where the model picks from a bunch of bad options. +

+ +

+ Note: + The models available in this interactive demo are not those from the + paper. + We had to train much smaller text towers and tokenizers to avoid + overloading your browser. Please see + our GitHub repository + for the models from the paper pre-trained on public datasets. + Multilingual models coming soon. +

+ + + + \ No newline at end of file diff --git a/big_vision/tools/lit_demo/src/lit_demo/app.ts b/big_vision/tools/lit_demo/src/lit_demo/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e104f298033933514a575ef8ba796c70b11c4c4 --- /dev/null +++ b/big_vision/tools/lit_demo/src/lit_demo/app.ts @@ -0,0 +1,47 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Global app state. + */ + +import {ImageData} from './data'; +import {Models} from './compute'; + +/** + * Container class holding image data and models. + * + * The main application component would typically call `load()` and then show + * the components depending on this class asynchronously. + */ +export class App { + + imageData = new ImageData(); + models = new Models(); + + ready: boolean = false; + + async load() { + await this.imageData.load(); + this.ready = true; + } +} + +/** + * Global app state. + */ +export const app = new App(); diff --git a/big_vision/tools/lit_demo/src/lit_demo/compute.ts b/big_vision/tools/lit_demo/src/lit_demo/compute.ts new file mode 100644 index 0000000000000000000000000000000000000000..06b9c7eb72a4e565047fc6966e56cc67465c6145 --- /dev/null +++ b/big_vision/tools/lit_demo/src/lit_demo/compute.ts @@ -0,0 +1,293 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Model code. + */ + +import '@tensorflow/tfjs-backend-webgl'; + +import * as tfconv from '@tensorflow/tfjs-converter'; +import * as tf from '@tensorflow/tfjs-core'; +import {MathBackendWebGL} from '@tensorflow/tfjs-backend-webgl'; + +import {getTokenizer, Tokenizer} from '../tokenizers/index'; + +import {getModelFileUrl} from './constants'; + +/** + * Callback to be updated with model load status. + * + * @param progress: the callback function is repeatedly called with values from + * 0 to 1 (both endpoints are guaranteed to be included) + * @param message: optional message to be displayed to user + */ +export type StatusCallback = (progress: number, message?: string) => void; + +const canonicalize = (s: string) => { + s = s.toLocaleLowerCase(); + s = s.replace(/[^\w ]/g, ''); + s = s.replace(/\s+/g, ' '); + return s.trim(); +}; + +/** + * The model definition is read from a JSON and specifies model details. + */ +// tslint:disable:enforce-name-casing +export interface ModelDefinition { + /** Human-readable description of the model. */ + description: string; + /** Tokenizer type. See ./tokenizers/index */ + tokenizer_type: string; + /** Temperature for computing softmax. */ + temperature: number; + /** Token used for padding. */ + pad_value: number; + /** Maximum token length. */ + max_len: number; + /** Dimensionality of image/text embeddings. */ + embedding_size: number; +} +// tslint:enable:enforce-name-casing + +/** + * TFJS model to compute text embeddings and similarities. + */ +export class Model { + def?: ModelDefinition; + tokenizer?: Tokenizer; + model?: tfconv.GraphModel; + /** Pre-computed image embeddings. */ + zimgs?: tf.Tensor; + /** Pre-computed text embeddings. */ + ztxts?: tf.Tensor; + /** IDs for pre-computed image embeddings. */ + zimgIds?: string[]; + /** Prompts for pre-computed text embeddings. */ + ztxtPrompts?: string[]; + /** Will be set to `true` when `load()` has completed successfully. */ + ready: boolean = false; + + /** + * @param name: Name of the model to be loaded. Will be used to construct the + * model URL. Note that the model must be loaded via calling `load()` before + * it can be used. + */ + constructor(public name: string) { + } + + /** + * Loads model, tokenizer, and pre-computed embeddings. + */ + async load(callback?: StatusCallback) { + this.def = + await fetch(getModelFileUrl(this.name, 'def.json')).then(resp => { + if (resp.ok) return resp.json(); + throw new Error(`Could not load model def: ${resp.status}`); + }); + console.log('def', this.def); + + const tokenizer = fetch(getModelFileUrl(this.name, 'vocabulary.json')) + .then(resp => resp.json()) + .then( + vocabulary => getTokenizer( + this.def!.tokenizer_type, vocabulary)); + + const model = + tfconv.loadGraphModel(getModelFileUrl(this.name, 'tfjs/model.json'), { + onProgress: (progress: number) => { + callback && callback(progress); + } + }); + + const fetchBin = async (name: string) => { + const response = await fetch(getModelFileUrl(this.name, `${name}.bin`)); + const blob = await response.blob(); + const data = await new Promise(resolve => { + const reader = new FileReader(); + reader.addEventListener('loadend', () => { + resolve(reader.result); + }); + reader.readAsArrayBuffer(blob); + }); + const arr = new Float32Array(data as Iterable); + const n = arr.length / this.def!.embedding_size; + return tf.tensor(arr, [n, this.def!.embedding_size]); + }; + const fetchTxt = (name: string) => + fetch(getModelFileUrl(this.name, `${name}.txt`)) + .then(response => response.text()) + .then(text => text.split(/\n/g)); + + [this.tokenizer, + this.model, + this.zimgs, + this.ztxts, + this.zimgIds, + this.ztxtPrompts, + ] = + [ + await tokenizer, + await model, + await fetchBin('zimgs'), + await fetchBin('ztxts'), + await fetchTxt('zimgs'), + await fetchTxt('ztxts'), + ]; + this.ready = true; + await this.warmup(); + if (callback) callback(1, 'Done.'); + } + + private async warmup() { + if (getBackend() !== 'webgl') return; + + const webGLBackend = tf.backend() as MathBackendWebGL; + tf.env().set('ENGINE_COMPILE_ONLY', true); + const tokens = tf.zeros([5, this.def!.max_len], 'int32'); + const preCompileResults = + this.model!.predict({inputs: tokens}) as tf.Tensor; + webGLBackend.checkCompileCompletion(); + webGLBackend.getUniformLocations(); + + tf.env().set('ENGINE_COMPILE_ONLY', false); + const warmUpResults = this.model!.predict({inputs: tokens}) as tf.Tensor; + await warmUpResults.data(); + + preCompileResults.dispose(); + warmUpResults.dispose(); + } + + /** + * Tokenizes strings with the model's tokenizer. + */ + tokenize(texts: string[]): tf.Tensor { + if (!this.ready) throw new Error('Cannot tokenize: not ready'); + const tokenize = (text: string) => { + const maxLen = this.def!.max_len || 16; + const tokens = this.tokenizer!.encode(text).slice(0, maxLen); + // eos="sticky" + const tokenEos = tf.tensor( + [ + ...tokens, + ...new Array(16 - tokens.length).fill(this.def!.pad_value), + ], + undefined, 'int32'); + return tokenEos; + }; + return tf.stack(texts.map(tokenize)); + } + + /** + * Computes embeddings for text tokenized via `tokenize()`. + */ + embed(tokens: tf.Tensor): tf.Tensor { + if (!this.ready) throw new Error('Cannot embed: not ready'); + return this.model!.execute({inputs: tokens}) as tf.Tensor; + } + + /** + * Computes similarities between specified prompts and images. Images are + * referenced by their ID. + */ + computeSimilarities(texts: string[], imgidxs: number[]) { + if (!this.ready) throw new Error('Cannot compute similarities: not ready'); + texts = texts.map(canonicalize); + const precomputed = + texts + .map(text => { + const idx = this.ztxtPrompts!.indexOf(text); + return idx === -1 ? null : tf.slice(this.ztxts!, idx, 1); + }) + .filter((x: tf.Tensor|null) => !!x) as tf.Tensor[]; + console.log(texts.length, 'texts, ', precomputed.length, 'precomputed'); + const textEmbeddings = texts.length === precomputed.length ? + tf.concat(precomputed) : + this.embed(this.tokenize(texts)); + const imageEmbeddingsTransposed = tf.transpose( + tf.concat(imgidxs.map(idx => tf.slice(this.zimgs!, idx, 1)))); + const sims = tf.matMul(textEmbeddings, imageEmbeddingsTransposed); + sims.print(); + return sims; + } + + /** + * Computes probabilities between a set of prompts and a single image + * (identified by its ID). + */ + computeProbabilities(texts: string[], imgidx: number): number[] { + const sims = this.computeSimilarities(texts, [imgidx]); + const row = tf.squeeze(tf.slice(tf.transpose(sims), 0, 1)); + return [...tf.softmax(tf.mul(this.def!.temperature, row)).dataSync()]; + } +} + +/** + * Container that holds a set of models. + */ +export class Models { + private readonly map = new Map(); + private readonly listeners = new Set(); + model?: Model; + + /** + * Adds a listener to be updated about individual models' loading progress. + */ + addListener(callback: StatusCallback) { + this.listeners.add(callback); + } + + /** + * Updates all listeners wth `progress` and `message`. + */ + onUpdate(progress: number, message?: string) { + if (progress === 1) { + message = `Loaded model "${this.model?.name}".`; + } + for (const callback of this.listeners) { + callback(progress, message); + } + } + + /** + * Loads model and sets `model` attribute when ready. + */ + async load(name: string) { + if (this.map.has(name)) { + this.model = this.map.get(name); + this.onUpdate(1, `Loaded "${name}".`); + return; + } + this.onUpdate(0, 'Loading...'); + this.model = new Model(name); + await this.model.load(this.onUpdate.bind(this)); + this.map.set(name, this.model); + } + + /** + * Whether model referenced by `model` attribute is ready. + */ + get ready(): boolean { + return !!this.model?.ready; + } +} + +/** Returns backend, such as "cpu" or "webgl". */ +export function getBackend(): string { + return tf.getBackend(); +} diff --git a/big_vision/tools/lit_demo/src/lit_demo/constants.ts b/big_vision/tools/lit_demo/src/lit_demo/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..45dab49ca5699a2897b98c0a412c838d8d8ba0e7 --- /dev/null +++ b/big_vision/tools/lit_demo/src/lit_demo/constants.ts @@ -0,0 +1,50 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Project-wide constants. + */ + +// Can be overwritten with setBaseUrl() below. +// let baseUrl = 'https://google-research.github.io/vision_transformer/lit'; +let baseUrl = 'https://figur.li/jax2tfjs'; +// Can be overwritten with setModels() below. +let models = ['tiny', 'small']; + +/** Allows to set abnew base URL. ase URL on which all other. */ +export const setBaseUrl = (newBaseUrl: string) => { + baseUrl = newBaseUrl; +}; + +/** Retrieves URL for a model-specific file (vocabulary, embeddings, ...). */ +export const getModelFileUrl = (name: string, relativePath: string) => ( + `${baseUrl}/data/models/${name}/${relativePath}` +); + +/** Retrieves the URL for images information JSON file. */ +export const getImagesInfoUrl = () => `${baseUrl}/data/images/info.json`; + +/** Retrieves the URL for an image. */ +export const getImageUrl = (id: string) => `${baseUrl}/data/images/${id}.jpg`; + +/** Returns names of available models. */ +export const getModels = () => models; + +/** Sets names of available models. */ +export const setModels = (newModels: string[]) => { + models = newModels; +}; diff --git a/big_vision/tools/lit_demo/src/lit_demo/data.ts b/big_vision/tools/lit_demo/src/lit_demo/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..6322d7dcefe22147d5715ee4710d2d7f2daf951a --- /dev/null +++ b/big_vision/tools/lit_demo/src/lit_demo/data.ts @@ -0,0 +1,76 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Accessing additional data. + */ + +import {getImagesInfoUrl} from './constants'; + +/** + * Information about a single image. + */ +export interface ImageRow { + /** Stable ID of the image. */ + id: string; + /** Set of example prompts for this image. */ + prompts: string; + /** License of the image. */ + license: string; + /** Where the image was originally downloaded from. */ + source: string; + /** Short description of image. */ + description: string; +} +/** + * Contains information about all images. + */ +export class ImageData { + + rows: ImageRow[] = []; + /** Will be set to `true` when `load()` finishes. */ + ready = false; + + /** + * Gets an image by ID. Throws an error if image is not found, data is not + * loaded, or ID is not unique. + */ + get(id: string): ImageRow { + if (!this.ready) { + throw new Error('ImageData not loaded!'); + } + const matching = this.rows.filter(row => row.id === id); + if (matching.length !== 1) { + throw new Error(`Got unexpected ${matching.length} matches for id="${id}"`); + } + return matching[0]; + } + + /** + * Loads image data asynchronously. + */ + async load() { + this.rows = ( + await fetch(getImagesInfoUrl()) + .then(response => { + console.log('response', response); + return response.json(); + }) + ); + this.ready = true; + } +} diff --git a/big_vision/tools/lit_demo/src/lit_demo/url_utils.ts b/big_vision/tools/lit_demo/src/lit_demo/url_utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..d169bc4711d44fbeb146a85de55d47a882fe695b --- /dev/null +++ b/big_vision/tools/lit_demo/src/lit_demo/url_utils.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview (De)serialize state from/to URL. + */ + +// Should be updated whenever URLs are not compatible anymore +// (e.g. adding new images) +export const VERSION = 'v2'; +// version history: +// v1 used row number instead of image id + +const V1_IMAGE_IDS = [ + '1', '48', '43', '22', '2', '3', '4', '5', '6', '7', '8', '9', + '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', + '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', + '35', '36', '37', '38', '39', '40', '41', '42', '44', '45', '46', '47', + '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60' +]; + +/** + * State that can be stored in the URL. + */ +export interface State { + /** Name of the model. */ + modelName: string; + /** ID Of the image. */ + imageId: string; + /** List of text prompts. */ + prompts: string[]; +} + +/** + * Returns a URL for provided model/image/prompts. + */ +export const getUrl = + (modelName: string, imageId: string, prompts: string[]): string => { + let href = window.location.href; + if (href.indexOf('#') !== -1) { + href = href.substring(0, href.indexOf('#')); + } + const parts = [ + VERSION, + modelName, + imageId, + ...prompts, + ]; + return href + '#' + parts.map(encodeURIComponent).join('|'); + }; + +/** + * Parses an URL and returns a `State`, or undefined if no state is spefified. + * + * Raises an exception if there was a problem with the parsing of the URL. + */ +export const parseUrl = (): State|undefined => { + const hash = window.location.hash.substring(1); + if (!hash) return; + const parts = hash.split(/\|/g); + if (parts.length < 4) { + throw new Error(`Invalid URL: "${hash}"`); + } + let [version, modelName, imageId, ...texts] = parts; + if (version === VERSION) { + } else if (version === 'v1') { + const idx = Number(imageId); + if (isNaN(idx)) throw new Error(`Expected idx="${idx}" to be numerical!`); + imageId = V1_IMAGE_IDS[idx]; + } else { + throw new Error(`Incompatible version: ${version} (supported: ${VERSION})`); + } + return { + modelName, + imageId, + prompts: texts.map(decodeURIComponent), + }; +}; diff --git a/big_vision/tools/lit_demo/src/playground.html b/big_vision/tools/lit_demo/src/playground.html new file mode 100644 index 0000000000000000000000000000000000000000..112d69eb294ea364ea6b843a491ddb78fb1ff343 --- /dev/null +++ b/big_vision/tools/lit_demo/src/playground.html @@ -0,0 +1,92 @@ + + + + + + +

+ A simple demonstration how to use LiT models in a JS application using global exports. + See source code of this file for API usage. +

+ +

+    
+
+
+
+ + + + diff --git a/big_vision/tools/lit_demo/src/style.scss b/big_vision/tools/lit_demo/src/style.scss new file mode 100644 index 0000000000000000000000000000000000000000..7fab695cb992dd0552068f92cad28a7c6546eb07 --- /dev/null +++ b/big_vision/tools/lit_demo/src/style.scss @@ -0,0 +1,80 @@ +// General styles for the page. + +@import './style/colors'; +@import './style/mixins'; + +html { + font-size: 14px; + line-height: 1.6em; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, + Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Arial, + sans-serif; + text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + + @media (min-width: 1200px) { + width: 1024px; + margin: 0 auto; + } + @media (min-width: 768px) { + font-size: 16px; + } + + color: var(--text-fg); + background: var(--text-bg); + + body { + margin: 0; + padding: 0rem 1rem 10rem; + } +} + +a, +a:visited { + color: var(--link-col); +} + +h1 { + font-weight: 700; + font-size: 2rem; + line-height: 1.3em; +} + +p { + font-size: 1.06rem; + line-height: 1.3em; +} + +input { + font-size: 1rem; + + &::placeholder { + color: var(--placeholder-col); + } +} + +.note { + font-style: normal; + border: none; + border-radius: 2px; + margin-left: auto; + margin-right: auto; + + padding: 0.5rem 0.5rem 0.5rem 2rem; + width: 90%; + + @include phone-portrait { + width: 100%; + padding: 0.5rem; + box-sizing: border-box; + } + + background-color: var(--note-bg); + color: var(--note-fg); + + &.warning { + background-color: var(--warn-bg); + color: var(--warn-fg); + } +} diff --git a/big_vision/tools/lit_demo/src/style/colors.scss b/big_vision/tools/lit_demo/src/style/colors.scss new file mode 100644 index 0000000000000000000000000000000000000000..8c03eefa93fed513fee7cf58660f7ee7fb8bf6a4 --- /dev/null +++ b/big_vision/tools/lit_demo/src/style/colors.scss @@ -0,0 +1,35 @@ +// Dark and light mode colors. + +:root { + --text-bg: hsl(0, 0%, 97%); + --gray-border: hsla(0, 0%, 0%, 0.1); + --gray: rgba(0, 0, 0, 0.6); + --border-radius: 5px; + --orange: hsl(24, 100%, 50%); + --distill-blue: hsl(200, 50%, 25%); + --blue: #337699; + --green: #3db867; + --text-fg: rgb(15, 15, 15); + --text-red: rgb(220, 0, 0); + --bar-col: rgb(171, 199, 227); + --link-col: rgb(0, 0, 238); + --placeholder-col: rgb(166, 166, 166); + --note-bg: #e1f5fe; + --note-fg: #1a6ebb; + --warn-bg: #ffe1aa; + --warn-fg: #a16800; + --error-bg: #850000; + --error-fg: white; + + @media (prefers-color-scheme: dark) { + --text-bg: rgb(56, 56, 56); + --text-fg: rgb(213, 213, 213); + --bar-col: rgb(20, 109, 163); + --link-col: rgb(66, 165, 245); + + --note-fg: rgb(121 157 190); + --note-bg: rgb(2 59 85); + --warn-bg: #784e00; + --warn-fg: #edbe68; + } +} diff --git a/big_vision/tools/lit_demo/src/style/mixins.scss b/big_vision/tools/lit_demo/src/style/mixins.scss new file mode 100644 index 0000000000000000000000000000000000000000..8e4ad3fd9e0bdd24b1ceb08dc55551a05e37f343 --- /dev/null +++ b/big_vision/tools/lit_demo/src/style/mixins.scss @@ -0,0 +1,8 @@ +// Useful mixins. + +// To wrap styles that should only trigger for phones in portrait mode. +@mixin phone-portrait { + @media only screen and (max-device-width: 800px) and (orientation: portrait) { + @content; + } +} diff --git a/big_vision/tools/lit_demo/src/tokenizers/common.ts b/big_vision/tools/lit_demo/src/tokenizers/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd601bf48f9cc66327d574efc0e189b2228c8330 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/common.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Utility code shared between tokenizers. + */ + +/** + * A vocabulary consists of a list of tokens, and optional numerical value. + * The numerical value is used by the unigram algorithnm to find the best + * tokenizaion, and is ignored by the BPE algorithm. + */ +export type Vocabulary = Array<[string, number]>; + +/** + * Converts a string to a sequence of tokens. + */ +export interface Tokenizer { + encode(input: string): number[]; +} + +/** + * Factory for new `Tokenizer`. + */ +export interface TokenizerConstructor { + new (vocabulary: Vocabulary): Tokenizer; +} + +/** + * Unicode-aware character iteration of strings. + */ +export const stringToChars = (input: string): string[] => { + const symbols = []; + for (const symbol of input) { + symbols.push(symbol); + } + return symbols; +}; + +/** + * Special separator character used to delimit sub-word tokens. + */ +export const TOKEN_SEPARATOR = + '\u2581'; // This is the unicode character 'lower one eighth block'. diff --git a/big_vision/tools/lit_demo/src/tokenizers/index.ts b/big_vision/tools/lit_demo/src/tokenizers/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..687e4da496cdfa726599f2791ba601fde62a324d --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/index.ts @@ -0,0 +1,40 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +/** + * @fileoverview Tokenizers and tokenizer mappings. + */ + +import {Tokenizer, TokenizerConstructor, Vocabulary} from './common'; +import * as sentencepieceBpe from './sentencepiece_bpe'; +import * as sentencepieceUnigram from './sentencepiece_unigram'; + +export {Tokenizer, Vocabulary} from './common'; + +const TOKENIZERS = new Map([ + ['BPE', sentencepieceBpe.Tokenizer], + ['UNIGRAM', sentencepieceUnigram.Tokenizer], +]); + +/** + * Returns a tokenizer of type `name` using `vocabulary`. + */ +export const getTokenizer = (name: string, vocabulary: Vocabulary): Tokenizer => { + const ctor = TOKENIZERS.get(name); + if (!ctor) throw new Error(`Unknown tokenizer: ${name}`); + return new ctor(vocabulary); +}; diff --git a/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe.ts b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2603bac81b0066e0c63bf445bc2e41703bf7bb3 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe.ts @@ -0,0 +1,80 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +import {stringToChars, TOKEN_SEPARATOR, Vocabulary, Tokenizer as TokenizerInterface} from './common'; + +interface Candidate { + piece: string; + pos: number; + score: number; +} + +const scoreDesc = (a: Candidate, b: Candidate) => b.score - a.score; + +function processInput(str: string): string { + const normalized = str.normalize('NFKC'); + return normalized.length > 0 ? + TOKEN_SEPARATOR + normalized.replace(/ /g, TOKEN_SEPARATOR) : + normalized; +} + +/** + * Sentencepiece tokenizer implementing the BPE algorithm. + */ +export class Tokenizer implements TokenizerInterface { + + // piece -> [score, index] + private readonly map: Map; + + constructor(vocabulary: Vocabulary) { + this.map = new Map(); + vocabulary.forEach(([piece, score], idx) => { + if (this.map.has(piece)) { + throw new Error(`Piece "${piece}" occurs multiple times in vocabulary`); + } + this.map.set(piece, [score, idx]); + }); + } + + encode(input: string): number[] { + const processed: string = processInput(input); + let pieces: string[] = stringToChars(processed); + + while (true) { + const candidates: Candidate[] = []; + for (let i = 0; i < pieces.length - 1; i++) { + const fused = pieces[i] + pieces[i + 1]; + const el = this.map.get(fused); + if (el) { + candidates.push({ piece: fused, pos: i, score: el[0] }); + } + } + if (candidates.length === 0) { + break; + } + candidates.sort(scoreDesc); + const best = candidates[0]; + pieces = [ + ...pieces.slice(0, best.pos), + best.piece, + ...pieces.slice(best.pos + 2) + ]; + } + + return pieces.map(piece => this.map.get(piece)![1]); + } +} diff --git a/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe_test.ts b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..022ae1065fe89dbc28695ac9d8f16dbf2f7aaa36 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe_test.ts @@ -0,0 +1,48 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +import 'jasmine'; + +describe('sentencepiece bpe test', () => { + it('computes a thing when asked', () => {}); +}); + +import * as bpe from './sentencepiece_bpe'; +import {TOKEN_SEPARATOR, Vocabulary} from './common'; + +const vocab: Vocabulary = [ + [TOKEN_SEPARATOR, 0], // 0 + ['a', 0], // 1 + ['e', 0], // 2 + ['s', 0], // 3 + ['t', 0], // 4 + ['te', -1], // 5 + ['st', -2], // 6 + ['test', -3], // 7 + ['tes', -4], // 8 +]; + +describe('BPE Tokenizer', () => { + let tokenizer: bpe.Tokenizer; + beforeAll(() => { + tokenizer = new bpe.Tokenizer(vocab); + }); + + it('should tokenize correctly', () => { + expect(tokenizer.encode('a test')).toEqual([0, 1, 0, 7]); + }); +}); diff --git a/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram.ts b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e3ac0a40ff83678423978c046f7d035a85de2dd --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram.ts @@ -0,0 +1,134 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +// Copied & adapted from +// https://github.com/tensorflow/tfjs-models/blob/master/universal-sentence-encoder/src/tokenizer/index.ts + +import {TOKEN_SEPARATOR, stringToChars, Tokenizer as TokenizerInterface, Vocabulary} from './common'; +import {Trie} from './trie'; + +function processInput(str: string): string { + const normalized = str.normalize('NFKC'); + return normalized.length > 0 ? + TOKEN_SEPARATOR + normalized.replace(/ /g, TOKEN_SEPARATOR) : + normalized; +} + +// The first tokens are reserved for unk, control symbols, and user-defined +// symbols. +const RESERVED_SYMBOLS_COUNT = 6; + +interface Score { + key: string[]; + score: number; + index: number; +} + +/** + * Sentencepiece tokenizer implementing the UNIGRAM algorithm. + * + * `Tokenizer.encode()` is a port of `EncodeAsIds` from the SentencePiece + * library (https://github.com/google/sentencepiece). Encode uses the Viterbi + * algorithm to find the most likely sequence of tokens that comprise the input. + * For more details, refer to https://arxiv.org/pdf/1804.10959.pdf. + */ +export class Tokenizer implements TokenizerInterface { + trie: Trie; + + constructor( + private readonly vocabulary: Vocabulary, + private readonly reservedSymbolsCount = RESERVED_SYMBOLS_COUNT) { + this.trie = new Trie(); + + for (let i = this.reservedSymbolsCount; i < this.vocabulary.length; i++) { + this.trie.insert(this.vocabulary[i][0], this.vocabulary[i][1], i); + } + } + + encode(input: string): number[] { + const nodes: Array<{[index: number]: Score[]}> = []; + const words: number[] = []; + const best: number[] = []; + + input = processInput(input); + + const symbols = stringToChars(input); + + for (let i = 0; i <= symbols.length; i++) { + nodes.push({}); + words.push(0); + best.push(0); + } + + // Construct the lattice. + for (let i = 0; i < symbols.length; i++) { + const matches = this.trie.commonPrefixSearch(symbols.slice(i)); + + for (let j = 0; j < matches.length; j++) { + const piece = matches[j]; + const obj = {key: piece[0], score: piece[1], index: piece[2]}; + + const endPos = piece[0].length; + if (nodes[i + endPos][i] == null) { + nodes[i + endPos][i] = []; + } + + nodes[i + endPos][i].push(obj); + } + } + + for (let endPos = 0; endPos <= symbols.length; endPos++) { + for (const startPos in nodes[endPos]) { + if (!nodes[endPos].hasOwnProperty(startPos)) continue; + const arr = nodes[endPos][startPos]; + + for (let j = 0; j < arr.length; j++) { + const word = arr[j]; + const score = word.score + best[endPos - word.key.length]; + + if (best[endPos] === 0 || score >= best[endPos]) { + best[endPos] = score; + words[endPos] = arr[j].index; + } + } + } + } + + const results: number[] = []; + + // Backward pass. + let iter = words.length - 1; + while (iter > 0) { + results.push(words[iter]); + iter -= this.vocabulary[words[iter]][0].length; + } + + // Merge consecutive unks. + const merged = []; + let isPreviousUnk = false; + for (let i = 0; i < results.length; i++) { + const id = results[i]; + if (!(isPreviousUnk && id === 0)) { + merged.push(id); + } + + isPreviousUnk = id === 0; + } + + return merged.reverse(); + } +} diff --git a/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram_test.ts b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d52af2ba624e6d104b3529491fee6ba6f6c61754 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram_test.ts @@ -0,0 +1,71 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +import {Tokenizer} from './sentencepiece_unigram'; + +const stubbedTokenizerVocab = [ + ['�', 0], + ['', 0], + ['', 0], + ['extra_token_id_1', 0], + ['extra_token_id_2', 0], + ['extra_token_id_3', 0], + ['▁', -2], + ['▁a', -1], + ['▁ç', -2], + ['a', -3], + ['.', -1], + ['▁I', -1], + ['▁like', -1], + ['▁it', -1], + ['I', -2], + ['like', -2], + ['it', -2], + ['l', -3], + ['i', -3], + ['k', -3], + ['e', -3], + ['i', -3], + ['t', -3] +]; + +describe('Universal Sentence Encoder tokenizer', () => { + let tokenizer: Tokenizer; + beforeAll(() => { + tokenizer = new Tokenizer(stubbedTokenizerVocab as Array<[string, number]>); + }); + + it('basic usage', () => { + expect(tokenizer.encode('Ilikeit.')).toEqual([11, 15, 16, 10]); + }); + + it('handles whitespace', () => { + expect(tokenizer.encode('I like it.')).toEqual([11, 12, 13, 10]); + }); + + it('should normalize inputs', () => { + expect(tokenizer.encode('ça')).toEqual(tokenizer.encode('c\u0327a')); + }); + + it('should handle unknown inputs', () => { + expect(() => tokenizer.encode('😹')).not.toThrow(); + }); + + it('should treat consecutive unknown inputs as a single word', () => { + expect(tokenizer.encode('a😹😹')).toEqual([7, 0]); + }); +}); diff --git a/big_vision/tools/lit_demo/src/tokenizers/trie.ts b/big_vision/tools/lit_demo/src/tokenizers/trie.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f6954bb962cad952e2f246a65a46bcb4fdb5756 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tokenizers/trie.ts @@ -0,0 +1,96 @@ +/** + * @license + * Copyright Big Vision 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. + */ + +// Copied from +// https://github.com/tensorflow/tfjs-models/blob/master/universal-sentence-encoder/src/tokenizer/trie.ts + +import {stringToChars} from './common'; + +// [token, score, index] +type OutputNode = [string[], number, number]; + +class TrieNode { + parent: TrieNode|null; + end: boolean; + children: {[firstSymbol: string]: TrieNode}; + word: OutputNode; + + constructor() { + this.parent = null; + this.children = {}; + this.end = false; + this.word = [[], 0, 0]; + } +} + +/** + * Simple Trie datastructure. + */ +export class Trie { + root: TrieNode; + + constructor() { + this.root = new TrieNode(); + } + + /** + * Inserts a token into the trie. + */ + insert(word: string, score: number, index: number) { + let node = this.root; + + const symbols = stringToChars(word); + + for (let i = 0; i < symbols.length; i++) { + if (!node.children[symbols[i]]) { + node.children[symbols[i]] = new TrieNode(); + node.children[symbols[i]].parent = node; + node.children[symbols[i]].word[0] = node.word[0].concat(symbols[i]); + } + + node = node.children[symbols[i]]; + if (i === symbols.length - 1) { + node.end = true; + node.word[1] = score; + node.word[2] = index; + } + } + } + + /** + * Returns an array of all tokens starting with ss. + * + * @param ss The prefix to match on. + */ + commonPrefixSearch(ss: string[]): OutputNode[] { + const output: OutputNode[] = []; + let node = this.root.children[ss[0]]; + + for (let i = 0; i < ss.length && node; i++) { + if (node.end) { + output.push(node.word); + } + node = node.children[ss[i + 1]]; + } + + if (!output.length) { + output.push([[ss[0]], 0, 0]); + } + + return output; + } +} diff --git a/big_vision/tools/lit_demo/src/tsconfig.json b/big_vision/tools/lit_demo/src/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..f77877754a883975f3680f4cdcdc7df1f0c5e796 --- /dev/null +++ b/big_vision/tools/lit_demo/src/tsconfig.json @@ -0,0 +1,41 @@ +{ + "compilerOptions": { + "outDir": "dist", + "target": "es6", + "module": "commonjs", + "lib": ["dom", "DOM.Iterable", "es2019", "es2020.string"], + "types": ["node", "jasmine", "resize-observer-browser"], + "moduleResolution": "node", + "allowJs": false, + "pretty": true, + "resolveJsonModule": true, + "sourceMap": false, + "skipLibCheck": true, + "removeComments": true, + "esModuleInterop": true, + "importsNotUsedAsValues": "preserve", + "downlevelIteration": true, + "skipDefaultLibCheck": true, + "preserveConstEnums": false, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noErrorTruncation": false, + "noEmitOnError": false, + "declaration": false, + "stripInternal": true, + "inlineSourceMap": true, + "inlineSources": true, + "importHelpers": true, + "allowUnreachableCode": false, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": false, + "noImplicitThis": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "strictNullChecks": false, + "strictPropertyInitialization": false + }, + "include": ["./client", "./examples"], + "compileOnSave": false +} diff --git a/big_vision/train.py b/big_vision/train.py new file mode 100644 index 0000000000000000000000000000000000000000..42381890a3f26068da3f98afcc711b1ef00fb049 --- /dev/null +++ b/big_vision/train.py @@ -0,0 +1,522 @@ +# Copyright 2024 Big Vision 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. + +"""Training loop example. + +This is a basic variant of a training loop, good starting point for fancy ones. +""" +# pylint: disable=consider-using-from-import +# pylint: disable=logging-fstring-interpolation + +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.utils as u +from clu import parameter_overview +import flax.linen as nn +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +from jax.experimental.shard_map import shard_map +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def main(argv): + del argv + + # This is needed on multihost systems, but crashes on non-TPU single-host. + if os.environ.get("BV_JAX_INIT"): + jax.distributed.initialize() + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + # Allow for things like timings as early as possible! + u.chrono.inform(measure=mw.measure, write_note=write_note) + +################################################################################ +# # +# Set up Mesh # +# # +################################################################################ + + # We rely on jax mesh_utils to organize devices, such that communication + # speed is the fastest for the last dimension, second fastest for the + # penultimate dimension, etc. + config_mesh = config.get("mesh", [("data", jax.device_count())]) + + # Sharding rules with default + sharding_rules = config.get("sharding_rules", [("act_batch", "data")]) + + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(jax.devices()).reshape(mesh_size).shape + + device_mesh = mesh_utils.create_device_mesh( + mesh_size, allow_split_physical_axes=config.get( + "mesh_allow_split_physical_axes", False)) + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. The + # order presribed by the `devices_flat` variable should be used throughout + # the program. + devices_flat = device_mesh.flatten() + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices_flat, n_prefetch) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note("Creating model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model( + num_classes=config.num_classes, **config.get("model", {})) + + def init(rng): + batch = jax.tree.map(lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + params = model.init(rng, batch["image"])["params"] + + # Set bias in the head to a low value, such that loss is small initially. + if "init_head_bias" in config: + params["head"]["bias"] = jnp.full_like(params["head"]["bias"], + config["init_head_bias"]) + + return params + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + write_note("Inferring parameter shapes...") + rng, rng_init = jax.random.split(rng) + params_shape = jax.eval_shape(init, rng_init) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, nn.unbox(params_shape), sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + if jax.process_index() == 0: + num_params = sum(np.prod(p.shape) for p in jax.tree.leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Shard & Transfer # +# # +################################################################################ + + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(device_mesh, mesh_axes) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + write_note("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + strategy = config.get("sharding_strategy", [(".*", "replicate")]) + with nn.logical_axis_rules(sharding_rules): + train_state_sharding = bv_sharding.infer_sharding( + train_state_shape, strategy=strategy, mesh=mesh) + + write_note("Transferring train_state to devices...") + # RNG is always replicated + rng_init = u.reshard(rng_init, repl_sharding) + + # Parameters and the optimizer are now global (distributed) jax arrays. + params = jax.jit(init, out_shardings=train_state_sharding["params"])(rng_init) + opt = jax.jit(tx.init, out_shardings=train_state_sharding["opt"])(params) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng # not used anymore, so delete it. + + # At this point we have everything we need to form a train state. It contains + # all the parameters that are passed and updated by the main training step. + # From here on, we have no need for Flax AxisMetadata (such as partitioning). + train_state = nn.unbox({"params": params, "opt": opt}) + del params, opt # Delete to avoid memory leak or accidental reuse. + + write_note("Logging parameter overview...") + parameter_overview.log_parameter_overview( + train_state["params"], msg="Init params", + include_stats="global", jax_logging_process=0) + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, rng, batch): + """Update step.""" + + images, labels = batch["image"], batch["labels"] + + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + + if config.get("mixup") and config.mixup.p: + # The shard_map below makes mixup run on every device independently and + # thus avoids unnecessary communication. + sharded_mixup_fn = shard_map( + u.get_mixup(rng, config.mixup.p), + mesh=jax.sharding.Mesh(devices_flat, ("data",)), + in_specs=P("data"), out_specs=(P(), P("data"), P("data"))) + rng, (images, labels), _ = sharded_mixup_fn(images, labels) + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + + def loss_fn(params): + logits, _ = model.apply( + {"params": params}, images, + train=True, rngs={"dropout": rng_model}) + return getattr(u, config.get("loss", "sigmoid_xent"))( + logits=logits, labels=labels) + + params, opt = train_state["params"], train_state["opt"] + loss, grads = jax.value_and_grad(loss_fn)(params) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + measurements = {"training_loss": loss} + gs = jax.tree.leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.sum(g * g) for g in gs])) + ps = jax.tree.leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.sum(p * p) for p in ps])) + us = jax.tree.leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.sum(u * u) for u in us])) + + return {"params": params, "opt": opt}, measurements + +################################################################################ +# # +# Load Checkpoint # +# # +################################################################################ + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + ckpt_mngr = None + if save_ckpt_path or resume_ckpt_path: + ckpt_mngr = array_serial.GlobalAsyncCheckpointManager() + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + jax.tree.map(lambda x: x.delete(), train_state) + del train_state + shardings = { + **train_state_sharding, + "chrono": jax.tree.map(lambda _: repl_sharding, + u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + # TODO: when updating the `load` API soon, do pass and request the + # full `train_state` from it. Examples where useful: VQVAE, BN. + train_state["params"] = model_mod.load( + train_state["params"], config.model_init, config.get("model"), + **config.get("model_load", {})) + + # load has the freedom to return params not correctly sharded. Think of for + # example ViT resampling position embedings on CPU as numpy arrays. + train_state["params"] = u.reshard( + train_state["params"], train_state_sharding["params"]) + + parameter_overview.log_parameter_overview( + train_state["params"], msg="restored params", + include_stats="global", jax_logging_process=0) + + +################################################################################ +# # +# Setup Evals # +# # +################################################################################ + + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def eval_logits_fn(train_state, batch): + logits, out = model.apply({"params": train_state["params"]}, batch["image"]) + return logits, out + + def eval_loss_fn(train_state, batch): + logits, _ = model.apply({"params": train_state["params"]}, batch["image"]) + loss_fn = getattr(u, config.get("loss", "sigmoid_xent")) + return { + "loss": loss_fn(logits=logits, labels=batch["labels"], reduction=False) + } + + eval_fns = { + "predict": eval_logits_fn, + "loss": eval_loss_fn, + } + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, eval_fns, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, nn.logical_axis_rules(sharding_rules): + train_state, measurements = update_fn(train_state, rng_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + if not np.isfinite(measurements["training_loss"]): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_ckpt_steps = get_steps("keep_ckpt", None) or total_steps + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree.map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/cappa/generative.py b/big_vision/trainers/proj/cappa/generative.py new file mode 100644 index 0000000000000000000000000000000000000000..a4b8873a45eac7e5c4d5d9dfea8c42abccda8283 --- /dev/null +++ b/big_vision/trainers/proj/cappa/generative.py @@ -0,0 +1,498 @@ +# Copyright 2023 Big Vision 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. + +"""Training loop for CapPa (https://arxiv.org/abs/2306.07915).""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.datasets.core as ds_core +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.trainers.proj.cappa.predict_fns as predict_fns +import big_vision.utils as u +from clu import parameter_overview +import flax +import flax.linen as nn +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +def main(argv): + del argv + + try: + jax.distributed.initialize() + except ValueError as e: + logging.warning('Could not initialize distributed environment: %s', e) + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. We use + # jax utils to infer device order that will be used throughout the program. + config = flags.FLAGS.config + num_sharded_replicas = config.get("num_sharded_replicas", 1) + assert jax.device_count() % num_sharded_replicas == 0, ( + num_sharded_replicas, jax.device_count()) + devices = mesh_utils.create_device_mesh( + (num_sharded_replicas, jax.device_count() // num_sharded_replicas)) + devices_flat = devices.reshape(-1) + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size, + measure=mw.measure, write_note=write_note) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices_flat, n_prefetch) + + # For mixed data, add per-dataset epoch and examples seen measurements. + if isinstance(config.input.data.get("name"), str): + measure_per_dataset_times = lambda step: None # No-op + else: + nexamples = { + name: ds_core.get(**config.input[name].data).total_examples + for name in config.input.data + } + def measure_per_dataset_times(step): + total = sum(config.input.data.values()) + for name, w in config.input.data.items(): + w = w / total + mw.measure(f"examples_seen_{name}", u.chrono.accum_examples_seen * w) + mw.measure(f"epoch_{name}", step * batch_size * w / nexamples[name]) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**config.get("model", {})) + + def init(rng): + bs = batch_size // jax.device_count() + img_shape = (bs,) + tuple(train_ds.element_spec["image"].shape[1:]) + txt_shape = (bs,) + tuple(train_ds.element_spec["labels"].shape[1:]) + dummy_img = jnp.zeros(img_shape, jnp.float32) + dummy_txt = jnp.zeros(txt_shape, jnp.int64) + variables = model.init(rng, dummy_img, dummy_txt) + return flax.core.unfreeze(variables["params"]) + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + rng, rng_init = jax.random.split(rng) + with u.chrono.log_timing("z/secs/init"): + write_note("Inferring parameter shapes...") + params_shape = jax.eval_shape(init, rng_init) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, params_shape, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + num_params = sum(np.prod(p.shape) for p in jax.tree_leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Shard & Transfer # +# # +################################################################################ + + # Using a 2D mesh where the model/optimizer parameters are replicated along + # the "replica" axis and sharded along the "fsdp" axis if the sharding is set + # to "fully_sharded" for the model/optimizer. + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(devices, ("replica", "fsdp")) + repl_sharding = jax.sharding.NamedSharding(mesh, jax.sharding.PartitionSpec()) + + write_note("Inferring shardings...") + params_sharding = bv_sharding.infer_sharding( + params_shape, mesh, axis_name="fsdp", + # TODO: implement scan for parameter sharding. + strategy=config.get("param_sharding", "replicated"), + extra_strategy_args=config.get("param_sharding_args", {})) + opt_sharding = bv_sharding.infer_sharding( + opt_shape, mesh, axis_name="fsdp", + strategy=config.get("optim_sharding", "replicated"), + extra_strategy_args=config.get("optim_sharding_args", {})) + + write_note("Transferring train_state to devices...") + # RNG is always replicated + rng_init = u.reshard(rng_init, repl_sharding) + + # Parameters and the optimizer are now global (distributed) jax arrays. + params = jax.jit(init, out_shardings=params_sharding)(rng_init) + opt = jax.jit(tx.init, out_shardings=opt_sharding)(params) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng # not used anymore, so delete it. + + # At this point we have everything we need to form a train state. It contains + # all the parameters that are passed and updated by the main training step. + train_state_sharding = { + "params": params_sharding, "opt": opt_sharding, "rng": repl_sharding} + train_state = { + "params": params, "opt": opt, "rng": rng_loop} + del params, opt, rng_loop # Delete to avoid memory leak or accidental reuse. + + write_note("Logging parameter overview...") + parameter_overview.log_parameter_overview( + train_state["params"], msg="Init params", + include_stats="global", jax_logging_process=0) + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, batch): + """Update step.""" + + images, labels, label_masks = ( + batch["image"], batch["labels"], batch.get("label_masks")) + + # Get device-specific loss rng. + rng = train_state["rng"] + rng, rng_model = jax.random.split(rng, 2) + + def loss_fn(params): + logits = model.apply( + {"params": params}, images, labels, + train=True, rngs={"dropout": rng_model}) + + weights = jnp.where(labels != config.get("pad_token", 0), 1.0, 0.0) + if label_masks is not None: + weights = weights * label_masks + + loss = u.weighted_softmax_xent( + logits=logits, labels=labels, + weights=weights, label_smoothing=config.get("label_smoothing", 0.0), + reduction=True, normalize=True) + + return loss + + params, opt = train_state["params"], train_state["opt"] + loss, grads = jax.value_and_grad(loss_fn)(params) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + measurements = {"training_loss": loss} + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.sum(g * g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.sum(p * p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.sum(u * u) for u in us])) + + return {"params": params, "opt": opt, "rng": rng}, measurements + +################################################################################ +# # +# Load Checkpoint # +# # +################################################################################ + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + ckpt_mngr = None + if save_ckpt_path or resume_ckpt_path: + ckpt_mngr = array_serial.GlobalAsyncCheckpointManager() + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + jax.tree_map(lambda x: x.delete(), train_state) + del train_state + shardings = { + **train_state_sharding, + "chrono": jax.tree_map(lambda _: repl_sharding, + u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + train_state["params"] = model_mod.load( + train_state["params"], config.model_init, config.get("model"), + **config.get("model_load", {})) + + # load has the freedom to return params not correctly sharded. Think of for + # example ViT resampling position embedings on CPU as numpy arrays. + train_state["params"] = u.reshard( + train_state["params"], train_state_sharding["params"]) + + parameter_overview.log_parameter_overview( + train_state["params"], msg="restored params", + include_stats="global", jax_logging_process=0) + +################################################################################ +# # +# Setup Evals # +# # +################################################################################ + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, + predict_fns.get_predict_fns(model), + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules([("act_batch", ("replica", "fsdp"))]): # pytype: disable=wrong-arg-types + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, nn.logical_axis_rules([("act_batch", ("replica", "fsdp"))]): # pytype: disable=wrong-arg-types + train_state, measurements = update_fn(train_state, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + measure_per_dataset_times(step) + + if not np.isfinite(measurements["training_loss"]): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_ckpt_steps = get_steps("keep_ckpt", None) or total_steps + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree_map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules( + [("act_batch", ("replica", "fsdp"))]): # pytype: disable=wrong-arg-types + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/cappa/predict_fns.py b/big_vision/trainers/proj/cappa/predict_fns.py new file mode 100644 index 0000000000000000000000000000000000000000..4e369f23cd840370cdd419b5e67f377e2dcf4ad1 --- /dev/null +++ b/big_vision/trainers/proj/cappa/predict_fns.py @@ -0,0 +1,118 @@ +# Copyright 2023 Big Vision 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. + +"""Prediction functions for clippo/generative.py.""" + +import functools + +import big_vision.pp.ops_text as pp_ops_text +import big_vision.utils as u +import jax +import jax.numpy as jnp +import numpy as np + +# pylint: disable=missing-function-docstring + + +# We do not jit/pmap this function, because it is passed to evaluator that +# does it later. We output as many intermediate tensors as possible for +# maximal flexibility. Later `jit` will prune out things that are not needed. +def predict_fn_perplexity( + train_state, batch, *, model): + logits = model.apply( + {"params": train_state["params"]}, + batch["image"], + batch["labels"], + train=False, + ) + return logits, {"logits": logits} + + +def predict_fn_enc_rep(train_state, batch, *, model): + logits, out = model.apply( + {"params": train_state["params"]}, + batch["image"], + None, + train=False, + return_enc_features=True, + ) + return logits, out + + +def predict_fn_score( + train_state, batch, *, model, prompt="", prompt_tokenizer=""): + """For a batch of images, return score (LL) for each image-label pair.""" + encoded = model.apply( + {"params": train_state["params"]}, + batch["image"], + train=False, + method=model.encode, + ) + + # This needs to be added by the evaluator. It is the pre-computed tokenized + # list of all available labels. For ImageNet-1k, that's (1000, 13). + all_labels = batch["_label_tokens"] + + # Optionally prefix a single prompt to all labels: + if prompt and prompt_tokenizer: + prompt = make_prompt(prompt, prompt_tokenizer) # Note: this is cached. + prompts = jnp.tile(prompt, (all_labels.shape[0], 1)) + all_labels = jnp.concatenate([prompts, all_labels], axis=-1) + # For ImageNet-1k and a prompt of length 2, we now have (1000, 15). + + def score_label(label): + """Score (LogLik) each minibatch example (image) with a single `label`.""" + label_rep = jnp.tile(label, (encoded.shape[0], 1)) + logits = model.apply( + {"params": train_state["params"]}, + encoded, + label_rep, + train=False, + decode=False, + method=model.decode, + ) + # The returned value is (batch,) scalars, the score each image has with + # this label. We turn the softmax_xent's NLL into LL so higher = better. + return -u.weighted_softmax_xent( + logits=logits, + labels=label_rep, + weights=(label_rep > 0).astype(jnp.float32), # Ignore (=0). + reduction=False, + normalize=False, + ) + + # Use lax.map() instead of vmap() to conserve memory. + nlls = jax.lax.map(score_label, all_labels) # -> (nlabel, batch) + return nlls.T # -> (batch, nlabel) array of scores. + + +@functools.cache +def make_prompt(prompt, tokenizer_path, seq_len=None): + """Tokenizes `prompt` with specified tokenizer, with optional padding.""" + tokenizer = pp_ops_text.create_tokenizer(tokenizer_path, add_eos=False) + + prompt = tokenizer.tokenize(prompt).numpy() + if seq_len: + prompt = np.pad(prompt, (0, seq_len - len(prompt))).astype(np.int32) + return prompt + + +def get_predict_fns(model): + """Returns `predict_fns` for evaluators.""" + fns = { + "perplexity": predict_fn_perplexity, + "score": predict_fn_score, + "enc_rep": predict_fn_enc_rep, + } + return {name: functools.partial(fn, model=model) for name, fn in fns.items()} diff --git a/big_vision/trainers/proj/distill/distill.py b/big_vision/trainers/proj/distill/distill.py new file mode 100644 index 0000000000000000000000000000000000000000..e42fbc2c7b9c72c1ba78d1dddc949f65fc90be69 --- /dev/null +++ b/big_vision/trainers/proj/distill/distill.py @@ -0,0 +1,473 @@ +# Copyright 2024 Big Vision 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. + +"""Training loop for distillation as in https://arxiv.org/abs/2106.05237. + +It works by having a (set of) teacher model(s) defined the same way as student +in the config and, for now, only distilling logits with one of many loss +functions. + +We explored distilling intermediate feature maps, extra data, and other tricks +in depth in two interships in a separate prototype codebase but eventually they +are not necessary, and thus not (yet?) implemented in this codebase. + +Thus, for now, there are no extra learnable parameters besides the student. +This keeps code relatively simple. +""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.evaluators.proj.distill.distance as dd +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +from jax.experimental import mesh_utils +import jax.numpy as jnp +from jax.sharding import NamedSharding +from jax.sharding import PartitionSpec as P +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +def getfirst(d, *keys): + """Returns the first of `keys` that's present in mapping `d`.""" + result, found = None, False + for k in reversed(keys): + if k in d: + result, found = d[k], True + if found: + return result + else: + raise KeyError(f"None of {keys} is in {d.keys()}") + + +def main(argv): + del argv + tf.config.experimental.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + devices = mesh_utils.create_device_mesh((jax.device_count(),)) + mesh = jax.sharding.Mesh(devices, ("data",)) + repl_sharding = NamedSharding(mesh, P()) + + write_note("Initializing train dataset...") + train_ds, ntrain_img = input_pipeline.training(config.input) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices, n_prefetch) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size, + measure=mw.measure, write_note=write_note) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Create student and teacher models + def get_model_mod(name): # Used many times. + mod_name = config[f"{name}_name"] + return importlib.import_module(f"big_vision.models.{mod_name}") + + write_note("Initializing models...") + def make_model(name): + return get_model_mod(name).Model( + num_classes=config.num_classes, **config.get(name, {})) + + models = { + "student": make_model("student"), + **{t: make_model(t) for t in config.teachers} + } + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + def get_init(model, name): + @functools.partial(jax.jit, backend="cpu") + def _init(rng): + bs = batch_size // jax.device_count() + img_size = tuple(getfirst(train_ds.element_spec, name, "image").shape[1:]) + no_image = jnp.zeros((bs,) + img_size, jnp.float32) + params = flax.core.unfreeze(model.init(rng, no_image))["params"] + + # Set bias in the head to a low value, such that loss is small initially. + if "init_head_bias" in config: + params["head"]["bias"] = jnp.full_like(params["head"]["bias"], + config["init_head_bias"]) + return params + return _init + + rng, *rng_inits = jax.random.split(rng, len(models) + 1) + with u.chrono.log_timing("z/secs/init"): + params_cpu = { + name: get_init(models[name], name=name)(r) + for name, r in zip(models, rng_inits)} + + if jax.process_index() == 0: + for name, params in params_cpu.items(): + parameter_overview.log_parameter_overview(params, msg=f"{name} params") + mw.measure(f"num_params_{name}", + sum(p.size for p in jax.tree_leaves(params))) + + write_note(f"Initializing {config.optax_name} optimizer...") + # For now, we explicitly only optimize the student parameters as there's + # nothing else to be optimized. If we ever want to add learnable projections + # or similar for good (we explored but ditched), need to refactor this a bit. + tx, sched_fns = bv_optax.make( + config, params_cpu["student"], sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu["student"]) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @jax.named_call + def loss_fn(student_params, params, data, rngs): + # Note: need to extract and use `student_params` out of `params` because the + # first argument of `loss_fn` is what's differentiated wrt. + params["student"] = student_params + + def fwd(name, params): + return jax.named_call(models[name].apply, name=name)( + {"params": params}, getfirst(data, name, "image"), + train=name == "student", rngs=rngs.get(name) + )[0] # logits, unused_outputs + logits = {name: fwd(name, w) for name, w in params.items()} + + measurements = {} + for name, lg in logits.items(): + measurements[f"entropy_{name}"] = -jnp.sum( + jax.nn.log_softmax(lg) * jax.nn.softmax(lg), axis=-1) + if "labels" in data: + measurements[f"task_loss_{name}"] = u.softmax_xent( + logits=lg, labels=data["labels"], reduction=False) + + # NOTE: xent is linear in labels, so for KL, this is actually the same as + # using a teacher-ensemble in probs-space! + measurements["distill_loss"] = 0.0 + for name in config.teachers: + l = dd.dist(logits["student"], logits[name], config.get("distance", "kl"), + **config.get("distance_kw", {})) + measurements[f"distill_loss_{name}"] = l + measurements["distill_loss"] += l + + outputs = (measurements["distill_loss"], measurements) + return jax.tree_map(jnp.mean, outputs) + + @functools.partial( + jax.jit, donate_argnums=(0, 1, 2), out_shardings=repl_sharding + ) + def update_fn(params, opt, rng, data): + """Update step.""" + + # Mixup. Note: overwrites the `data` entries (that's intended). + if config.get("mixup") and config.mixup.p: + to_mix = {name: data[name] + for name in ("image", "labels") + tuple(models) if name in data} + rng, _, to_mix = u.mixup(rng, **config.mixup, **to_mix) + data = {**data, **to_mix} + + # Get model-specific loss rng. + rng, *rng_models = jax.random.split(rng, len(models) + 1) + rngs_model_dicts = { + name: {"dropout": rngi} for name, rngi in zip(models, rng_models) + } + + w = params["student"] # Need to explicitly pull out the optimized ones. + (l, measurements), grads = jax.value_and_grad(loss_fn, has_aux=True)( + w, params, data, rngs=rngs_model_dicts + ) + updates, opt = tx.update(grads, opt, w) + w = optax.apply_updates(w, updates) + params["student"] = w + + # Take some logging measurements + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(w) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, rng, l, measurements + + # We always load the teachers first, because they NEED to be initialized + # and since we don't ever modify them, we don't store them in checkpoints. + for name in config.teachers: + init_def = config[f"{name}_init"] + write_note(f"Initializing {name} from {init_def}…") + params_cpu[name] = get_model_mod(name).load( + params_cpu[name], init_def, config[name], + **config.get(f"{name}_load", {})) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize student from something, e.g. start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + # NOTE: we never change the teachers, so only checkpoint student here. + checkpoint = { + "params": params_cpu["student"], + "opt": opt_cpu, + "chrono": u.chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint_np(resume_ckpt_path, checkpoint_tree) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu["student"], opt_cpu = checkpoint["params"], checkpoint["opt"] + u.chrono.load(checkpoint["chrono"]) + elif config.get("student_init"): + write_note(f"Initialize student from {config.student_init}...") + params_cpu["student"] = get_model_mod("student").load( + params_cpu["student"], config.student_init, config.get("student"), + **config.get("student_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu["student"], msg="restored (student) params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + u.chrono.inform(first_step=first_step) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{u.chrono.note}") + params_repl = u.reshard(params_cpu, repl_sharding) + opt_repl = u.reshard(opt_cpu, repl_sharding) + + # Define predict functions that the evaluators can use: + # 1. One per model + predict_fns = {} + for name, model in models.items(): + def fwd(train_state, batch, n=name, m=model): + return m.apply({"params": train_state["params"][n]}, batch["image"]) + predict_fns[f"{name}_fwd"] = fwd + # 2. One for the ensemble of all teachers. + def teacher_ensemble_fwd(train_state, batch): + all_teacher_logits = [ + models[name].apply(train_state["params"][name], batch["image"])[0] + for name in config.teachers + ] + return jnp.mean([jax.nn.softmax(l) for l in all_teacher_logits], axis=0), {} # pytype: disable=wrong-arg-types # jnp-type + predict_fns["teacher_ensemble_fwd"] = teacher_ensemble_fwd + # 3.One for each (student, teacher) pair, eg for distance eval. + for name in [*config.teachers, "teacher_ensemble"]: + def fwd(train_state, batch, n=name): # pylint: disable=function-redefined + student_ret = predict_fns["student_fwd"](train_state, batch) + teacher_ret = predict_fns[f"{n}_fwd"](train_state, batch) + return student_ret, teacher_ret + predict_fns[f"student_{name}_fwd"] = fwd + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, + predict_fns, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices, + ) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = u.reshard(rng_loop, repl_sharding) + ckpt_writer = None + + write_note(f"First step compilations...\n{u.chrono.note}") + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run({"params": params_repl}): + mw.measure(f"{prefix}{key}", value) + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + params_repl, opt_repl, rngs_loop, loss_value, measurements = update_fn( + params_repl, opt_repl, rngs_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + if not np.isfinite(l): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + u.chrono.pause(wait_for=(params_repl["student"], opt_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu["student"], opt_cpu = jax.tree_map( + np.array, (params_repl["student"], opt_repl) + ) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = {"params": params_cpu["student"], + "opt": opt_cpu, + "chrono": u.chrono.save()} + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=params_repl) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run({"params": params_repl}): + mw.measure(f"{prefix}{key}", value) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/flexi/common.py b/big_vision/trainers/proj/flexi/common.py new file mode 100644 index 0000000000000000000000000000000000000000..2dd59883fb72b171a902a758f3aa578047dd0c3d --- /dev/null +++ b/big_vision/trainers/proj/flexi/common.py @@ -0,0 +1,47 @@ +# Copyright 2024 Big Vision 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. + +"""Few common utils used in both/all flexi-trainers.""" +import functools +import itertools +import numpy as np + + +def mkrng(xid, wid, step): + # Need to cap at 0, for example localruns use -1. + rng_key = (max(xid, 0), max(wid, 0), max(step, 0)) + return np.random.default_rng(rng_key) + + +def mkprob(x): + if x is None: + return x + return np.array(x) / np.sum(x) + + +def choice(values, ratios, rng=None): + rng = rng or np.random.default_rng() + return rng.choice(values, p=mkprob(ratios)) + + +def mkpredictfns(predict_fn, config, template="predict_{x}"): + # If we have two flexi args a=[1,2], b=[10,20], then we create a + # predict_fn for all possible combinations, named "predict_a=1_b=10" etc. + all_combinations = [dict(comb) for comb in itertools.product( + *[[(arg, val) for val in config[arg].v] for arg in config] + )] + return { + template.format(x="_".join(f"{k}={v}" for k, v in kw.items())): + functools.partial(predict_fn, **kw) + for kw in all_combinations} diff --git a/big_vision/trainers/proj/flexi/distill.py b/big_vision/trainers/proj/flexi/distill.py new file mode 100644 index 0000000000000000000000000000000000000000..00ce8ad93c32abd13bc8785bb9999a7be9ca9645 --- /dev/null +++ b/big_vision/trainers/proj/flexi/distill.py @@ -0,0 +1,464 @@ +# Copyright 2024 Big Vision 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. + +"""Distill a teacher model into a FlexiViT student. + +Note this file has code that is generic enough to allow using an ensemble +of teachers. This is inherited from `proj/distill/distill.py` and the goal +to only make minimal changes in a fork of that file. However, this feature +does not really make sense for FlexiViT. +""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.evaluators.proj.distill.distance as dd +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.trainers.proj.flexi.common as flexi +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def getfirst(d, *keys): + """Returns the first of `keys` that's present in mapping `d`.""" + result, found = None, False + for k in reversed(keys): + if k in d: + result, found = d[k], True + if found: + return result + else: + raise KeyError(f"None of {keys} is in {d.keys()}") + + +def main(argv): + del argv + tf.config.experimental.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + write_note("Initializing train dataset...") + train_ds, ntrain_img = input_pipeline.training(config.input) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size, + measure=mw.measure, write_note=write_note) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Create student and teacher models + def get_model_mod(name): # Used many times. + mod_name = config[f"{name}_name"] + return importlib.import_module(f"big_vision.models.{mod_name}") + + write_note("Initializing models...") + def make_model(name): + return get_model_mod(name).Model( + num_classes=config.num_classes, **config.get(name, {})) + + models = { + "student": make_model("student"), + **{t: make_model(t) for t in config.teachers} + } + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + def get_init(model, name): + @functools.partial(jax.jit, backend="cpu") + def _init(rng): + bs = batch_size // jax.device_count() + img_size = tuple(getfirst(train_ds.element_spec, name, "image").shape[1:]) + no_image = jnp.zeros((bs,) + img_size, jnp.float32) + params = flax.core.unfreeze(model.init(rng, no_image))["params"] + return params + return _init + + rng, *rng_inits = jax.random.split(rng, len(models) + 1) + with u.chrono.log_timing("z/secs/init"): + params_cpu = { + name: get_init(models[name], name=name)(r) + for name, r in zip(models, rng_inits)} + + if jax.process_index() == 0: + for name, params in params_cpu.items(): + parameter_overview.log_parameter_overview(params, msg=f"{name} params") + mw.measure(f"num_params_{name}", + sum(p.size for p in jax.tree_leaves(params))) + + write_note(f"Initializing {config.optax_name} optimizer...") + # For now, we explicitly only optimize the student parameters as there's + # nothing else to be optimized. If we ever want to add learnable projections + # or similar for good (we explored but ditched), need to refactor this a bit. + tx, sched_fns = bv_optax.make( + config, params_cpu["student"], sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu["student"]) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @jax.named_call + def loss_fn(student_params, params, data, rngs, **flexi_kw): + # Note: need to extract and use `student_params` out of `params` because the + # first argument of `loss_fn` is what's differentiated wrt. + params["student"] = student_params + + def fwd(name, params): + return jax.named_call(models[name].apply, name=name)( + {"params": params}, getfirst(data, name, "image"), + train=name == "student", rngs=rngs.get(name), + **(flexi_kw if name == "student" else {}) + )[0] # logits, unused_outputs + logits = {name: fwd(name, w) for name, w in params.items()} + + measurements = {} + for name, lg in logits.items(): + measurements[f"entropy_{name}"] = -jnp.sum( + jax.nn.log_softmax(lg) * jax.nn.softmax(lg), axis=-1) + if "labels" in data: + measurements[f"task_loss_{name}"] = u.softmax_xent( + logits=lg, labels=data["labels"], reduction=False) + + # NOTE: xent is linear in labels, so for KL, this is actually the same as + # using a teacher-ensemble in probs-space! + measurements["distill_loss"] = 0.0 + for name in config.teachers: + l = dd.dist(logits["student"], logits[name], config.get("distance", "kl"), + **config.get("distance_kw", {})) + measurements[f"distill_loss_{name}"] = l + measurements["distill_loss"] += l + + outputs = (measurements["distill_loss"], measurements) + return jax.tree_map(jnp.mean, outputs) + + flexi_argnames = sorted(config.flexi) + + @functools.partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1), + static_broadcasted_argnums=tuple(range(4, 4 + len(flexi_argnames)))) + def update_fn(params, opt, rng, data, *args): + """Update step.""" + + # Mixup. Note: overwrites the `data` entries (that's intended). + if config.get("mixup") and config.mixup.p: + to_mix = {name: data[name] + for name in ("image", "labels") + tuple(models) if name in data} + rng, _, to_mix = u.mixup(rng, **config.mixup, **to_mix) + data = {**data, **to_mix} + + # Get device-specific loss rng. + rng, *rng_models = jax.random.split(rng, len(models) + 1) + rngs_models_local = { + name: {"dropout": jax.random.fold_in(rngi, jax.lax.axis_index("batch"))} + for name, rngi in zip(models, rng_models) + } + + w = params["student"] # Need to explicitly pull out the optimized ones. + (l, measurements), grads = jax.lax.pmean( + jax.value_and_grad(loss_fn, has_aux=True)( + w, params, data, rngs=rngs_models_local, + **dict(zip(flexi_argnames, args))), + axis_name="batch") + updates, opt = tx.update(grads, opt, w) + w = optax.apply_updates(w, updates) + params["student"] = w + + # Take some logging measurements + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(w) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, rng, l, measurements + + # We always load the teachers first, because they NEED to be initialized + # and since we don't ever modify them, we don't store them in checkpoints. + for name in config.teachers: + init_def = config[f"{name}_init"] + write_note(f"Initializing {name} from {init_def}…") + params_cpu[name] = get_model_mod(name).load( + params_cpu[name], init_def, config[name], + **config.get(f"{name}_load", {})) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize student from something, e.g. start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + # NOTE: we never change the teachers, so only checkpoint student here. + checkpoint = { + "params": params_cpu["student"], + "opt": opt_cpu, + "chrono": u.chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint_np(resume_ckpt_path, checkpoint_tree) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu["student"], opt_cpu = checkpoint["params"], checkpoint["opt"] + u.chrono.load(checkpoint["chrono"]) + elif config.get("student_init"): + write_note(f"Initialize student from {config.student_init}...") + params_cpu["student"] = get_model_mod("student").load( + params_cpu["student"], config.student_init, config.get("student"), + **config.get("student_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu["student"], msg="restored (student) params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + u.chrono.inform(first_step=first_step) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{u.chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + + # Define predict functions that the evaluators can use: + def predict_fn(params, *, name, **kw): + image = kw.pop(name, kw.pop("image", None)) + # Ugly API compatibility necessity: + for k in ("student", *config.teachers): + kw.pop(k, 0) + return models[name].apply({"params": params[name]}, image, **kw) + + # 1. One for each variant of the student + student_pfns = flexi.mkpredictfns( + functools.partial(predict_fn, name="student"), config.flexi, "student_{x}" + ) + # 2. One per teacher model + teacher_pfns = { + name: functools.partial(predict_fn, name=name) + for name in config.teachers + } + # 3. One for each (student-variant, teacher) pair, eg for distance eval. + combined_pfns = { + f"{sn}_{tn}": lambda *a, sfn=sfn, tfn=tfn, **kw: (sfn(*a, **kw), tfn(*a, **kw)) # pylint: disable=line-too-long + for sn, sfn in student_pfns.items() + for tn, tfn in teacher_pfns.items() + } + + predict_fns = {**student_pfns, **teacher_pfns, **combined_pfns} + + @functools.cache + def evaluators(): + return eval_common.from_config( + config, predict_fns, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + ) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = flax.jax_utils.replicate(rng_loop) + ckpt_writer = None + + write_note(f"First step compilations...\n{u.chrono.note}") + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + np_rng = flexi.mkrng(xid, wid, step) + flexi_args = [ + flexi.choice(config.flexi[n].v, config.flexi[n].p, np_rng) + for n in flexi_argnames + ] + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + params_repl, opt_repl, rngs_loop, loss_value, measurements = update_fn( + params_repl, opt_repl, rngs_loop, batch, *flexi_args) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + u.chrono.tick(step) + if not np.isfinite(l): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + u.chrono.pause(wait_for=(params_repl["student"], opt_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu["student"], opt_cpu = jax.tree_map( + lambda x: np.array(x[0]), (params_repl["student"], opt_repl)) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = {"params": params_cpu["student"], + "opt": opt_cpu, + "chrono": u.chrono.save()} + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=params_repl) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/flexi/train.py b/big_vision/trainers/proj/flexi/train.py new file mode 100644 index 0000000000000000000000000000000000000000..5e76265271e3cd0db97851e93e3d6bf09a11670f --- /dev/null +++ b/big_vision/trainers/proj/flexi/train.py @@ -0,0 +1,365 @@ +# Copyright 2024 Big Vision 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. + +"""Training loop with flexible/schedulable settings.""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.trainers.proj.flexi.common as flexi +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def main(argv): + del argv + tf.config.experimental.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + write_note("Initializing train dataset...") + train_ds, ntrain_img = input_pipeline.training(config.input) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size, + measure=mw.measure, write_note=write_note) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model( + num_classes=config.num_classes, **config.get("model", {})) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @functools.partial(jax.jit, backend="cpu") + def init(rng): + shape = tuple(train_ds.element_spec["image"].shape[1:]) + bs = batch_size // jax.device_count() + dummy_input = jnp.zeros((bs,) + shape, jnp.float32) + params = flax.core.unfreeze(model.init(rng, dummy_input))["params"] + + # Set bias in the head to a low value, such that loss is small initially. + if "init_head_bias" in config: + params["head"]["bias"] = jnp.full_like(params["head"]["bias"], + config["init_head_bias"]) + + return params + + rng, rng_init = jax.random.split(rng) + with u.chrono.log_timing("z/secs/init"): + params_cpu = init(rng_init) + + if jax.process_index() == 0: + num_params = sum(p.size for p in jax.tree_leaves(params_cpu)) + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + mw.measure("num_params", num_params) + + write_note(f"Initializing {config.optax_name} optimizer...") + tx, sched_fns = bv_optax.make(config, params_cpu, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + flexi_argnames = sorted(config.flexi) + + @functools.partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1), + static_broadcasted_argnums=tuple(range(5, 5 + len(flexi_argnames)))) + def update_fn(params, opt, rng, images, labels, *args): + """Update step.""" + + measurements = {} + + if config.get("mixup") and config.mixup.p: + rng, (images, labels), _ = u.mixup(rng, images, labels, **config.mixup) + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + rng_model_local = jax.random.fold_in(rng_model, jax.lax.axis_index("batch")) + + def loss_fn(params, images, labels): + logits, _ = model.apply( + {"params": params}, images, + train=True, rngs={"dropout": rng_model_local}, + **dict(zip(flexi_argnames, args))) + return getattr(u, config.get("loss", "sigmoid_xent"))( + logits=logits, labels=labels) + + l, grads = jax.value_and_grad(loss_fn)(params, images, labels) + l, grads = jax.lax.pmean((l, grads), axis_name="batch") + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, rng, l, measurements + + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def predict_fn(params, image, **flexi_kw): + logits, out = model.apply({"params": params}, image, **flexi_kw) + return logits, out + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + checkpoint = { + "params": params_cpu, + "opt": opt_cpu, + "chrono": u.chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint_np(resume_ckpt_path, checkpoint_tree) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu, opt_cpu = checkpoint["params"], checkpoint["opt"] + u.chrono.load(checkpoint["chrono"]) + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu = model_mod.load( + params_cpu, config.model_init, config.get("model"), + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu, msg="restored params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + u.chrono.inform(first_step=first_step) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{u.chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + + @functools.cache + def evaluators(): + return eval_common.from_config( + config, flexi.mkpredictfns(predict_fn, config.flexi, "predict_{x}"), + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + ) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = flax.jax_utils.replicate(rng_loop) + ckpt_writer = None + + write_note(f"First step compilations...\n{u.chrono.note}") + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + np_rng = flexi.mkrng(xm_xp.id, xm_wu.id, step) + flexi_args = [ + flexi.choice(config.flexi[n].v, config.flexi[n].p, np_rng) + for n in flexi_argnames + ] + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + params_repl, opt_repl, rngs_loop, loss_value, measurements = update_fn( + params_repl, opt_repl, rngs_loop, batch["image"], batch["labels"], + *flexi_args) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + u.chrono.tick(step) + if not np.isfinite(l): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + u.chrono.pause(wait_for=(params_repl, opt_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu = jax.tree_map(lambda x: np.array(x[0]), params_repl) + opt_cpu = jax.tree_map(lambda x: np.array(x[0]), opt_repl) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = {"params": params_cpu, "opt": opt_cpu, "chrono": u.chrono.save()} + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=params_repl) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/givt/generative.py b/big_vision/trainers/proj/givt/generative.py new file mode 100644 index 0000000000000000000000000000000000000000..e571b759161d05c129bcb48719f27a2c57fddd1e --- /dev/null +++ b/big_vision/trainers/proj/givt/generative.py @@ -0,0 +1,719 @@ +# Copyright 2024 Big Vision 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. + +"""Training loop for GIVT-style autoregressive and masked models.""" + +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +from big_vision.models.proj.givt import parallel_decode +import big_vision.models.proj.givt.decode as softar_decode +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.trainers.proj.givt.utils as trainer_utils +from big_vision.trainers.proj.uvim import panoptic_task +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def main(argv): + del argv + + jax.distributed.initialize() + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text", + "proj.uvim.pp_ops", "proj.givt.pp_ops"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + # Allow for things like timings as early as possible! + u.chrono.inform(measure=mw.measure, write_note=write_note) + +################################################################################ +# # +# Set up Mesh # +# # +################################################################################ + + # We rely on jax mesh_utils to organize devices, such that communication + # speed is the fastest for the last dimension, second fastest for the + # penultimate dimension, etc. + config_mesh = config.get("mesh", [("data", jax.device_count())]) + + # Sharding rules with default + sharding_rules = config.get("sharding_rules", [("act_batch", "data")]) + + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(jax.devices()).reshape(mesh_size).shape + + device_mesh = mesh_utils.create_device_mesh(mesh_size) + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. The + # order presribed by the `devices_flat` variable should be used throughout + # the program. + devices_flat = device_mesh.flatten() + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices_flat, n_prefetch) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note(f"Creating {config.vae.model_name} model...") + vae_mod = importlib.import_module( + f"big_vision.models.{config.vae.model_name}") + vae = vae_mod.Model(**config.vae.get("model", {})) + + write_note(f"Creating {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model_config = config.get("model", {}) + model = model_mod.Model(**model_config) + + if config.get("adaptor_name"): + write_note(f"Creating {config.adaptor_name} model...") + adaptor_mod = importlib.import_module( + f"big_vision.models.{config.adaptor_name}") + adaptor = adaptor_mod.Model(num_channels=model_config.out_dim, + **config.adaptor.model) + else: + adaptor = None + + def init(rng): + def _get_dummy_input(input_name, dtype=jnp.int64): + if input_name in train_ds.element_spec: + return jnp.zeros(train_ds.element_spec[input_name].shape, dtype=dtype) + return None + + dummy_img = _get_dummy_input("image", dtype=jnp.float32) + dummy_labels = _get_dummy_input("labels") + dummy_cond_img = _get_dummy_input("cond_image", dtype=jnp.float32) + local_batch_size = dummy_img.shape[0] # pytype: disable=attribute-error + + code_shape = ( + local_batch_size, model_config.seq_len, model_config.out_dim) + dummy_code = jnp.zeros(code_shape, jnp.float32) + + input_mask = model.get_input_mask_training( + jax.random.PRNGKey(0), (local_batch_size, model_config.seq_len) + ) + params = model.init(rng, dummy_code, dummy_labels, image=dummy_cond_img, + input_mask=input_mask)["params"] + + if adaptor is not None: + _, rng_adaptor = jax.random.split(rng) + adaptor_variables = adaptor.init(rng_adaptor, dummy_code) + params_adaptor = flax.core.unfreeze(adaptor_variables["params"]) + params["params_adaptor"] = params_adaptor # store in same dict + + return params + + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + write_note("Inferring parameter shapes...") + rng, rng_init = jax.random.split(rng) + params_shape = jax.eval_shape(init, rng_init) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, params_shape, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + # Training a stage 2 model requires a pretrained stage 1 model. We treat this + # as a constant and do not shard the parameters. + assert "model_init" in config.vae + params_vae = vae_mod.load(None, config.vae.model_init, + **config.vae.get("model_load", {})) + + def vae_encode(images, rng=None, reparametrize=True): + mu, logvar = vae.apply({"params": params_vae}, images, method=vae.encode) + if reparametrize: + assert rng is not None and "dropout" in rng + return vae.apply({"params": params_vae}, mu, logvar, + method=vae.reparametrize, rngs=rng) + return mu + + if jax.process_index() == 0: + num_params = sum(np.prod(p.shape) for p in jax.tree_leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Shard & Transfer # +# # +################################################################################ + + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(device_mesh, mesh_axes) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + write_note("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + strategy = config.get("sharding_strategy", [(".*", "replicate")]) + train_state_sharding = bv_sharding.infer_sharding( + train_state_shape, strategy=strategy, mesh=mesh) + + write_note("Transferring train_state to devices...") + # RNG is always replicated + rng_init = u.reshard(rng_init, repl_sharding) + + # Parameters and the optimizer are now global (distributed) jax arrays. + params = jax.jit(init, out_shardings=train_state_sharding["params"])(rng_init) + opt = jax.jit(tx.init, out_shardings=train_state_sharding["opt"])(params) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng # not used anymore, so delete it. + + # At this point we have everything we need to form a train state. It contains + # all the parameters that are passed and updated by the main training step. + train_state = {"params": params, "opt": opt} + del params, opt # Delete to avoid memory leak or accidental reuse. + + write_note("Logging parameter overview...") + parameter_overview.log_parameter_overview( + train_state["params"], msg="Init params", + include_stats="global", jax_logging_process=0) + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + # Define the loss function + def loss_fn(params, images, labels, cond_images, rng): + rng, rng_dropout = jax.random.split(rng, 2) + rng, rng_mask = jax.random.split(rng, 2) + _, rng_droplabels = jax.random.split(rng, 2) + + rng_dropout = {"dropout": rng_dropout} + + sequence = vae_encode(images, rng_dropout) + if adaptor is not None: + # Use the (invertible) adaptor to map to a new latent sequence + sequence = adaptor.apply({"params": params["params_adaptor"]}, + sequence, method=adaptor.forward) + + b, s, _ = sequence.shape + # This is None for the non-mask style. Otherwise, shape (b, s). + input_mask = model.get_input_mask_training(rng_mask, (b, s)) + drop_labels = model.get_drop_labels(rng_droplabels, batch_size=b) + + _, pdf = model.apply( + {"params": params}, sequence, labels, + image=cond_images, + train=True, + input_mask=input_mask, + drop_labels=drop_labels, + rngs=rng_dropout) + + # Shape: (B, L, out_dim) + nll = -pdf.log_prob(sequence) + metrics = {"nll": nll} + if input_mask is not None: + metrics["fraction_masked_out"] = input_mask.astype(jnp.float32).mean( + axis=1 + ) + if nll.ndim == 3: + input_mask = input_mask[:, :, None] + # Note that `input_mask` is True where we mask out the input (ie replace + # with mask token), so we also only gather nlls at the corresponding + # points. + nll = jnp.where(input_mask, nll, 0.0) + # Take mean only of the spots we care about to smooth loss magnitute + # between examples, like in maskgit (ie this is + # sum(loss * input_mask) / sum(input_mask) in their code. + loss = nll.mean(where=input_mask) + else: + loss = nll.mean() + + return loss, metrics + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, rng, batch): + """Update step.""" + + images = batch["image"] + labels, cond_images = batch.get("labels"), batch.get("cond_image") + + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + + measurements = {} + + # Get device-specific loss rng. + _, rng_model = jax.random.split(rng, 2) + params, opt = train_state["params"], train_state["opt"] + + (loss, metrics), grads = jax.value_and_grad(loss_fn, has_aux=True)( + params, images, labels, cond_images, rng_model) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + train_state = {"params": params, "opt": opt} + + measurements["training_loss"] = loss + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + if adaptor is not None: + ps_a = jax.tree_leaves(params["params_adaptor"]) + measurements["l2_params_adaptor"] = jnp.sqrt(sum([jnp.vdot(p, p) + for p in ps_a])) + + measurements.update({f"train/{k}": v.mean() for k, v in metrics.items()}) + + return train_state, measurements + +################################################################################ +# # +# Set up Evals # +# # +################################################################################ + + def validation_fn(train_state, batch, seed=0): + params = train_state["params"] + + local_rng = trainer_utils.get_local_rng(seed, batch) + + _, aux = loss_fn( + params, batch["image"], batch.get("labels"), + batch.get("cond_image"), local_rng) + return { + key: jnp.mean(value, axis=tuple(range(1, value.ndim))) + for key, value in aux.items() + } + + def predict_fn_teacher_forcing(train_state, batch, seed=0): + params = train_state["params"] + image, labels = batch["image"], batch.get("labels") + + local_rng = trainer_utils.get_local_rng(seed, batch) + + rng_dropout = {"dropout": local_rng} + sequence = vae_encode(image, rng_dropout) + + if adaptor is not None: + # Use the adaptor to map from VAE latent space to GIVT in/output space. + sequence = adaptor.apply({"params": params["params_adaptor"]}, + sequence, method=adaptor.forward) + + b, s, _ = sequence.shape + # This is None for the non-mask style. Otherwise, shape (b, s) of zeros + # (nothing masked). + input_mask = model.get_input_mask_teacher_forced((b, s)) + + _, pdf = model.apply( + {"params": params}, sequence, labels, + train=True, input_mask=input_mask, rngs=rng_dropout) + + rng_sample, _ = jax.random.split(local_rng, 2) + sampled_sequence = pdf.sample(seed=rng_sample) + + if adaptor is not None: + # Use the adaptor inverse to map back to the VAE latent space + sampled_sequence = adaptor.apply({"params": params["params_adaptor"]}, + sampled_sequence, method=adaptor.inverse) + logits = vae.apply( + {"params": params_vae}, sampled_sequence, method=vae.decode) + + return {"logits": logits} + + def predict_fn_rep(train_state, image, seed=0): + assert model.style == "ar" + assert model.drop_labels_probability == 1.0 + params = train_state["params"] + + local_rng = trainer_utils.get_local_rng(seed, batch) + + rng_dropout = {"dropout": local_rng} + sequence = vae_encode(image, rng_dropout) + placeholder_labels = jnp.zeros((sequence.shape[0],), dtype=jnp.int32) + + return model.apply({"params": params}, sequence, labels=placeholder_labels, + return_reps=True, method=model.decode) + + def predict_fn_sampling(train_state, batch, seed=0): + params = train_state["params"] + labels = batch.get("labels") + + local_rng = trainer_utils.get_local_rng(seed, batch) + code_logprobs = None + + if model.style == "ar": + if labels is None: + # Try to infer batch size if labels are not provided + if "image" in batch: + sampling_batch_size = batch["image"].shape[0] + elif "cond_image" in batch: + sampling_batch_size = batch["cond_image"].shape[0] + else: + sampling_batch_size = config.get("sampling_batch_size", 4) + else: + sampling_batch_size = None + sampled_codes, code_logprobs = softar_decode.generate( + params={"params": params}, + seed=local_rng, + model=model, + seq_len=config.model.seq_len, + feature_dim=config.model.out_dim, + labels=labels, + cond_image=batch.get("cond_image"), + batch_size=sampling_batch_size, + config=config.get("ar_generation_config"), + ) + elif model.style == "masked": + assert "cond_image" not in batch + sampled_codes = parallel_decode.decode_masked( # pytype: disable=wrong-arg-types + rng=local_rng, + labels=labels, + seq_len=config.model.seq_len, + feature_dim=config.model.out_dim, + model=model, + variables={"params": params}, + config=parallel_decode.MaskedGenerationConfig( + **config.get("masked_generation_config", {}) + ), + ).current_inputs_q + else: + raise NotImplementedError + + if adaptor is not None: + # Use the adaptor inverse to map back to the VAE latent space. + sampled_codes = adaptor.apply({"params": params["params_adaptor"]}, + sampled_codes, method=adaptor.inverse) + + sampled_images = vae.apply( + {"params": params_vae}, sampled_codes, method=vae.decode) + + sampling_results = {"logits": sampled_images} + if code_logprobs is not None: + sampling_results["logprobs"] = code_logprobs + + return sampling_results + + def predict_fn_sampling_panoptic( + train_state, batch, seed=0, min_fraction=0.0): + logits = predict_fn_sampling(train_state, batch, seed)["logits"] + return panoptic_task.panoptic_predictions_from_logits( + logits["semantics"], logits["instances"], min_fraction=min_fraction) + + def predict_fn_sampling_depth(train_state, batch, seed=0): + depth = predict_fn_sampling(train_state, batch, seed)["logits"]["depth"] + depth = trainer_utils.unbin_depth( + depth, min_depth=config.min_depth, max_depth=config.max_depth, + num_bins=config.vae.model.inout_specs["depth"][1]) + return {"depth": depth} + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, + { + "validation": validation_fn, + "sample_teacher_forced": predict_fn_teacher_forcing, + "sample": predict_fn_sampling, + "sample_panoptic": predict_fn_sampling_panoptic, + "sample_depth": predict_fn_sampling_depth, + "representation": predict_fn_rep, + }, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + ckpt_mngr = None + if save_ckpt_path or resume_ckpt_path: + ckpt_mngr = array_serial.GlobalAsyncCheckpointManager() + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + jax.tree_map(lambda x: x.delete(), train_state) + del train_state + shardings = { + **train_state_sharding, + "chrono": jax.tree_map(lambda _: repl_sharding, + u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + train_state["params"] = model_mod.load( + train_state["params"], config.model_init, config.get("model"), + **config.get("model_load", {})) + + # load has the freedom to return params not correctly sharded + train_state["params"] = u.reshard( + train_state["params"], train_state_sharding["params"]) + + parameter_overview.log_parameter_overview( + train_state["params"], msg="restored params", + include_stats="global", jax_logging_process=0) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, flax.linen.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + # Skip training loop when running an eval-only config + if config.get("eval_only", False): + break + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, flax.linen.logical_axis_rules(sharding_rules): + train_state, measurements = update_fn(train_state, rng_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + if not np.isfinite(measurements["training_loss"]): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_ckpt_steps = get_steps("keep_ckpt", None) or total_steps + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree_map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, flax.linen.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/givt/utils.py b/big_vision/trainers/proj/givt/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..449b43f37fb8f60fb1e41090ed91c70c92719f02 --- /dev/null +++ b/big_vision/trainers/proj/givt/utils.py @@ -0,0 +1,70 @@ +# Copyright 2024 Big Vision 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. + +"""Utils for GIVT stage I and II trainers.""" + +from typing import Any + +import jax +import jax.numpy as jnp + + +def unbin_depth( + depth: jax.Array, + *, + min_depth: float, + max_depth: float, + num_bins: int, +) -> jax.Array: + """Transform a depth map with binned values into a float-valued depth map. + + Args: + depth: Depth map whose binned values are encoded in one-hot fashion along + the last dimension. + min_depth: Minimum binned depth value. + max_depth: Maximum value of binned depth. + num_bins: Number of depth bins. + + Returns: + Float-valued depth map. + """ + depth = jnp.argmax(depth, axis=-1) + depth = depth.astype(jnp.float32) + 0.5 # Undoes floor in expectation. + depth /= num_bins + return depth * (max_depth - min_depth) + min_depth + + +def get_local_rng( + seed: int | jax.Array, + batch: Any, +) -> jax.Array: + """Generate a per-image seed based on the image id or the image values. + + Args: + seed: Random seed from which per-image seeds should be derived. + batch: Pytree containing a batch of images (key "image") and optionally + image ids (key "image/id"). + + Returns: + Array containing per-image ids. + """ + fake_id = None + if "image" in batch: + fake_id = (10**6 * jax.vmap(jnp.mean)(batch["image"])).astype(jnp.int32) + return jax.lax.scan( + lambda k, x: (jax.random.fold_in(k, x), None), + jax.random.PRNGKey(seed), + batch.get("image/id", fake_id), + )[0] + diff --git a/big_vision/trainers/proj/givt/vae.py b/big_vision/trainers/proj/givt/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..242899e5ea00dd133fad1269382bb9309ba5dfca --- /dev/null +++ b/big_vision/trainers/proj/givt/vae.py @@ -0,0 +1,569 @@ +# Copyright 2024 Big Vision 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. + +"""Train loop for training a VAE or beta-VAE with a Gaussian encoder.""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import math +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +from big_vision import input_pipeline +import big_vision.evaluators.common as eval_common +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.trainers.proj.givt.utils as trainer_utils +from big_vision.trainers.proj.uvim import panoptic_task +import big_vision.utils as u +from clu import parameter_overview +import flax.linen as nn +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + +partial = functools.partial + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def main(argv): + del argv + + jax.distributed.initialize() + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", + ["ops_general", "ops_image", "proj.uvim.pp_ops", + "proj.givt.pp_ops"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + # Allow for things like timings as early as possible! + u.chrono.inform(measure=mw.measure, write_note=write_note) + +################################################################################ +# # +# Set up Mesh # +# # +################################################################################ + + # We rely on jax mesh_utils to organize devices, such that communication + # speed is the fastest for the last dimension, second fastest for the + # penultimate dimension, etc. + config_mesh = config.get("mesh", [("data", jax.device_count())]) + + # Sharding rules with default + sharding_rules = config.get("sharding_rules", [("act_batch", "data")]) + + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(jax.devices()).reshape(mesh_size).shape + + device_mesh = mesh_utils.create_device_mesh(mesh_size) + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. The + # order presribed by the `devices_flat` variable should be used throughout + # the program. + devices_flat = device_mesh.flatten() + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices_flat, n_prefetch) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note("Creating model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**config.get("model", {})) + + def init(rng): + batch = jax.tree_map( + lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + params = model.init(rng, batch["image"])["params"] + + return params + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + write_note("Inferring parameter shapes...") + rng, rng_init = jax.random.split(rng) + params_shape = jax.eval_shape(init, rng_init) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, nn.unbox(params_shape), sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + if jax.process_index() == 0: + num_params = sum(np.prod(p.shape) for p in jax.tree_leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Shard & Transfer # +# # +################################################################################ + + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(device_mesh, mesh_axes) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + write_note("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + strategy = config.get("sharding_strategy", [(".*", "replicate")]) + with nn.logical_axis_rules(sharding_rules): + train_state_sharding = bv_sharding.infer_sharding( + train_state_shape, strategy=strategy, mesh=mesh) + + write_note("Transferring train_state to devices...") + # RNG is always replicated + rng_init = u.reshard(rng_init, repl_sharding) + + # Parameters and the optimizer are now global (distributed) jax arrays. + params = jax.jit(init, out_shardings=train_state_sharding["params"])(rng_init) + opt = jax.jit(tx.init, out_shardings=train_state_sharding["opt"])(params) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng # not used anymore, so delete it. + + # At this point we have everything we need to form a train state. It contains + # all the parameters that are passed and updated by the main training step. + # From here on, we have no need for Flax AxisMetadata (such as partitioning). + train_state = nn.unbox({"params": params, "opt": opt}) + del params, opt # Delete to avoid memory leak or accidental reuse. + + write_note("Logging parameter overview...") + parameter_overview.log_parameter_overview( + train_state["params"], msg="Init params", + include_stats="global", jax_logging_process=0) + + # Computing ELBO or beta-VAE loss for Gaussian encoder. + def vae_loss_fn(logits, image, mu, logvar, beta=1.0, + keep_batch_dim=False): + rec_loss_fn = config.get("rec_loss_fn", "l2") + if rec_loss_fn == "l2": + loss_rec = 0.5 * jnp.sum( + jnp.square(logits - image), axis=tuple(range(1, logits.ndim))) + elif rec_loss_fn == "xent": + loss_rec = 0.0 + for k, (in_ch, _) in config.model.inout_specs.items(): + cur_logits = logits[k] + b, _, _, c = cur_logits.shape + # This xent function expect a 1-D sequence of logits + image_flat = image[..., in_ch].reshape((b, -1)) + if config.get("mask_zero_target", False): + weights = (image_flat != 0).astype(jnp.int32) + else: + weights = None + loss_rec += u.weighted_softmax_xent( + logits=cur_logits.reshape((b, -1, c)), + labels=image_flat, + reduction=False, + weights=weights) + else: + raise ValueError(f"Unknown reconstruction loss function: {rec_loss_fn}") + + loss_kl = - 0.5 * jnp.sum( + 1 + logvar - jnp.square(mu) - jnp.exp(logvar), + axis=tuple(range(1, mu.ndim))) + + if not keep_batch_dim: + loss_rec, loss_kl = jnp.mean(loss_rec), jnp.mean(loss_kl) + loss = loss_rec + beta * loss_kl + return loss, {"loss": loss, "loss_rec": loss_rec, "loss_kl": loss_kl} + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, rng, batch): + """Update step.""" + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + + # Get device-specific loss rng. + _, rng_model = jax.random.split(rng, 2) + + def loss_fn(params): + logits, out = model.apply( + {"params": params}, + batch["image"], + train=True, + rngs={"dropout": rng_model}) + mu = out["mu"] + logvar = out["logvar"] + + loss, aux_loss = vae_loss_fn( + logits, batch["image"], mu, logvar, config.get("beta", 1.0), + ) + return loss, aux_loss + + params, opt = train_state["params"], train_state["opt"] + (loss, measurements), grads = jax.value_and_grad(loss_fn, has_aux=True)( + params) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + measurements["training_loss"] = loss + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.sum(g * g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.sum(p * p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.sum(u * u) for u in us])) + + return {"params": params, "opt": opt}, measurements + +################################################################################ +# # +# Load Checkpoint # +# # +################################################################################ + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + ckpt_mngr = None + if save_ckpt_path or resume_ckpt_path: + ckpt_mngr = array_serial.GlobalAsyncCheckpointManager() + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + jax.tree_map(lambda x: x.delete(), train_state) + del train_state + shardings = { + **train_state_sharding, + "chrono": jax.tree_map(lambda _: repl_sharding, + u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + train_state["params"] = model_mod.load( + train_state["params"], config.model_init, config.get("model"), + **config.get("model_load", {})) + + # load has the freedom to return params not correctly sharded. Think of for + # example ViT resampling position embedings on CPU as numpy arrays. + train_state["params"] = u.reshard( + train_state["params"], train_state_sharding["params"]) + + parameter_overview.log_parameter_overview( + train_state["params"], msg="restored params", + include_stats="global", jax_logging_process=0) + + +################################################################################ +# # +# Setup Evals # +# # +################################################################################ + + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def validation_fn(train_state, batch, seed=0): + """Compute per-example metrics.""" + local_rng = trainer_utils.get_local_rng(seed, batch) + # Provide `dropout` rng for reprarametrization and set `train=True` to have + # the model actually do reparametrization. + logits, out = model.apply({"params": train_state["params"]}, batch["image"], + rngs={"dropout": local_rng}, train=True) + _, aux_loss = vae_loss_fn( + logits, batch["image"], out["mu"], out["logvar"], + config.get("beta", 1.0), + keep_batch_dim=True) + + return jax.tree_map( + lambda x: jnp.mean(x, axis=tuple(range(1, x.ndim))), + aux_loss) + + def predict_fn(train_state, batch, seed=0): + if isinstance(batch, dict): + batch = batch["image"] + local_rng = trainer_utils.get_local_rng(seed, {"image": batch}) + # Provide `dropout` rng and set `train=True` to perform reparametrization + logits, _ = model.apply({"params": train_state["params"]}, batch, + rngs={"dropout": local_rng}, train=True) + return {"logits": logits} + + def predict_fn_panoptic(train_state, batch): + logits = predict_fn({"params": train_state["params"]}, batch)["logits"] + return panoptic_task.panoptic_predictions_from_logits( + logits["semantics"], logits["instances"]) + + def predict_fn_depth(train_state, batch): + depth = predict_fn( + {"params": train_state["params"]}, batch)["logits"]["depth"] + depth = trainer_utils.unbin_depth( + depth, min_depth=config.min_depth, max_depth=config.max_depth, + num_bins=config.model.inout_specs["depth"][1]) + return {"depth": depth} + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, { + "predict": predict_fn, + "predict_panoptic": predict_fn_panoptic, + "predict_depth": predict_fn_depth, + "validation": validation_fn}, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, nn.logical_axis_rules(sharding_rules): + train_state, measurements = update_fn(train_state, rng_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + if not np.isfinite(measurements["training_loss"]): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_ckpt_steps = get_steps("keep_ckpt", None) or total_steps + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree_map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/gsam/gsam.py b/big_vision/trainers/proj/gsam/gsam.py new file mode 100644 index 0000000000000000000000000000000000000000..89efa8d38c2dddf8acd66b8440beafb980710189 --- /dev/null +++ b/big_vision/trainers/proj/gsam/gsam.py @@ -0,0 +1,122 @@ +# Copyright 2022 Big Vision 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. +'''This file provides jax implementation of GSAM.''' + +import jax +import jax.numpy as jnp + +def dual_vector(y): + """Returns the solution of max_x y^T x s.t. ||x||_2 <= 1. + Args: + y: A pytree of numpy ndarray, vector y in the equation above. + """ + gradient_norm = jnp.sqrt(sum( + jnp.sum(jnp.square(e)) for e in jax.tree_util.tree_leaves(y))) + normalized_gradient = jax.tree_map(lambda x: x / gradient_norm, y) + return normalized_gradient, gradient_norm + +def gsam_gradient(loss_fn, params, inputs, targets, + rho_max, rho_min, alpha, lr, lr_max, lr_min, eps=1e-12, + adaptive_perturbation=False, minimize_fp=True): + """ + Get the GSAM gradient (https://openreview.net/pdf?id=edONMAnhLu-). + Args: + loss_fn: the loss function. + params: the model weights. + inputs: the inputs to the loss function. + targets: the targets to the loss function. + rho_max: the maximum rho value for perturbation of weights. + rho_min: the minimum rho value for perturbation of weights. + alpha: the alpha value for the rho schedule, see Algorithm 1 in the paper. + lr: current learning rate. + lr_max: the maximum learning rate. + lr_min: the minimum learning rate. + eps: the epsilon value for numerical stability. + adaptive_perturbation: if False, same perturbation as SAM, + treat all parameters as a single vector, + perturbation norm is calculated as the norm of the whole vector; + If True, perturbation norm is proportional to parameter norm, + this stabilizes training when different layers have weights + of different scales. + Emprically, setting it to True can handle 10x larger rho than + setting it to False. + minimize_fp: if True, min(f_p, h), original GSAM; + if False, min(f, h), where f is the clean loss. + f_p is the perturbed loss, h is the surrogate gap. + If True, training dynamics is closer to SAM than conventional training, + you might observe several loss spikes during training. + If False, the training dynamics is closer to conventional training, + and is often more stable (fewer loss spikes) during training. + Returns: + l_clean: the loss function value. + g_gsam: the GSAM gradient. g_gsam is not averaged across workers, + need to call "jax.lax.pmean" to average. + + Note: + Setting `rho_max=rho_min` and `alpha=0` reduces GSAM to SAM. + """ + l_clean, g_clean = jax.value_and_grad(loss_fn)(params, inputs, targets) + g_clean_normalized, g_clean_length = dual_vector(g_clean) + + if lr_max == lr_min: + sam_rho = rho_max + else: + sam_rho = rho_min + (rho_max - rho_min) * (lr - lr_min) / (lr_max - lr_min) + + # Per-worker perturbation. + if adaptive_perturbation: + param_sam = jax.tree_map(lambda a, b: a + \ + jnp.abs(a) * sam_rho * b / (g_clean_length + eps), params, g_clean) + else: + param_sam = jax.tree_map(lambda a, b: a + \ + sam_rho * b / (g_clean_length + eps), params, g_clean) + + # Get gradients at perturbed weights. + _, g_robust = jax.value_and_grad(loss_fn)(param_sam, inputs, targets) + + # Decompose gradients. + g_clean_flatten, _ = jax.tree_util.tree_flatten(g_clean) + g_robust_flatten, _ = jax.tree_util.tree_flatten(g_robust) + + if minimize_fp: + # Decompose g_clean onto parallel and vertical to g_robust. + g_robust_normalized, _ = dual_vector(g_robust) + g_robust_normalized_flatten, _ = jax.tree_util.tree_flatten( + g_robust_normalized) + + g_clean_projection_norm = sum(jnp.vdot(p, q) for (p,q) in + zip(g_robust_normalized_flatten, g_clean_flatten)) + g_clean_residual = jax.tree_map(lambda a, b: + a - g_clean_projection_norm * b, g_clean, g_robust_normalized) + + # Get GSAM gradient. + g_gsam = jax.tree_map(lambda a, b: a - b * alpha, + g_robust, g_clean_residual) + else: + # Decompose g_robust onto parallel and vertical to g_clean. + g_clean_normalized, g_clean_length = dual_vector(g_clean) + g_clean_normalized_flatten, _ = jax.tree_util.tree_flatten( + g_clean_normalized) + + g_robust_projection_norm = sum(jnp.vdot(p, q) for (p,q) in + zip(g_clean_normalized_flatten, g_robust_flatten)) + g_robust_residual = jax.tree_map(lambda a, b: + a - g_robust_projection_norm * b, g_robust, g_clean_normalized) + + # Get GSAM gradient. + g_gsam = jax.tree_map(lambda a, b: a + b * alpha, + g_clean, g_robust_residual) + + # Always return the clean loss (rather than the perturbed loss). + return l_clean, g_gsam diff --git a/big_vision/trainers/proj/gsam/train.py b/big_vision/trainers/proj/gsam/train.py new file mode 100644 index 0000000000000000000000000000000000000000..8b66b03eb544987cbcbf91240ccf9ee5e1d5883c --- /dev/null +++ b/big_vision/trainers/proj/gsam/train.py @@ -0,0 +1,370 @@ +# Copyright 2022 Big Vision 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. + +"""Training loop example. +Trainer that implements SAM/GSAM optimizers. +""" +# pylint: disable=consider-using-from-import +from functools import partial +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.pp.builder as pp_builder +from big_vision.trainers.proj.gsam.gsam import gsam_gradient +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf +import tensorflow.io.gfile as gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def main(argv): + del argv + tf.config.experimental.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + assert not config.get("grad_accum_steps"), "Grad-acc not supported anymore." + + save_checkpoint_path = None + if workdir and config.get("checkpoint_steps"): + gfile.makedirs(workdir) + save_checkpoint_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + # Verify settings to make sure no checkpoints are accidentally missed. + if config.get("keep_checkpoint_steps"): + assert config.get("checkpoint_steps"), "Specify `checkpoint_steps`." + assert config.keep_checkpoint_steps % config.checkpoint_steps == 0, ( + f"`keep_checkpoint_steps` ({config.checkpoint_steps}) should be" + f"divisible by `checkpoint_steps ({config.checkpoint_steps}).`") + + batch_size = config.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir) + chrono = u.Chrono() + + write_note("Initializing train dataset...") + train_ds = input_pipeline.make_for_train( + dataset=config.dataset, + split=config.train_split, + batch_size=config.batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(config.pp_train), + shuffle_buffer_size=config.get("shuffle_buffer_size"), + cache_raw=config.get("cache_raw", False), + data_dir=fillin(config.get("dataset_dir"))) + + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + + ntrain_img = input_pipeline.get_num_examples( + config.dataset, config.train_split, + data_dir=fillin(config.get("dataset_dir"))) + steps_per_epoch = ntrain_img / batch_size + + if config.get("num_epochs"): + total_steps = int(config.num_epochs * steps_per_epoch) + assert not config.get("total_steps"), "Set either num_epochs or total_steps" + else: + total_steps = config.total_steps + + info("Running for %d steps, that means %f epochs and %f steps per epoch", + total_steps, total_steps * batch_size / ntrain_img, steps_per_epoch) + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model( + num_classes=config.num_classes, **config.get("model", {})) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @partial(jax.jit, backend="cpu") + def init(rng): + shape = tuple(train_ds.element_spec["image"].shape[1:]) + bs = config.batch_size // jax.device_count() + dummy_input = jnp.zeros((bs,) + shape, jnp.float32) + params = flax.core.unfreeze(model.init(rng, dummy_input))["params"] + + # Set bias in the head to a low value, such that loss is small initially. + if "init_head_bias" in config: + params["head"]["bias"] = jnp.full_like(params["head"]["bias"], + config["init_head_bias"]) + + return params + + rng, rng_init = jax.random.split(rng) + params_cpu = init(rng_init) + + if jax.process_index() == 0: + num_params = sum(p.size for p in jax.tree_util.tree_leaves(params_cpu)) + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + mw.measure("num_params", num_params) + + write_note(f"Initializing {config.optax_name} optimizer...") + tx, sched_fns = bv_optax.make(config, params_cpu, sched_kw=dict( + global_batch_size=batch_size, + total_steps=total_steps, + steps_per_epoch=steps_per_epoch)) + + assert len(sched_fns) == 1, "Current GSAM supports one global learning-rate." + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1)) + def update_fn(params, opt, rng, images, labels, step): + """Update step.""" + + measurements = {} + + if config.get("mixup") and config.mixup.p: + rng, (images, labels), _ = u.mixup(rng, images, labels, **config.mixup) + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + rng_model_local = jax.random.fold_in(rng_model, jax.lax.axis_index("batch")) + + def loss_fn(params, images, labels): + logits, _ = model.apply( + {"params": flax.core.freeze(params)}, images, + train=True, rngs={"dropout": rng_model_local}) + return getattr(u, config.get("loss", "sigmoid_xent"))( + logits=logits, labels=labels) + + learning_rate = sched_fns[0](step) * config.lr + l, grads = gsam_gradient(loss_fn=loss_fn, params=params, inputs=images, + targets=labels, lr=learning_rate, **config.gsam) + l, grads = jax.lax.pmean((l, grads), axis_name="batch") + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum(jnp.vdot(g, g) for g in gs)) + ps = jax.tree_util.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum(jnp.vdot(p, p) for p in ps)) + us = jax.tree_util.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum(jnp.vdot(u, u) for u in us)) + + return params, opt, rng, l, measurements + + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def predict_fn(params, image): + logits, out = model.apply({"params": params}, image) + return logits, out + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_checkpoint_path = None + if save_checkpoint_path and gfile.exists(save_checkpoint_path): + resume_checkpoint_path = save_checkpoint_path + elif config.get("resume"): + resume_checkpoint_path = fillin(config.resume) + if resume_checkpoint_path: + write_note("Resume training from checkpoint...") + checkpoint = { + "params": params_cpu, + "opt": opt_cpu, + "chrono": chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint(checkpoint_tree, resume_checkpoint_path) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu, opt_cpu = checkpoint["params"], checkpoint["opt"] + chrono.load(checkpoint["chrono"]) + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu = model_mod.load( + params_cpu, config.model_init, config.get("model"), + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu, msg="restored params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + chrono.inform(first_step, total_steps, batch_size, steps_per_epoch) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + + evaluators = eval_common.from_config( + config, {"predict": predict_fn}, + lambda s: write_note(f"Initializing evaluator: {s}...\n{chrono.note}")) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = flax.jax_utils.replicate(rng_loop) + checkpoint_writer = None + + write_note(f"First step compilations...\n{chrono.note}") + error = None # For exiting with an error after cleanup. Avoids indentation. + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, train_batch in zip( + range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + params_repl, opt_repl, rngs_loop, loss_value, measurements = update_fn( + params_repl, opt_repl, rngs_loop, + train_batch["image"], + train_batch["labels"], + flax.jax_utils.replicate(step)) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, config.log_training_steps) + + # Report training progress + if (u.itstime(step, config.log_training_steps, total_steps, host=0) + or chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + chrono.tick(step, mw.measure, write_note) + if not np.isfinite(l): + error = (f"The loss became nan or inf somewhere within steps " + f"[{step - config.log_training_steps}, {step}]") + break + + # Checkpoint saving + if (save_checkpoint_path and + u.itstime(step, config.get("checkpoint_steps"), total_steps, host=0)): + chrono.pause(wait_for=(params_repl, opt_repl)) + u.checkpointing_timeout(checkpoint_writer, + config.get("checkpoint_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu = jax.tree_map(lambda x: np.array(x[0]), params_repl) + opt_cpu = jax.tree_map(lambda x: np.array(x[0]), opt_repl) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, config.get("keep_checkpoint_steps"), total_steps): + copy_step = step + + ckpt = {"params": params_cpu, "opt": opt_cpu, "chrono": chrono.save()} + checkpoint_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_checkpoint_path, copy_step)) + chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators: + if u.itstime(step, log_steps, total_steps): + chrono.pause(wait_for=params_repl) + write_note(f"{name} evaluation...\n{chrono.note}") + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + if not error: + write_note(f"Done!\n{chrono.note}") + else: + write_note(f"Failed!\n{error}\n{chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync_all_hosts() + + # Before cleanup, as cleanup should only run for successful jobs. + if error is not None: + raise RuntimeError(error) + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/image_text/_deprecated_contrastive.py b/big_vision/trainers/proj/image_text/_deprecated_contrastive.py new file mode 100644 index 0000000000000000000000000000000000000000..863941a8e5d84b73530b93a19591cb65e01e0dcf --- /dev/null +++ b/big_vision/trainers/proj/image_text/_deprecated_contrastive.py @@ -0,0 +1,514 @@ +# Copyright 2023 Big Vision 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. + +"""Contrastive training loop. + +For models Like +- LiT (https://arxiv.org/abs/2111.07991) +- CLIP (https://arxiv.org/abs/2103.00020) +- SigLIP (https://arxiv.org/abs/2303.15343) +""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + +# pylint: disable=logging-fstring-interpolation + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def clip(x, *, a_max=None, a_min=None): + """Like jnp.clip, but allows all-None to mean don't clip.""" + if a_max is None and a_min is None: + return x + return jnp.clip(x, a_max=a_max, a_min=a_min) + + +def all_gather(z, roll=False, only_others=False): + """All gather and flatten first two dims.""" + def gather_flat(x): + x = jax.lax.all_gather(x, "batch") + if roll or only_others: + # Each device moves "its" chunk to the beginning. Simplies loss/acc calcs. + x = jnp.roll(x, -jax.lax.axis_index("batch"), axis=0) + if only_others: + x = x[1:] + return jnp.concatenate(x, 0) # Fold in "device" and "batch" dims. + return jax.tree_map(gather_flat, z) + + +def softmax_loss(zimg, ztxt, temperature): + """Softmax loss following the CLIP paper. Factorized to reduce memory cost.""" + + def unidirectional_loss(z1, z2, t): + z2 = all_gather(z2, roll=True) + logits = jnp.dot(z1, z2.T) * t + # This a softmax across the larger gathered axis, taking advantage of the + # fact that positives are known to be on the diagonal. + loss = -(jnp.diag(logits) - jax.scipy.special.logsumexp(logits, axis=-1)) + acc = jnp.argmax(logits, axis=1) == jnp.arange(z1.shape[0]) + return loss.mean(), acc.mean() + + extras = {} + loss = 0 + for name, row, col in [("i2t", zimg, ztxt), ("t2i", ztxt, zimg)]: + loss_dir, acc_dir = unidirectional_loss(row, col, temperature) + loss += 0.5 * loss_dir + extras[f"{name}_acc"] = acc_dir + extras[f"{name}_loss"] = loss_dir + + loss = jax.lax.pmean(loss, "batch") + return loss, extras + + +def _avg_pos_logit(x_me): + return jnp.mean(jnp.diag(x_me)) + + +def _avg_neg_logit(x_me, x_ot=None): + nom = jnp.sum(x_me) - jnp.sum(jnp.diag(x_me)) + den = x_me.size - len(x_me) + if x_ot is not None and x_ot.size: + nom += jnp.sum(x_ot) + den += x_ot.size + return nom / den + + +def sigmoid_loss(zimg, ztxt, temperature, bias=0.0): + """Sigmoid loss from SigLIP: https://arxiv.org/abs/2303.15343.""" + # Sigmoid loss. Since it's unidirectional, image embeddings stick to + # "me", i.e. the device they are computed on, and text embeddings travel. + ztxt_me = ztxt # Text embeddings on my devices: (n, D) + ztxt_ot = all_gather(ztxt, only_others=True) # Text emb from others: (N, D) + + logits_me = jnp.dot(zimg, ztxt_me.T) # (n, D) . (D, n) -> (n, n) + logits_ot = jnp.dot(zimg, ztxt_ot.T) # (n, D) . (D, N) -> (n, N) + logits_me = logits_me * temperature + bias + logits_ot = logits_ot * temperature + bias + + eye = jnp.eye(zimg.shape[0]) + # Standard sigmoid computes everything twice, once assuming positive + # labels and once assuming negative ones. But here we know exactly where + # to find positives (on "me" diagonal) and negatives (everywhere else), + # so compute each one's loss only once: + m1_diag1 = -jnp.ones_like(logits_me) + 2 * eye + loglik_me = jax.nn.log_sigmoid(m1_diag1 * logits_me) + loglik_ot = jax.nn.log_sigmoid(-logits_ot) + + # Normalize by npos per column, but that's one, so just sum. + nll_me = -loglik_me.sum(axis=-1) + nll_ot = -loglik_ot.sum(axis=-1) + l = nll_me.mean() + nll_ot.mean() # == concat'ing me/ot along axis -1 above. + + return l, { + # Only local device metrics for now, as last time I tried, there was + # some funny unimplemented business with jax.lax.pmin/pmax! + # So what's reported here is average of per-device min/max/avg. + "pos_min_logit": jnp.min(jnp.diag(logits_me)), + "pos_max_logit": jnp.max(jnp.diag(logits_me)), + "pos_avg_logit": _avg_pos_logit(logits_me), + "local_neg_min_logit": jnp.min(logits_me + 1e9 * eye), + "local_neg_max_logit": jnp.max(logits_me - 1e9 * eye), + "local_neg_avg_logit": _avg_neg_logit(logits_me), + "neg_min_logit": jnp.minimum( + jnp.min(logits_me + 1e9 * eye), + jnp.min(logits_ot) if logits_ot.size else jnp.inf), + "neg_max_logit": jnp.maximum( + jnp.max(logits_me - 1e9 * eye), + jnp.max(logits_ot) if logits_ot.size else -jnp.inf), + "neg_avg_logit": _avg_neg_logit(logits_me, logits_ot), + } + + +def _gather_from_device(x, device_id, axis_name="batch"): + return jax.lax.psum((jax.lax.axis_index(axis_name) == device_id) * x, + axis_name) + + +def chunked_sigmoid_loss(zimg, ztxt, temperature, bias=0.0): + """Loss computation from section 3.1 of arxiv.org/abs/2303.15343.""" + + # Calculate loss for representations on this device, which includes positives. + logits_me = jnp.dot(zimg, ztxt.T) # (n, D) . (D, n) -> (n, n) + logits_me = logits_me * temperature + bias + m1_diag1 = -jnp.ones_like(logits_me) + 2 * jnp.eye(zimg.shape[0]) + loglik_me = jax.nn.log_sigmoid(m1_diag1 * logits_me) + nll_me = -loglik_me.sum(axis=-1).mean() + + def negative_loss(ztxt_other_device): + logits_ot = jnp.dot(zimg, ztxt_other_device.T) # (n, D) . (D, n) -> (n, n) + logits_ot = logits_ot * temperature + bias + loglik_ot = jax.nn.log_sigmoid(-logits_ot) + return -jnp.sum(loglik_ot, axis=-1).mean() + + me = jax.lax.axis_index("batch") + # All other devices are negatives. Hot-potato swap ztxt across devices. + # Interestingly, ppermute based implementation was memory intensive, so using + # all-reduce to gather representations. + nll_others = 0 + for device_id in range(jax.device_count()): + skip = jnp.not_equal(device_id, me) + nll_others += skip * negative_loss(_gather_from_device(ztxt, device_id)) + + eye = jnp.eye(zimg.shape[0]) + return nll_me + nll_others, { + "pos_min_logit": jnp.min(jnp.diag(logits_me)), + "pos_max_logit": jnp.max(jnp.diag(logits_me)), + "pos_avg_logit": _avg_pos_logit(logits_me), + "local_neg_min_logit": jnp.min(logits_me + 1e9 * eye), + "local_neg_max_logit": jnp.max(logits_me - 1e9 * eye), + "local_neg_avg_logit": _avg_neg_logit(logits_me),} + + +def main(argv): + del argv + tf.config.experimental.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( # pylint: disable=logging-fstring-interpolation + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + write_note("Initializing train dataset...") + train_ds, ntrain_img = input_pipeline.training(config.input) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size, + measure=mw.measure, write_note=write_note) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**config.get("model", {})) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @functools.partial(jax.jit, backend="cpu") + def init(rng): + bs = batch_size // jax.device_count() + image_size = tuple(train_ds.element_spec["image"].shape[1:]) + no_image = jnp.zeros((bs,) + image_size, jnp.float32) + text_size = tuple(train_ds.element_spec["labels"].shape[1:]) + no_text = jnp.zeros((bs,) + text_size, jnp.int32) + params = flax.core.unfreeze(model.init(rng, no_image, no_text))["params"] + return params + + rng, rng_init = jax.random.split(rng) + with u.chrono.log_timing("z/secs/init"): + params_cpu = init(rng_init) + + if jax.process_index() == 0: + num_params = sum(p.size for p in jax.tree_leaves(params_cpu)) + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + mw.measure("num_params", num_params) + + write_note(f"Initializing {config.optax_name} optimizer...") + tx, sched_fns = bv_optax.make(config, params_cpu, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @functools.partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1)) + def update_fn(params, opt, rng, batch): + """Update step.""" + assert "mixup" not in config, "We still have to figure out mixup." + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + rng_model_local = jax.random.fold_in(rng_model, jax.lax.axis_index("batch")) + + def loss_fn(params, images, labels): + zimg, ztxt, extras = model.apply( + {"params": params}, images, labels, + train=True, rngs={"dropout": rng_model_local}) + + match config.get("loss_fn", "softmax"): + case "softmax": + l, l_extras = softmax_loss(zimg, ztxt, extras["t"]) + case "sigmoid": + l, l_extras = sigmoid_loss(zimg, ztxt, extras["t"], bias=extras["b"]) + case "chunked_sigmoid": + l, l_extras = chunked_sigmoid_loss(zimg, ztxt, extras["t"], + bias=extras["b"]) + case _: + raise NotImplementedError(f"Unrecognized loss {config.loss_fn=}") + + return l, { + "t": extras["t"], + "t/parameter": extras["t/parameter"], + "train/nimg": jnp.mean(extras["img/norm"]), + "train/ntxt": jnp.mean(extras["txt/norm"]), + **{f"train/{k}": v for k, v in l_extras.items()}, + } + + (l, measurements), grads = jax.value_and_grad( + loss_fn, has_aux=True)(params, batch["image"], batch["labels"]) + l, measurements, grads = jax.lax.pmean((l, measurements, grads), + axis_name="batch") + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, rng, l, measurements + + # We require hashable function reference for evaluator. + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def predict_fn(params, image=None, text=None, **unused_kwargs): + del unused_kwargs # `unused_kwargs` is to be compatible with few-shot + zimg, ztxt, out = model.apply({"params": params}, image, text) + return zimg, ztxt, out + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, {"predict": predict_fn}, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + ) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = config.resume.format(wid=xm_wu.id) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + checkpoint = { + "params": params_cpu, + "opt": opt_cpu, + "chrono": u.chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint_np(resume_ckpt_path, checkpoint_tree) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu, opt_cpu = checkpoint["params"], checkpoint["opt"] + u.chrono.load(checkpoint["chrono"]) + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu = model_mod.load( + params_cpu, config.model_init, config.get("model"), + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu, msg="restored params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + u.chrono.inform(first_step=first_step) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{u.chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = flax.jax_utils.replicate(rng_loop) + ckpt_writer = None + + write_note(f"First step compilations...\n{u.chrono.note}") + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + params_repl, opt_repl, rngs_loop, loss_value, measurements = update_fn( + params_repl, opt_repl, rngs_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + u.chrono.tick(step) + if not np.isfinite(l): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + u.chrono.pause(wait_for=(params_repl, opt_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu = jax.tree_map(lambda x: np.array(x[0]), params_repl) + opt_cpu = jax.tree_map(lambda x: np.array(x[0]), opt_repl) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = {"params": params_cpu, "opt": opt_cpu, "chrono": u.chrono.save()} + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=params_repl) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + for key, value in evaluator.run(params_repl): + mw.measure(f"{prefix}{key}", value) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/image_text/siglip.py b/big_vision/trainers/proj/image_text/siglip.py new file mode 100644 index 0000000000000000000000000000000000000000..3ea6118be9f5d1f581667737cb774412672fcaa4 --- /dev/null +++ b/big_vision/trainers/proj/image_text/siglip.py @@ -0,0 +1,527 @@ +# Copyright 2024 Big Vision 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. + +"""Trainer for "Sigmoid Loss for Language Image Pre-Training". + +SigLIP (https://arxiv.org/abs/2303.15343) + +TODO: implement chunked version with shard_map. +""" +# pylint: disable=consider-using-from-import +# pylint: disable=logging-fstring-interpolation + +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.utils as u +from clu import parameter_overview +import flax.linen as nn +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") +# Fixes design flaw in jax.random that may cause unnecessary d2d comms. +jax.config.update("jax_threefry_partitionable", True) + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def main(argv): + del argv + + jax.distributed.initialize() + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + + config = flags.FLAGS.config + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + # Allow for things like timings as early as possible! + u.chrono.inform(measure=mw.measure, write_note=write_note) + +################################################################################ +# # +# Set up Mesh # +# # +################################################################################ + + # We rely on jax mesh_utils to organize devices, such that communication + # speed is the fastest for the last dimension, second fastest for the + # penultimate dimension, etc. + config_mesh = config.get("mesh", [("data", jax.device_count())]) + + # Sharding rules with default + sharding_rules = config.get("sharding_rules", [("act_batch", "data")]) + + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(jax.devices()).reshape(mesh_size).shape + + device_mesh = mesh_utils.create_device_mesh(mesh_size) + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. The + # order presribed by the `devices_flat` variable should be used throughout + # the program. + devices_flat = device_mesh.flatten() + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global(train_ds, devices_flat, n_prefetch) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note("Creating model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**config.get("model", {})) + + def init(rng): + batch = jax.tree_map(lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + params = model.init(rng, batch["image"], batch["labels"])["params"] + + # Set bias in the head to a low value, such that loss is small initially. + if "init_head_bias" in config: + params["head"]["bias"] = jnp.full_like(params["head"]["bias"], + config["init_head_bias"]) + + return params + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + write_note("Inferring parameter shapes...") + rng, rng_init = jax.random.split(rng) + params_shape = jax.eval_shape(init, rng_init) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, params_shape, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + if jax.process_index() == 0: + num_params = sum(np.prod(p.shape) for p in jax.tree_leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Shard & Transfer # +# # +################################################################################ + + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(device_mesh, mesh_axes) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + write_note("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + strategy = config.get("sharding_strategy", [(".*", "replicate")]) + train_state_sharding = bv_sharding.infer_sharding( + train_state_shape, strategy=strategy, mesh=mesh) + + write_note("Transferring train_state to devices...") + # RNG is always replicated + rng_init = u.reshard(rng_init, repl_sharding) + + # Parameters and the optimizer are now global (distributed) jax arrays. + params = jax.jit(init, out_shardings=train_state_sharding["params"])(rng_init) + opt = jax.jit(tx.init, out_shardings=train_state_sharding["opt"])(params) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng # not used anymore, so delete it. + + # At this point we have everything we need to form a train state. It contains + # all the parameters that are passed and updated by the main training step. + train_state = {"params": params, "opt": opt} + del params, opt # Delete to avoid memory leak or accidental reuse. + + write_note("Logging parameter overview...") + parameter_overview.log_parameter_overview( + train_state["params"], msg="Init params", + include_stats="global", jax_logging_process=0) + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, rng, batch): + """Update step.""" + + images, labels = batch["image"], batch["labels"] + + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + assert "mixup" not in config, "Mixup is not supported for SigLIP." + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + + def loss_fn(params): + zimg, ztxt, extras = model.apply( + {"params": params}, images, labels, + train=True, rngs={"dropout": rng_model}) + logits = jnp.dot(zimg, ztxt.T) + logits = logits * extras["t"] + extras["b"] + eye = jnp.eye(zimg.shape[0]) + + # Standard sigmoid computes everything twice, once assuming positive + # labels and once assuming negative ones. But here we know exactly where + # to find positives (on "me" diagonal) and negatives (everywhere else), + # so compute each one's loss only once: + m1_diag1 = -jnp.ones_like(logits) + 2 * eye + loglik = jax.nn.log_sigmoid(m1_diag1 * logits) + + # Normalize by npos per column, but that's one, so just sum. + nll = -jnp.sum(loglik, axis=-1) + + # NOTE: same as concat'ing me/ot along axis -1 above. + l = jnp.mean(nll) + + return l + + params, opt = train_state["params"], train_state["opt"] + loss, grads = jax.value_and_grad(loss_fn)(params) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + measurements = {"training_loss": loss} + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.sum(g * g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.sum(p * p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.sum(u * u) for u in us])) + + return {"params": params, "opt": opt}, measurements + +################################################################################ +# # +# Load Checkpoint # +# # +################################################################################ + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + ckpt_mngr = None + if save_ckpt_path or resume_ckpt_path: + ckpt_mngr = array_serial.GlobalAsyncCheckpointManager() + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + jax.tree_map(lambda x: x.delete(), train_state) + del train_state + shardings = { + **train_state_sharding, + "chrono": jax.tree_map(lambda _: repl_sharding, + u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + # TODO: when updating the `load` API soon, do pass and request the + # full `train_state` from it. Examples where useful: VQVAE, BN. + train_state["params"] = model_mod.load( + train_state["params"], config.model_init, config.get("model"), + **config.get("model_load", {})) + + # load has the freedom to return params not correctly sharded. Think of for + # example ViT resampling position embedings on CPU as numpy arrays. + train_state["params"] = u.reshard( + train_state["params"], train_state_sharding["params"]) + + parameter_overview.log_parameter_overview( + train_state["params"], msg="restored params", + include_stats="global", jax_logging_process=0) + + +################################################################################ +# # +# Setup Evals # +# # +################################################################################ + + # We do not jit/pmap this function, because it is passed to evaluator that + # does it later. We output as many intermediate tensors as possible for + # maximal flexibility. Later `jit` will prune out things that are not needed. + def eval_logits_fn(train_state, batch): + zimg, ztxt, out = model.apply( + {"params": train_state["params"]}, + batch.get("image", None), batch.get("labels", None)) + return zimg, ztxt, out + + def eval_loss_fn(train_state, batch): + logits, _ = model.apply({"params": train_state["params"]}, batch["image"]) + loss_fn = getattr(u, config.get("loss", "sigmoid_xent")) + return { + "loss": loss_fn(logits=logits, labels=batch["labels"], reduction=False) + } + + eval_fns = { + "predict": eval_logits_fn, + "loss": eval_loss_fn, + } + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, eval_fns, + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, nn.logical_axis_rules(sharding_rules): + train_state, measurements = update_fn(train_state, rng_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + if not np.isfinite(measurements["training_loss"]): + raise RuntimeError(f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_ckpt_steps = get_steps("keep_ckpt", None) or total_steps + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree_map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/paligemma/__pycache__/predict_fns.cpython-310.pyc b/big_vision/trainers/proj/paligemma/__pycache__/predict_fns.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8f623ac2503409f0d1caf5527d40be21eaf08d75 Binary files /dev/null and b/big_vision/trainers/proj/paligemma/__pycache__/predict_fns.cpython-310.pyc differ diff --git a/big_vision/trainers/proj/paligemma/predict_fns.py b/big_vision/trainers/proj/paligemma/predict_fns.py new file mode 100644 index 0000000000000000000000000000000000000000..48627d203a3b8ff4f99e8e26ca1eeb3859e0903a --- /dev/null +++ b/big_vision/trainers/proj/paligemma/predict_fns.py @@ -0,0 +1,466 @@ +# Copyright 2024 Big Vision 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. + +"""Prediction functions for PaliGemma.""" + +import collections +import functools + +from big_vision.pp import registry +import big_vision.utils as u +import einops +import jax +import jax.numpy as jnp +import numpy as np + + +P = jax.sharding.PartitionSpec + +# pylint: disable=missing-function-docstring + + +def get_all(model): + """Returns `predict_fns` for evaluators.""" + fns = { + "logits": _logits, + "image_avg_repr": _image_avg_repr, + "decode": _decode, + "decode_with_logp": _decode_with_logp, + "beam_decode": _beam_decode, + } + return {name: functools.partial(fn, model=model) for name, fn in fns.items()} + + +def _logits(train_state, batch, *, model): + images, text, mask = batch["image"], batch["text"], batch["mask_ar"] + text_logits, out = model.apply( + {"params": train_state["params"]}, + images, text[:, :-1], mask[:, :-1], + ) + return text_logits, out + + +def _image_avg_repr(train_state, batch, *, model, key="img/pre_logits"): + zimg, out = model.apply( + {"params": train_state["params"]}, + image=batch["image"], + method=model.embed_image, + ) + if key: + zimg = u.tree_get(out, key) + # At this point, zimg is a (batch of) sequence of image tokens, because we + # assume the model is a vit with "none" head. This predict-fn is for fewshot + # evaluator, so we need to turn it into reasonably-sized vector -> avg. + zimg = jnp.mean(zimg, axis=range(1, zimg.ndim - 1)) + return zimg, out + + +def _decode_with_logp( + train_state, batch, *, model, devices, max_decode_len, eos_token, + best_of_n=1, sampler="greedy", replicate_out=False, eos_look_behind=0): + """Sample token continuations to the input sequences.""" + mesh = jax.sharding.Mesh(devices, ("devices",)) + replicate_sharding = jax.sharding.NamedSharding(mesh, P()) + out_sharding = jax.sharding.NamedSharding( + mesh, P() if replicate_out else P("devices") + ) + + # Prefill the model cache and generate logits for first token. + logits, cache = jax.jit( + _prefill_cache, + out_shardings=out_sharding, + static_argnames=("model", "max_decode_len"), + )( + train_state["params"], + { + "image": batch["image"], + "text": batch["text"], + "mask_input": batch["mask_input"], + "mask_ar": batch["mask_ar"], + }, + model=model, + max_decode_len=max_decode_len, + ) + + # Mask indicating real examples. False if example is used to pad the batch. + mask = batch["_mask"] + + # Repeat example in case we are picking the best of n. + logits, cache, mask = jax.jit( + _bon_repeat, + static_argnames=("n",) + )((logits, cache, mask), n=best_of_n) + + decode_sample_output = jax.jit( + _decode_sample_output, + static_argnames=("max_decode_len", "sampler"), + ) + decode_early_stop = jax.jit( + _decode_early_stop, + out_shardings=replicate_sharding, + static_argnames=("eos_token",), + ) + extend_cache = jax.jit( + _extend_cache, + donate_argnums=1, + static_argnames=("model",), + ) + + # Keep sampling tokens from last logits until EOS or max_decode_len. + state = None + # Setting `eos_look_behind>0` removes blocking transfer with small batches. + stops = collections.deque(maxlen=1 + eos_look_behind) + for idx in range(max_decode_len): + tokens, state = decode_sample_output( + state, logits, max_decode_len=max_decode_len, sampler=sampler + ) + + if idx + 1 >= max_decode_len: + break + + stops.append(decode_early_stop(state, mask, eos_token=eos_token)) + if len(stops) == stops.maxlen and jax.device_get(stops[0]): + break + + # Compute logits for next token + logits, cache = extend_cache( + train_state["params"], cache, tokens, model=model + ) + + # Select the best of n sample for each example. + _, tokens, logp = jax.jit( + _bon_select, + out_shardings=out_sharding, + static_argnames=("n", "eos_token"), + )(state, n=best_of_n, eos_token=eos_token) + + return tokens, logp + + +def _decode(train_state, batch, **kwargs): + tokens, _ = _decode_with_logp(train_state, batch, **kwargs) + return tokens + + +def _bon_repeat(tree, *, n): + return jax.tree.map(lambda x: jnp.repeat(x, n, axis=0), tree) + + +def _compute_score(tokens, logp, eos_token): + """Compute log-probability of each sequence up to first eos (including it).""" + seqlen = jnp.sum(jnp.cumsum(tokens == eos_token, axis=-1) == 0, axis=-1) + 1 + token_mask = jnp.arange(tokens.shape[-1]) < seqlen[..., None] + scores = jnp.sum(logp * token_mask, axis=-1) + return scores + + +def _bon_select(state, *, n, eos_token): + """Pick the sampled sequence with the highest likelihood for each example.""" + (_, tokens, logp) = state + + # Filter state to only keep the best of each example. + scores = _compute_score(tokens, logp, eos_token) + scores = einops.rearrange(scores, "(b n) -> b n", n=n) + state = jax.tree.map( + lambda x: einops.rearrange(x, "(b n) l -> b n l", n=n), state) + best_indices = jnp.argmax(scores, -1) # [b] + state = jax.tree.map( + lambda x: jnp.take_along_axis(x, best_indices[:, None, None], axis=1), + state) + state = jax.tree.map(lambda x: x[:, 0], state) + + return state + + +def _decode_sample_output(state, logits, *, max_decode_len, sampler): + if state is None: + # Decode state keeps track of sampled tokens and their logp. + bs = logits.shape[0] + seqlen = jnp.zeros((bs, 1), dtype=jnp.int32) + tokens = jnp.zeros((bs, max_decode_len), dtype=jnp.int32) + logp = jnp.zeros((bs, max_decode_len), dtype=logits.dtype) + else: + (seqlen, tokens, logp) = state + + # Sample tokens. + sampled_tokens, sampled_logp = _sample_logits(logits, sampler=sampler) + + # Update state with sampled outputs. + new_len = seqlen + 1 + new_tokens = _put_along_last_axis(tokens, seqlen, sampled_tokens) + new_logp = _put_along_last_axis(logp, seqlen, sampled_logp) + new_state = (new_len, new_tokens, new_logp) + + return sampled_tokens, new_state + + +def _decode_early_stop(state, mask, *, eos_token): + (seqlen, tokens, unused_logp) = state + token_mask = jnp.arange(tokens.shape[-1])[None, :] < seqlen + has_eos = jnp.any(jnp.logical_and(tokens == eos_token, token_mask), axis=-1) + done = jnp.logical_or(has_eos, jnp.logical_not(mask)) + return jnp.all(done) + + +def _put_along_last_axis(arr, indices, values): + """Like np.put_along_axis(..., axis=-1), since jax is missing it.""" + assert arr.ndim == indices.ndim == values.ndim, ( + arr.ndim, indices.ndim, values.ndim) + onehot = jax.nn.one_hot(indices, arr.shape[-1], dtype=values.dtype) + put_mask = jnp.einsum("...i,...in->...n", + jnp.ones(values.shape, jnp.int32), onehot) + put_values = jnp.einsum("...i,...in->...n", values, onehot) + return jnp.where(put_mask, put_values, arr) + + +def _prefill_cache(params, batch, *, model, max_decode_len): + """Initialize the model cache for decoding with the prompts.""" + variables = {"params": params} + (x, input_mask, mask_ar), _ = model.apply( + variables, batch["image"], batch["text"], + input_mask=batch["mask_input"], + mask_ar=batch["mask_ar"], + method=model.embed_image_and_text) + last_logits, variables = model.apply( + variables, x, input_mask, mask_ar, + cache_size=x.shape[1] + max_decode_len, + method=model.prefill_cache, + mutable=("cache",)) + return last_logits, variables["cache"] + + +def _extend_cache(params, cache, tokens, *, model): + """Extend the model cache for decoding with one token per sequence.""" + variables = {"params": params, "cache": cache} + x, _ = model.apply(variables, tokens, method=model.embed_text) + last_logits, variables = model.apply( + variables, x, method=model.extend_cache, mutable=("cache",)) + return last_logits, variables["cache"] + + +def _sample_logits(logits, sampler): + """Returns a sampled token and its logp from logits.""" + # Note: Consider making it possible for evaluators to pass rng seed to + # decode functions. For now generate it from jax.lax and avoid evaluators + # having to deal with it. + rng = jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + + # Use Registry to support specifying things like: + # "greedy", "nucleus(0.2)", "temperature(t=1.0)" + sampled_tokens = registry.Registry.lookup("paligemma_sampler." + sampler)( + logits=logits, rng=rng) + + # Find the log probability (normalized logits) of selected tokens. + sampled_logp = jnp.take_along_axis( + jax.nn.log_softmax(logits, axis=-1), + sampled_tokens[..., None], -1)[..., 0] + + return sampled_tokens, sampled_logp + + +@registry.Registry.register("paligemma_sampler.greedy") +def _greedy_sampling(*, logits, rng): + del rng + return jnp.argmax(logits, axis=-1) + + +@registry.Registry.register("paligemma_sampler.temperature") +def _temperature_sampling(t, *, logits, rng): + return jax.random.categorical(rng, logits / t) + + +@registry.Registry.register("paligemma_sampler.nucleus") +def _nucleus_sampling(p: float, t: float = 1.0, *, logits, rng): + logits = logits / t + neg_inf = np.array(-1.0e7) # Effective negative infinity. + logits_sorted = jnp.sort(logits, axis=-1, descending=True) + sorted_cum_probs = jnp.cumsum( + jax.nn.softmax(logits_sorted, axis=-1), axis=-1) + cutoff_index = jnp.sum(sorted_cum_probs < p, axis=-1, keepdims=True) + cutoff_logit = jnp.take_along_axis(logits_sorted, cutoff_index, axis=-1) + logits = jnp.where(logits < cutoff_logit, + jnp.full_like(logits, neg_inf), logits) + return jax.random.categorical(rng, logits) + + +def _beam_decode(train_state, batch, *, + model, devices, max_decode_len, + eos_token, beam_size, replicate_out=False): + """Beam search (greedy/top-k exploration).""" + mesh = jax.sharding.Mesh(devices, ("devices",)) + replicate_sharding = jax.sharding.NamedSharding(mesh, P()) + out_sharding = jax.sharding.NamedSharding( + mesh, P() if replicate_out else P("devices") + ) + + # Prefill the model cache and generate logits for first token. + logits, cache = jax.jit( + _prefill_cache, + out_shardings=out_sharding, + static_argnames=("model", "max_decode_len"), + )( + train_state["params"], + { + "image": batch["image"], + "text": batch["text"], + "mask_input": batch["mask_input"], + "mask_ar": batch["mask_ar"], + }, + model=model, + max_decode_len=max_decode_len, + ) + + # Mask indicating real examples. False if example is used to pad the batch. + mask = batch["_mask"] + + beam_sample_output = jax.jit( + _beam_sample_output, + static_argnames=("max_decode_len", "beam_size", "eos_token"), + ) + beam_early_stop = jax.jit( + _beam_early_stop, + out_shardings=replicate_sharding, + static_argnames=("eos_token",), + ) + extend_cache = jax.jit( + _extend_cache, + donate_argnums=1, + static_argnames=("model",), + ) + + # Keep sampling tokens from last logits until EOS or max_decode_len. + state = None + for idx in range(max_decode_len): + tokens, state, cache = beam_sample_output( + state, logits, cache, + max_decode_len=max_decode_len, beam_size=beam_size, eos_token=eos_token) + + early_stop = beam_early_stop(state, mask, eos_token=eos_token) + if jax.device_get(early_stop) or (idx + 1 >= max_decode_len): + break + + # Compute logits for next token + logits, cache = extend_cache( + train_state["params"], cache, tokens, model=model) + + return jax.jit(_beam_make_output, out_shardings=out_sharding)(state) + + +def _beam_early_stop(state, mask, eos_token): + (best_tokens, best_logp, seqlen, unused_tokens, logp) = state + + # Scores of finalized sequences. + best_scores = _compute_score(best_tokens, best_logp, eos_token) + + # Scores of live sequences. + live_mask = jnp.arange(logp.shape[-1])[None, None] < seqlen + live_scores = jnp.sum(logp * live_mask, axis=-1) + live_scores = jnp.max(live_scores, axis=1) + + done = live_scores < best_scores + return jnp.all(jnp.logical_or(done, jnp.logical_not(mask))) + + +def _beam_make_output(state): + (best_tokens, *_) = state + return best_tokens[:, 0, ...] + + +def _beam_sample_output(state, logits, cache, *, + beam_size, max_decode_len, eos_token): + assert logits.shape[1] == 1 + logits = jax.nn.log_softmax(logits[:, 0, :]) # Normalize logits + + if state is None: + bs = logits.shape[0] + # Beam decode state keeps track of: + # A) Best sampled output for each example. At initialization these have + # shape[1]=0, but end up with shape[1]=1 after first call. + best_tokens = jnp.zeros((bs, 0, max_decode_len), dtype=jnp.int32) + best_logp = jnp.zeros((bs, 0, max_decode_len), dtype=logits.dtype) + # B) N candidate sequences for each example. At initialization these have + # beam_size=1, but end up with correct beam_size when expanded. + seqlen = jnp.zeros((bs, 1, 1), dtype=jnp.int32) + tokens = jnp.zeros((bs, 1, max_decode_len), dtype=jnp.int32) + logp = jnp.zeros((bs, 1, max_decode_len), dtype=logits.dtype) + else: + (best_tokens, best_logp, seqlen, tokens, logp) = state + bs = logits.shape[0] // beam_size + assert best_tokens.shape[0] == bs + + # Reshape cache to [example, candidate, ...]. + # Note: on first call the number of candidates is 1. Later it is beam_size. + cache, logits = jax.tree.map( + lambda x: einops.rearrange(x, "(b n) ... -> b n ...", b=bs), + (cache, logits)) + + # Consider a live sequence could end now and update the best finished + # sequences so far for each example. This strategy is found in some beam + # implementations such as in praxis. + # The code below also adjusts the best shape[1]=0 -> 1 during first call. + eos_tokens = jnp.array(eos_token)[None, None, None] + new_tokens = _put_along_last_axis(tokens, seqlen, eos_tokens) + new_logp = _put_along_last_axis(logp, seqlen, logits[:, :, eos_token, None]) + + best_tokens = jnp.concatenate([best_tokens, new_tokens], axis=1) + best_logp = jnp.concatenate([best_logp, new_logp], axis=1) + best_scores = _compute_score(best_tokens, best_logp, eos_token=eos_token) + _, top_indices = jax.lax.top_k(best_scores, k=1) + + best_tokens = jnp.take_along_axis(best_tokens, top_indices[..., None], axis=1) + best_logp = jnp.take_along_axis(best_logp, top_indices[..., None], axis=1) + + # To find the next best N live candidates we expand each candidate and keep + # the best N (ignoring EOS tokens). In this case we expand into (N+1) + # candidates and set their likelihood to "-inf" (if EOS) after the fact. + live_mask = jnp.arange(logp.shape[-1])[None, None] < seqlen + live_scores = jnp.sum(logp * live_mask, axis=-1) + topk_logits, topk_tokens = jax.lax.top_k(logits, beam_size+1) + scores = live_scores[..., None] + topk_logits + scores = jnp.where( + topk_tokens != eos_token, scores, jnp.finfo(scores.dtype).min) + + # From the N*(N+1) candidates find the top N for each example. + topk_logits, topk_tokens, scores = jax.tree.map( + lambda x: einops.rearrange(x, "b n np1 -> b (n np1)"), + (topk_logits, topk_tokens, scores)) + _, topk_indices = jax.lax.top_k(scores, k=beam_size) + sampled_indices = topk_indices // (beam_size+1) + sampled_tokens = jnp.take_along_axis( + topk_tokens, topk_indices, axis=-1)[..., None] + sampled_logits = jnp.take_along_axis( + topk_logits, topk_indices, axis=-1)[..., None] + + # Adjust cache and state so it matches the selected top N input candidates. + # This also adjusts the beam_size=1->n during first call. + def take_candidates(x): + one_hot_matrix = jax.nn.one_hot(sampled_indices, x.shape[1], dtype=x.dtype) + return jnp.einsum("bi...,boi->bo...", x, one_hot_matrix) + cache, seqlen, tokens, logp = jax.tree.map( + take_candidates, (cache, seqlen, tokens, logp)) + + # Write the sampled tokens/logits on the reshuffled state. + tokens = _put_along_last_axis(tokens, seqlen, sampled_tokens) + logp = _put_along_last_axis(logp, seqlen, sampled_logits) + seqlen = seqlen + 1 + + state = (best_tokens, best_logp, seqlen, tokens, logp) + + # Reshape to [(example, candidate), ...]. + sampled_tokens, cache = jax.tree.map( + lambda x: einops.rearrange(x, "b n ... -> (b n) ..."), + (sampled_tokens, cache)) + + return sampled_tokens, state, cache diff --git a/big_vision/trainers/proj/paligemma/run.py b/big_vision/trainers/proj/paligemma/run.py new file mode 100644 index 0000000000000000000000000000000000000000..b4b1f5d18507aab37d3c15293d9e3f15faf1d28a --- /dev/null +++ b/big_vision/trainers/proj/paligemma/run.py @@ -0,0 +1,141 @@ +# Copyright 2024 Big Vision 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. + +"""Load and run the PaliGemma model.""" +import functools +import sys + +from absl import app +from absl import flags +from absl import logging + +# pylint: disable=all +import jax +from jax.sharding import Mesh, NamedSharding, PartitionSpec +import ml_collections +import numpy as np + +import big_vision.models.proj.paligemma.gemma_bv +import big_vision.models.proj.paligemma.paligemma as model_mod +import big_vision.models.vit +import big_vision.pp.builder +import big_vision.pp.tokenizer +import big_vision.pp.ops_image +import big_vision.pp.ops_general +import big_vision.pp.ops_text +import big_vision.pp.proj.paligemma.ops +import big_vision.sharding +import big_vision.trainers.proj.paligemma.predict_fns +import big_vision.utils as u +# pylint: enable=all + +# We always want to be explicit about any host-device transfers. +jax.config.update("jax_transfer_guard", "disallow") + +CKPT = flags.DEFINE_string( + "ckpt", default=None, help="Path to checkpoint.") +IMAGE = flags.DEFINE_string( + "image", default=None, help="Path to input image.") + +SAMPLER = flags.DEFINE_string( + "sampler", default="greedy", help="Decoding strategy. Try `nucleus(0.1)`") +RES = flags.DEFINE_integer( + "res", default=224, help="Image resolution (224, 448, 896).") +MAX_DECODE_LEN = flags.DEFINE_integer( + "max_decode_len", default=128, help="Max total generation steps.") +PREFILL_LEN = flags.DEFINE_integer( + "prefill_len", default=32, help="Size of prefill (prompt). " + "Shorter is faster, but too short will cut off your prompt.") + +TOKENIZER = "gemma(tokensets=['loc', 'seg'])" + + +def load_model(ckpt): + model_cfg = ml_collections.FrozenConfigDict(dict( + img=dict(variant="So400m/14", pool_type="none", scan=True), + llm=dict(vocab_size=256_000 + 1024 + 128), + )) + model = model_mod.Model(**model_cfg) + params = model_mod.load(None, ckpt, model_cfg) + return model, params + + +def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + logging.flush() + + +def main(argv): + info(f"{argv=}") + info("Loading model...") + model, params = load_model(CKPT.value) + + predict_fns = big_vision.trainers.proj.paligemma.predict_fns.get_all(model) + + info("Loading tokenizer...") + tokzr = big_vision.pp.tokenizer.get_tokenizer(TOKENIZER) + + info("Creating mesh and sharding params...") + mesh = Mesh(jax.devices(), ("data")) + repl_sharding = NamedSharding(mesh, PartitionSpec()) + data_sharding = NamedSharding(mesh, PartitionSpec("data")) + params_sharding = big_vision.sharding.infer_sharding( + params, strategy=[(".*", "fsdp(axis='data')")], mesh=mesh) + + # Ship the params to device(s) + params = jax.tree.map(lambda x, sh: u.reshard(x, sh), params, params_sharding) + + # Mostly go through pp ops to build our batch: + pp_fn = big_vision.pp.builder.get_preprocess_fn("|".join([ + f"decode|resize({RES.value})|value_range(-1, 1)", + f"tok(key='prefix', bos='yes', model={repr(TOKENIZER)})", + f"tok(key='septok', text='\\n', model={repr(TOKENIZER)})", + 'masked_concat(["prefix", "septok"], mask_ar=[0, 0], mask_input=[1, 1])', + f'tolen({PREFILL_LEN.value}, pad_value=0, key="text")', + f'tolen({PREFILL_LEN.value}, pad_value=1, key="mask_ar")', + f'tolen({PREFILL_LEN.value}, pad_value=0, key="mask_input")', + 'keep("image", "text", "mask_ar", "mask_input")', + ]), log_data=False) + + decode = functools.partial( + predict_fns["decode"], devices=jax.devices(), + eos_token=tokzr.eos_token, max_decode_len=MAX_DECODE_LEN.value, + sampler=SAMPLER.value) + + def make_batch(fname, prompt): + image = open(fname, "rb").read() + + # Create an example + example = pp_fn({"image": image, "prefix": np.array(prompt)}) + example["_mask"] = np.array(True) # True means valid non-pad example + + batch = jax.tree.map(lambda x: x[None], example) + return u.reshard(batch, repl_sharding) # Move to device(s) + + info("Precompiling inference function...") + decode({"params": params}, batch=make_batch(IMAGE.value, "caption en")) + + info("Type a prompt and press enter, for example 'caption en': ") + for line in map(str.strip, sys.stdin): + tokens = decode({"params": params}, batch=make_batch(IMAGE.value, line)) + tokens = jax.device_get(tokens)[0] # First batch entry. + + # TODO: b/lbeyer - flip around: output on stdout, logs on stderr. + print(tokzr.to_str(tokens), file=sys.stderr, flush=True) + + +if __name__ == "__main__": + flags.mark_flag_as_required("ckpt") + flags.mark_flag_as_required("image") + app.run(main) diff --git a/big_vision/trainers/proj/paligemma/train.py b/big_vision/trainers/proj/paligemma/train.py new file mode 100644 index 0000000000000000000000000000000000000000..9a0d1bbb6b626a41b40a0cae3ac047c5a55d710c --- /dev/null +++ b/big_vision/trainers/proj/paligemma/train.py @@ -0,0 +1,525 @@ +# Copyright 2024 Big Vision 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. + +"""Training loop for PaliGemma-style VLM.""" +# pylint: disable=consider-using-from-import +# pylint: disable=logging-fstring-interpolation + +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +import big_vision.datasets.core as ds_core +import big_vision.evaluators.common as eval_common +import big_vision.input_pipeline as input_pipeline +import big_vision.optax as bv_optax +import big_vision.sharding as bv_sharding +import big_vision.trainers.proj.paligemma.predict_fns as predict_fns +import big_vision.utils as u +from clu import parameter_overview +import flax +import flax.linen as nn +import jax +from jax.experimental import mesh_utils +from jax.experimental import multihost_utils +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +import ml_collections as mlc +from ml_collections import config_flags +import numpy as np +import optax +import tensorflow as tf + +from tensorflow.io import gfile + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() +# Transfer guard will fail the program whenever that data between a host and +# a device is transferred implicitly. This often catches subtle bugs that +# cause slowdowns and memory fragmentation. Explicit transfers are done +# with jax.device_put and jax.device_get. +jax.config.update("jax_transfer_guard", "disallow") + + +NamedSharding = jax.sharding.NamedSharding +P = jax.sharding.PartitionSpec + + +def main(argv): + del argv + + # This is needed on multihost systems, but crashes on non-TPU single-host. + if os.environ.get("BV_JAX_INIT"): + jax.distributed.initialize() + + # Make sure TF does not touch GPUs. + tf.config.set_visible_devices([], "GPU") + +################################################################################ +# # +# Set up logging # +# # +################################################################################ + + # Set up work directory and print welcome message. + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info( + f"\u001b[33mHello from process {jax.process_index()} holding " + f"{jax.local_device_count()}/{jax.device_count()} devices and " + f"writing to workdir {workdir}.\u001b[0m") + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", ["ops_general", "ops_image", "ops_text"]): + importlib.import_module(f"big_vision.pp.{m}") + + # Setup up logging and experiment manager. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + + # Allow for things like timings as early as possible! + u.chrono.inform(measure=mw.measure, write_note=write_note) + +################################################################################ +# # +# Set up Mesh # +# # +################################################################################ + + # We rely on jax mesh_utils to organize devices, such that communication + # speed is the fastest for the last dimension, second fastest for the + # penultimate dimension, etc. + config_mesh = config.get("mesh", [("data", jax.device_count())]) + + # Sharding rules with the default of doing full data sharding. + sharding_rules = config.get("sharding_rules", [("act_batch", "data")]) + + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(jax.devices()).reshape(mesh_size).shape + + device_mesh = mesh_utils.create_device_mesh( + mesh_size, allow_split_physical_axes=config.get( + "mesh_allow_split_physical_axes", False)) + + # Consistent device order is important to ensure correctness of various train + # loop components, such as input pipeline, update step, evaluators. The + # order prescribed by the `devices_flat` variable should be used throughout + # the program. + devices_flat = device_mesh.flatten() + +################################################################################ +# # +# Input Pipeline # +# # +################################################################################ + + write_note("Initializing train dataset...") + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + train_ds, ntrain_img = input_pipeline.training(config.input) + + total_steps = u.steps("total", config, ntrain_img, batch_size) + def get_steps(name, default=ValueError, cfg=config): + return u.steps(name, cfg, ntrain_img, batch_size, total_steps, default) + + u.chrono.inform(total_steps=total_steps, global_bs=batch_size, + steps_per_epoch=ntrain_img / batch_size) + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + # Start input pipeline as early as possible, this will kick-start filling + # shuffle buffers and get the first batch in a background thread. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_global( + train_ds, devices_flat, n_prefetch, warmup=n_prefetch > 0) + + # For mixed data, add per-dataset epoch and examples seen measurements. + if isinstance(config.input.data.get("name"), str): + measure_per_dataset_times = lambda step: None # No-op + else: + nexamples = { + name: ds_core.get(**config.input[name].data).total_examples + for name in config.input.data + } + def measure_per_dataset_times(step): + total = sum(config.input.data.values()) + for name, w in config.input.data.items(): + w = w / total + mw.measure(f"examples_seen_{name}", u.chrono.accum_examples_seen * w) + mw.measure(f"epoch_{name}", step * batch_size * w / nexamples[name]) + +################################################################################ +# # +# Create Model & Optimizer # +# # +################################################################################ + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**mlc.FrozenConfigDict(config.get("model", {}))) + + def init(rng, partial_params=None): + batch = jax.tree.map(lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + _, variables = model.apply( # flax init is just apply with mutable. + {"params": partial_params or {}}, + batch["image"], batch["text"][:, :-1], batch["mask_ar"][:, :-1], + rngs={"params": rng, "dropout": rng}, + mutable=["params"]) + return flax.core.unfreeze(variables["params"]) + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(u.put_cpu(config.get("seed", 0))) + + write_note("Inferring parameter shapes...") + rng, rng_init = jax.random.split(rng) + params_shape = jax.eval_shape(init, rng_init) + params_shape = nn.unbox(params_shape) + + write_note("Inferring optimizer state shapes...") + tx, sched_fns = bv_optax.make(config, params_shape, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + opt_shape = jax.eval_shape(tx.init, params_shape) + # We jit this, such that the arrays are created on the CPU, not device[0]. + sched_fns_cpu = [u.jit_cpu()(sched_fn) for sched_fn in sched_fns] + + if jax.process_index() == 0: + num_params = sum(np.prod(p.shape) for p in jax.tree.leaves(params_shape)) + mw.measure("num_params", num_params) + +################################################################################ +# # +# Init and/or load model onto devices # +# # +################################################################################ + + write_note("Creating device mesh...") + mesh = jax.sharding.Mesh(device_mesh, mesh_axes) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + write_note("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + strategy = config.get("sharding_strategy", [(".*", "replicate")]) + train_state_sharding = bv_sharding.infer_sharding( + train_state_shape, strategy=strategy, mesh=mesh) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from scratch or from something, e.g. fine-tuning job. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(f"{save_ckpt_path}-LAST"): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + + if resume_ckpt_path: + write_note(f"Resuming training from checkpoint {resume_ckpt_path}...") + shardings = { + **train_state_sharding, + "chrono": jax.tree.map(lambda _: repl_sharding, u.chrono.save()), + } + loaded = u.load_checkpoint_ts( + resume_ckpt_path, tree=shardings, shardings=shardings) + train_state = {key: loaded[key] for key in train_state_sharding.keys()} + u.chrono.load(jax.device_get(loaded["chrono"])) + del loaded + else: + write_note( + f"Initialize model from {config.get('model_init') or 'scratch'}...") + + # To avoid holding two copies of parameters we first call `model.load` + # and then initialize the missing variables. + if config.get("model_init"): + # We call `model.load` with params shape, so it can know all model params + # including their shapes and dtypes (also shardings once wired). + params = model_mod.load( + params_shape, config.model_init, config.get("model"), + **config.get("model_load", {})) + + # Keep only params loaded by `model.load` and shard them into devices. + mask = jax.tree.map( + lambda x: not isinstance(x, jax.ShapeDtypeStruct), params) + params = u.reshard(u.tree_filter(params, mask), + u.tree_filter(train_state_sharding["params"], mask)) + + parameter_overview.log_parameter_overview( + params, msg="Restored params", + include_stats="global", jax_logging_process=0) + else: + params = {} + + # Init will initialize any missing params. + rng_init = u.reshard(rng_init, repl_sharding) + params = jax.jit( + init, donate_argnums=1, out_shardings=train_state_sharding["params"])( + rng_init, params) + params = nn.unbox(params) + + # Initialize optimizer and construct train_state. + opt = jax.jit(tx.init, out_shardings=train_state_sharding["opt"])(params) + train_state = {"params": params, "opt": opt} + del params, opt # Delete to avoid memory leak or accidental reuse. + + parameter_overview.log_parameter_overview( + train_state["params"], msg="Parameter overview", + include_stats="global", jax_logging_process=0) + + rng, rng_loop = jax.random.split(rng, 2) + rng_loop = u.reshard(rng_loop, repl_sharding) + del rng, rng_init # not used anymore, so delete it. + +################################################################################ +# # +# Update Step # +# # +################################################################################ + + @functools.partial( + jax.jit, + donate_argnums=(0,), + out_shardings=(train_state_sharding, repl_sharding)) + def update_fn(train_state, rng, batch): + """Update step.""" + + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + assert "mixup" not in config, "Mixup is not supported for SigLIP." + + # Get device-specific loss rng. + _, rng_model = jax.random.split(rng, 2) + + imgs, txts, mask_ar = batch["image"], batch["text"], batch["mask_ar"] + + def loss_fn(params): + text_logits, _ = model.apply( + {"params": params}, imgs, txts[:, :-1], mask_ar[:, :-1], + train=True, rngs={"dropout": rng_model}) + + logp = jax.nn.log_softmax(text_logits, axis=-1) + targets = jax.nn.one_hot(txts[:, 1:], text_logits.shape[-1]) + off_value = config.get("label_smoothing", 0.0) + if off_value > 0: + denom = text_logits.shape[-1] - 1 + targets = jnp.where( + targets == 1.0, 1.0 - off_value, off_value / denom) + + # Sum across vocab. + token_pplx = jnp.sum(logp * targets, axis=-1) + + # Shift by one since the loss is on the _next_ token. + mask_loss = batch["mask_loss"][:, 1:] + token_pplx = token_pplx * mask_loss + pplx = -jnp.sum(token_pplx, axis=-1) + pplx /= jnp.clip(jnp.sum(mask_loss, axis=-1), 1) + + # In this dict the (outer) reduction is along batch. + measurements = dict( + training_loss=jnp.mean(pplx), + avg_sup_seqlen=jnp.mean(jnp.sum(mask_loss, axis=-1)), + max_sup_seqlen=jnp.max(jnp.sum(mask_loss, axis=-1)), + ) + + return measurements["training_loss"], measurements + + params, opt = train_state["params"], train_state["opt"] + (_, measurements), grads = jax.value_and_grad(loss_fn, has_aux=True)(params) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + gs = jax.tree.leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.sum(g * g) for g in gs])) + ps = jax.tree.leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.sum(p * p) for p in ps])) + us = jax.tree.leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.sum(u * u) for u in us])) + + return {"params": params, "opt": opt}, measurements + +################################################################################ +# # +# Setup Evals # +# # +################################################################################ + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, + predict_fns.get_all(model), + lambda s: write_note(f"Init evaluator: {s}…\n{u.chrono.note}"), + lambda key, cfg: get_steps(key, default=None, cfg=cfg), + devices_flat, + ) + + # At this point we need to know the current step to see whether to run evals. + write_note("Inferring the first step number...") + first_step_device = bv_optax.get_count(train_state["opt"], jittable=True) + first_step = int(jax.device_get(first_step_device)) + u.chrono.inform(first_step=first_step) + + # Note that training can be pre-empted during the final evaluation (i.e. + # just after the final checkpoint has been written to disc), in which case we + # want to run the evals. + if first_step in (total_steps, 0): + write_note("Running initial or final evals...") + mw.step_start(first_step) + for (name, evaluator, _, prefix) in evaluators(): + if config.evals[name].get("skip_first") and first_step != total_steps: + continue + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", value) + +################################################################################ +# # +# Train Loop # +# # +################################################################################ + + prof = None # Keeps track of start/stop of profiler state. + ckpt_mngr = None + + write_note("Starting training loop, compiling the first step...") + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + with u.chrono.log_timing("z/secs/update0", noop=step > first_step + 1): + with mesh, nn.logical_axis_rules(sharding_rules): + train_state, measurements = update_fn(train_state, rng_loop, batch) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or u.chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", + sched_fn_cpu(u.put_cpu(step - 1))) + measurements = jax.device_get(measurements) + for name, value in measurements.items(): + mw.measure(name, value) + u.chrono.tick(step) + measure_per_dataset_times(step) + + for k in ("training_loss", "l2_params", "l2_grads"): + if not np.isfinite(measurements.get(k, 0.0)): + raise RuntimeError(f"{k} became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + + # Checkpoint saving + keep_last = total_steps if get_steps("ckpt", None) else None + keep_ckpt_steps = get_steps("keep_ckpt", None) or keep_last + if save_ckpt_path and ( + (keep := u.itstime(step, keep_ckpt_steps, total_steps, first=False)) + or u.itstime(step, get_steps("ckpt", None), total_steps, first=True) + ): + u.chrono.pause(wait_for=train_state) + + # Copy because we add extra stuff to the checkpoint. + ckpt = {**train_state} + + # To save chrono state correctly and safely in a multihost setup, we + # broadcast the state to all hosts and convert it to a global array. + with jax.transfer_guard("allow"): + chrono_ckpt = multihost_utils.broadcast_one_to_all(u.chrono.save()) + chrono_shardings = jax.tree.map(lambda _: repl_sharding, chrono_ckpt) + ckpt = ckpt | {"chrono": u.reshard(chrono_ckpt, chrono_shardings)} + + ckpt_mngr = ckpt_mngr or array_serial.GlobalAsyncCheckpointManager() + u.save_checkpoint_ts(ckpt_mngr, ckpt, save_ckpt_path, step, keep) + u.chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=False, last=True): + u.chrono.pause(wait_for=train_state) + u.chrono.tick(step) # Record things like epoch number, core hours etc. + write_note(f"{name} evaluation...\n{u.chrono.note}") + with u.chrono.log_timing(f"z/secs/eval/{name}"): + with mesh, nn.logical_axis_rules(sharding_rules): + for key, value in evaluator.run(train_state): + mw.measure(f"{prefix}{key}", jax.device_get(value)) + u.chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Last note needs to happen before the pool's closed =) + write_note(f"Done!\n{u.chrono.note}") + + pool.close() + pool.join() + mw.close() + if ckpt_mngr: + ckpt_mngr.wait_until_finished() + + # Make sure all hosts stay up until the end of main. + u.sync() + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/uvim/coco_utils.py b/big_vision/trainers/proj/uvim/coco_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..70183030ac9d441e0e9b49007be7775b16cab233 --- /dev/null +++ b/big_vision/trainers/proj/uvim/coco_utils.py @@ -0,0 +1,75 @@ +# Copyright 2022 Big Vision 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. + +"""Utilities to inspect coco data and predictions in notebooks.""" +# pylint: disable=consider-using-from-import +import functools +import json + +import numpy as np +from panopticapi import utils as pycoco_utils +from skimage import segmentation + +import tensorflow.io.gfile as gfile + + +import os +ROOT = os.environ.get('COCO_DATA_DIR', '.') + + +PANOPTIC_COCO_CATS_FILE = f'{ROOT}/panoptic_coco_categories.json' + + +@functools.lru_cache(maxsize=None) +def _coco_panoptic_categories(): + with gfile.GFile(PANOPTIC_COCO_CATS_FILE, 'r') as f: + categories_list = json.load(f) + return tuple(categories_list) + + +def rgb_panoptic_from_twochannels(twochannels, boundaries: bool = False): + """Makes a RGB panoptic output and segments_info from a twochannels view.""" + semantics = twochannels[..., 0] + instances = twochannels[..., 1] + max_instances = np.max(instances) + 1 + merged = semantics * max_instances + instances + merged = np.where(semantics < 0, semantics, merged) + + categories_list = _coco_panoptic_categories() + categories = {category['id']: category for category in categories_list} + id_generator = pycoco_utils.IdGenerator(categories) + segments_info = {} + rgb = np.zeros((*instances.shape[:2], 3), dtype=np.uint8) + + for merged_id in np.unique(merged): + if merged_id // max_instances > 0: + category = categories_list[int(merged_id // max_instances) - 1] + segment_id, color = id_generator.get_id_and_color(category['id']) + else: + category = {'id': -1, 'name': 'void', 'isthing': False} + segment_id, color = -1, np.array([0, 0, 0]) + segments_info[segment_id] = { + 'id': segment_id, + 'color': color, + 'category_id': category['id'], + 'name': category['name'], + 'isthing': category['isthing'], + } + rgb[merged == merged_id] = color + + if boundaries: + boundaries = segmentation.find_boundaries( + pycoco_utils.rgb2id(rgb), mode='thick') + rgb[boundaries] = 0 + return rgb, segments_info diff --git a/big_vision/trainers/proj/uvim/colorization_task.py b/big_vision/trainers/proj/uvim/colorization_task.py new file mode 100644 index 0000000000000000000000000000000000000000..1624e1b2d3465f65f69d0634a3268d682a1c1ccc --- /dev/null +++ b/big_vision/trainers/proj/uvim/colorization_task.py @@ -0,0 +1,62 @@ +# Copyright 2022 Big Vision 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. + +"""Inputs, outputs and losses for colorization task.""" +import einops +import jax.numpy as jnp +import numpy as np + +ONE_HOT_AXIS = -2 + + +def input_pp(batch, config): + """Make inputs for colorization task.""" + if "labels" not in batch: + # During predict of phase2 there is no 'labels' field. + x = None + else: + hp, wp = config.model.patch_size + x = { + "color": batch["labels"], + } + # Convert labels from (B, H, W) to (B, num_patches, C, patch_size) + x["color"] = einops.rearrange( + x["color"], "b (hn hp) (wn wp) c -> b (hn wn) c (hp wp)", hp=hp, wp=wp) + ctx = batch.get("image_ctx", batch.get("image", None)) + return {"ctx": ctx, "x": x} + + +def loss_fn(logits, batch, config): + """Compute loss for colorization task.""" + labels = input_pp(batch, config)["x"] + error = logits["color"] - labels["color"] + loss = jnp.square(error) + return loss, {"loss_color": loss} + + +def predict_outputs(logits, config): + """Make outputs for colorization task.""" + # Map logits to (height, width, channels). + hp, wp = config.model.patch_size + hn, wn = np.array(config.model.input_size) // np.array((hp, wp)) + assert ONE_HOT_AXIS == -2, "Rearrange below depends on this." + output = einops.rearrange( + logits["color"], + "b (hn wn) c (hp wp) -> b (hn hp) (wn wp) c", + hn=hn, + wn=wn, + hp=hp, + wp=wp) + output = jnp.clip(output, -1., 1.) + return {"color": output} diff --git a/big_vision/trainers/proj/uvim/depth_task.py b/big_vision/trainers/proj/uvim/depth_task.py new file mode 100644 index 0000000000000000000000000000000000000000..e4768b769e586da2397e56d6312e61268e479830 --- /dev/null +++ b/big_vision/trainers/proj/uvim/depth_task.py @@ -0,0 +1,91 @@ +# Copyright 2022 Big Vision 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. + +"""Inputs, outputs and losses for depth prediction task.""" +import big_vision.utils as u +import einops +import jax +import jax.numpy as jnp +import numpy as np + + +ONE_HOT_AXIS = -2 + + +def input_pp(batch, config): + """Makes inputs for depth prediction task.""" + if "labels" not in batch: + x = None + else: + hp, wp = config.model.patch_size + depth = batch["labels"][..., 0] + + # Discretize to [0, ..., bins - 1]. + nbins = config.model.inputs.depth[ONE_HOT_AXIS] + mind = config.min_depth + maxd = config.max_depth + depth = (depth - mind) / (maxd - mind) + depth *= nbins + depth = jnp.floor(depth).astype(jnp.int32) + depth = jnp.minimum(depth, nbins - 1) + depth = jnp.maximum(depth, 0) + + # Converts labels from (B, H, W, c) to (B, num_patches, c, patch_size). + depth = jax.nn.one_hot( + einops.rearrange( + depth, "b (hn hp) (wn wp) -> b (hn wn) (hp wp)", hp=hp, wp=wp), + num_classes=config.model.inputs.depth[ONE_HOT_AXIS], + axis=ONE_HOT_AXIS) + x = {"depth": depth} + ctx = batch.get("image_ctx", batch.get("image", None)) + return {"ctx": ctx, "x": x} + + +def loss_fn(predictions, batch, config): + """Computes loss for depth prediction task.""" + labels = input_pp(batch, config)["x"] + losses = {} + loss = u.softmax_xent( + logits=predictions["depth"], labels=labels["depth"], reduction=False, + axis=ONE_HOT_AXIS) + # Do not train on the closest class; usually regions of the image with + # depth==0, which is the default for regions with no depth signal. + # TODO: Encode depth==0 as class==-1. + mask = jnp.argmax(labels["depth"], ONE_HOT_AXIS) != 0 + loss = loss * mask + losses["loss_depth"] = loss + return sum(losses.values()), losses + + +def predict_outputs(predictions, config): + """Makes outputs for depth predictin tasks.""" + # Maps predictions to (height, width, channels). + hp, wp = config.model.patch_size + hn, wn = np.array(config.model.input_size) // np.array((hp, wp)) + depth = einops.rearrange( + predictions["depth"], + "b (hn wn) c (hp wp) -> b (hn hp) (wn wp) c", + hn=hn, wn=wn, hp=hp, wp=wp) + + depth = jnp.argmax(depth, axis=-1) # [B, H, W] + + # Revert discretization. + nbins = config.model.inputs.depth[ONE_HOT_AXIS] + mind = config.min_depth + maxd = config.max_depth + depth = depth.astype(jnp.float32) + 0.5 # Undoes floor in expectation. + depth /= nbins + depth = depth * (maxd - mind) + mind + + return {"depth": depth} diff --git a/big_vision/trainers/proj/uvim/panoptic_task.py b/big_vision/trainers/proj/uvim/panoptic_task.py new file mode 100644 index 0000000000000000000000000000000000000000..4049c496252105ff283e8f0120dbce06fb24ef2b --- /dev/null +++ b/big_vision/trainers/proj/uvim/panoptic_task.py @@ -0,0 +1,87 @@ +# Copyright 2022 Big Vision 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. + +"""Inputs, outputs and losses for panoptic task.""" +import big_vision.utils as u +import einops +import jax +import jax.numpy as jnp +import numpy as np + +ONE_HOT_AXIS = -2 + + +def input_pp(batch, config): + """Make inputs for panoptic segmentation task.""" + if "labels" not in batch: + # During predict of phase2 there is no 'labels' field. + x = None + else: + hp, wp = config.model.patch_size + x = { + "semantics": batch["labels"][..., 0], + "instances": batch["labels"][..., 1], + } + # Convert labels from (B, H, W) to (B, num_patches, num_classes, patch_size) + for key in ["semantics", "instances"]: + x[key] = jax.nn.one_hot( + einops.rearrange( + x[key], "b (hn hp) (wn wp) -> b (hn wn) (hp wp)", hp=hp, wp=wp), + num_classes=config.model.inputs[key][ONE_HOT_AXIS], axis=ONE_HOT_AXIS) + ctx = batch.get("image_ctx", batch.get("image", None)) + return {"ctx": ctx, "x": x} + + +def loss_fn(logits, batch, config): + """Compute loss for panoptic task.""" + labels = input_pp(batch, config)["x"] + losses = {} + for key in ["semantics", "instances"]: + losses[f"loss_{key}"] = u.softmax_xent( + logits=logits[key], labels=labels[key], reduction=False, + axis=ONE_HOT_AXIS) + return sum(losses.values()), losses + + +def predict_outputs(logits, config, min_fraction=0.0): + """Make outputs for panoptic segmentation task.""" + # Map logits to (height, width, channels). + hp, wp = config.model.patch_size + hn, wn = np.array(config.model.input_size) // np.array((hp, wp)) + outputs = {} + for key in ["semantics", "instances"]: + assert ONE_HOT_AXIS == -2, "Rearrange below depends on this." + outputs[key] = einops.rearrange( + logits[key], + "b (hn wn) c (hp wp) -> b (hn hp) (wn wp) c", + hn=hn, wn=wn, hp=hp, wp=wp) + return panoptic_predictions_from_logits( + **outputs, min_fraction=min_fraction) + + +def panoptic_predictions_from_logits(semantics, instances, min_fraction=0.0): + """Make panoptic prediction from logits.""" + ins = jnp.argmax(instances, axis=-1) + # Note: Make sure each instance has all pixels annotated with same label. + # Otherwise they are further split into more instances and greatly affect + # the number of unmatched predicted segments (FP) and RQ. + masks = jax.nn.one_hot(ins, instances.shape[-1], dtype=jnp.int32) + label = jnp.argmax(jnp.einsum("bhwk,bhwn->bnk", semantics, masks), axis=-1) + sem = jnp.einsum("bhwn,bn->bhw", masks, label) + out = jnp.stack([sem, ins], axis=-1) + # Filter out small objects + fraction = jnp.sum(masks, axis=(1, 2), keepdims=True)/np.prod(ins.shape[1:3]) + mask_big = (fraction > min_fraction).astype("int32") + mask_big_spatial = jnp.sum(masks * mask_big, axis=-1, keepdims=True) > 0 + return out * mask_big_spatial.astype("int32") diff --git a/big_vision/trainers/proj/uvim/train.py b/big_vision/trainers/proj/uvim/train.py new file mode 100644 index 0000000000000000000000000000000000000000..bbaec203f748197ca7be2fd76a0c8c4aa58c804a --- /dev/null +++ b/big_vision/trainers/proj/uvim/train.py @@ -0,0 +1,440 @@ +# Copyright 2022 Big Vision 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. + +"""Train loop for training the stage-II model.""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +from big_vision import input_pipeline +import big_vision.datasets.core as ds_core +import big_vision.evaluators.common as eval_common +import big_vision.models.proj.uvim.decode as decode +import big_vision.optax as bv_optax +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax + +import tensorflow.io.gfile as gfile + + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +FLAGS = flags.FLAGS +ONE_HOT_AXIS = -2 +partial = functools.partial + + +def get_model(config): + mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = mod.Model(**config.model) + return model, mod + + +def setup_task(config): + """Get functions and params to encode and decode labels as token sequences.""" + config = config.oracle + + # Define task input and predict functions. + task_module = importlib.import_module(f"big_vision.trainers.{config.task}") + input_fn = partial(task_module.input_pp, config=config) + predict_outputs_fn = partial(task_module.predict_outputs, config=config) + + oracle, mod = get_model(config) + if config.get("model_init", None): + params, state = mod.load(None, config.model_init) + params = {"params": params, "state": state} + else: + params = {} + + def encode_labels(params, batch): + inputs = input_fn(batch) + code = oracle.apply(params, **inputs, method=oracle.encode)[1]["code"] + return code + 1 # To avoid padding symbol. + + def decode_labels(params, code, batch, **kwargs): + code = code - 1 + inputs = input_fn(batch) + inputs["x"] = code + logits, _ = oracle.apply( + params, **inputs, discrete_input=True, **kwargs, method=oracle.decode) + return logits + + return encode_labels, decode_labels, predict_outputs_fn, params + + +def main(argv): + del argv + + config = FLAGS.config + workdir = FLAGS.workdir + logging.info("\u001b[33mHello from process %i holding %i/%i devices and " + "writing to workdir %s.\u001b[0m", jax.process_index(), + jax.local_device_count(), jax.device_count(), workdir) + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", + ["ops_general", "ops_image", "proj.uvim.pp_ops"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + chrono = u.Chrono() + + write_note("Initializing train dataset...") + train_data = ds_core.get(**config.input.data) + train_ds = input_pipeline.make_for_train( + data=train_data.get_tfdata(ordered=False), + batch_size=batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(config.input.get("pp")), + shuffle_buffer_size=config.input.get("shuffle_buffer_size"), + cache_raw=config.input.get("cache_raw", False), + filter_fn=config.input.get("filter_fn"), + ) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + ntrain_img = train_data.total_examples + + def get_steps(name, default=ValueError): # partial doesn't work well here. + return u.steps(name, config, ntrain_img, batch_size, default) + total_steps = get_steps("total") + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + write_note(f"Initializing {config.model_name} model...") + model, model_mod = get_model(config) + + encode_labels, decode_labels, predict_outputs_fn, task_params = ( + setup_task(config)) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @partial(jax.jit, backend="cpu") + def init(rng): + batch = jax.tree_map( + lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + images = batch["image"] + labels = encode_labels(task_params, batch) + variables = model.init(rng, images, labels) + params = flax.core.unfreeze(variables["params"]) + return params + + rng, init_rng = jax.random.split(rng) + params_cpu = init(init_rng) + + if jax.process_index() == 0: + num_params = sum(p.size for p in jax.tree_leaves(params_cpu)) + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + mw.measure("num_params", num_params) + + write_note(f"Initializing {config.optax_name} optimizer...") + tx, sched_fns = bv_optax.make(config, params_cpu, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1)) + def update_fn(params, opt, batch, update_rng, task_params): + """Update step.""" + images = batch["image"] + labels = encode_labels(task_params, batch) + + measurements = {} + + rng, new_rng = jax.random.split(update_rng) + # bind the rng key to the device id (which is unique across hosts) + rng_local = jax.random.fold_in(rng, jax.lax.axis_index("batch")) + + def loss_fn(params, images, labels): + logits = model.apply({"params": params}, images, labels, train=True, + rngs={"dropout": rng_local}) + loss = u.weighted_softmax_xent( + logits=logits, labels=labels, + reduction=True, normalize=True) + return loss + + l, grads = jax.value_and_grad(loss_fn)(params, images, labels) + l, grads = jax.lax.pmean((l, grads), axis_name="batch") + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, l, new_rng, measurements + + # Define evaluators. + def validation_fn(params, batch): + """Compute per-example metrics.""" + params, task_params = params["params"], params["task_params"] + images = batch["image"] + labels = encode_labels(task_params, batch) + logits = model.apply({"params": params}, images, labels, train=False) + loss = u.weighted_softmax_xent( + logits=logits, labels=labels, + reduction=False, normalize=True) + losses = {"loss": loss} + return jax.tree_map( + lambda x: jnp.mean(x, axis=tuple(range(1, x.ndim))), + losses) + + def predict_fn(params, batch, seed=0, temperature=1e-7, **extra): + params, task_params = params["params"], params["task_params"] + + # Derive a rng key from the inputs so that all batches use different keys. + if "image/id" in batch: + key = batch["image/id"] + else: + key = batch["image"].sum(axis=[1, 2, 3]).astype(jnp.int32) + local_rng = jax.lax.scan( + lambda k, x: (jax.random.fold_in(k, x), None), + jax.random.PRNGKey(seed), + key, + )[0] + + images = batch["image"] + batch_size = images.shape[0] + prompts = jnp.zeros((batch_size, config.model.seq_len), dtype=jnp.int32) + seqs, _, _ = decode.temperature_sampling( + params={"params": params}, model=model, seed=local_rng, + inputs=images, prompts=prompts, + num_samples=1, eos_token=-1, prefill=False, + temperature=temperature) + seqs = jnp.squeeze(seqs, 1) + logits = decode_labels(task_params, seqs, batch) + return predict_outputs_fn(logits, **extra) + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, {"predict": predict_fn, "validation": validation_fn}, + lambda s: write_note(f"Initializing evaluator: {s}...\n{chrono.note}") + ) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Initialize part of the model from something, eg. only encoder or decoder. + # 5. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + checkpoint = { + "params": params_cpu, + "opt": opt_cpu, + "chrono": chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint(checkpoint_tree, resume_ckpt_path) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu, opt_cpu = checkpoint["params"], checkpoint["opt"] + chrono.load(checkpoint["chrono"]) + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu = model_mod.load( + params_cpu, config.model_init, config.model, + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu, msg="restored params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + chrono.inform(first_step, total_steps, batch_size, ntrain_img / batch_size) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + task_params = flax.jax_utils.replicate(task_params) + update_rngs = flax.jax_utils.replicate(rng) + + ckpt_writer = None + + write_note(f"First step compilations...\n{chrono.note}") + error = None # For exiting with an error after cleanup. Avoids indentation. + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + params_repl, opt_repl, loss_value, update_rngs, measurements = ( + update_fn( + params_repl, + opt_repl, + batch, + update_rng=update_rngs, + task_params=task_params)) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + chrono.tick(step, mw.measure, write_note) + if not np.isfinite(l): + error = (f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + break + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + chrono.pause(wait_for=(params_repl, opt_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + opt_cpu = jax.tree_map(lambda x: np.array(x[0]), opt_repl) + params_cpu = jax.tree_map(lambda x: np.array(x[0]), params_repl) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = {"params": params_cpu, "opt": opt_cpu, "chrono": chrono.save()} + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps, first=log_steps < total_steps, + last=False): + chrono.pause(wait_for=(params_repl, task_params)) + write_note(f"{name} evaluation...\n{chrono.note}") + for key, value in evaluator.run( + {"params": params_repl, "task_params": task_params}): + mw.measure(f"{prefix}{key}", value) + chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Run final evalution, also used for eval only jobs (when total_steps == 0). + for (name, evaluator, _, prefix) in evaluators(): + write_note(f"{name} evaluation...\n{chrono.note}") + for key, value in evaluator.run( + {"params": params_repl, "task_params": task_params}): + mw.measure(f"{prefix}{key}", value) + + # Last note needs to happen before the pool's closed =) + if not error: + write_note(f"Done!\n{chrono.note}") + else: + write_note(f"Failed!\n{error}\n{chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + # Before cleanup, as cleanup should only run for successful jobs. + if error is not None: + raise RuntimeError(error) + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/trainers/proj/uvim/vqvae.py b/big_vision/trainers/proj/uvim/vqvae.py new file mode 100644 index 0000000000000000000000000000000000000000..13467d02ca3c95c4bd8c9a38b9970db24c0f4e3a --- /dev/null +++ b/big_vision/trainers/proj/uvim/vqvae.py @@ -0,0 +1,414 @@ +# Copyright 2022 Big Vision 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. + +"""Train loop for training the stage-I model.""" +# pylint: disable=consider-using-from-import +import functools +import importlib +import multiprocessing.pool +import os + +from absl import app +from absl import flags +from absl import logging +from big_vision import input_pipeline +import big_vision.datasets.core as ds_core +import big_vision.evaluators.common as eval_common +import big_vision.optax as bv_optax +import big_vision.pp.builder as pp_builder +import big_vision.utils as u +from clu import parameter_overview +import flax +import jax +import jax.numpy as jnp +from ml_collections import config_flags +import numpy as np +import optax + +import tensorflow.io.gfile as gfile + + +SG = jax.lax.stop_gradient +partial = functools.partial + +config_flags.DEFINE_config_file( + "config", None, "Training configuration.", lock_config=True) + +flags.DEFINE_string("workdir", default=None, help="Work unit directory.") +flags.DEFINE_boolean("cleanup", default=False, + help="Delete workdir (only) after successful completion.") + +# Adds jax flags to the program. +jax.config.parse_flags_with_absl() + + +def main(argv): + del argv + + config = flags.FLAGS.config + workdir = flags.FLAGS.workdir + logging.info("Workdir: %s", workdir) + + logging.info("\u001b[33mHello from process %i holding %i/%i devices and " + "writing to workdir %s.\u001b[0m", jax.process_index(), + jax.local_device_count(), jax.device_count(), workdir) + + # Define task input, loss and predict functions. + task_module = importlib.import_module(f"big_vision.trainers.{config.task}") + input_pp_fn = partial(task_module.input_pp, config=config) + task_loss_fn = partial(task_module.loss_fn, config=config) + predict_outputs_fn = partial(task_module.predict_outputs, config=config) + + save_ckpt_path = None + if workdir: # Always create if requested, even if we may not write into it. + gfile.makedirs(workdir) + save_ckpt_path = os.path.join(workdir, "checkpoint.npz") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool() + + # Here we register preprocessing ops from modules listed on `pp_modules`. + for m in config.get("pp_modules", + ["ops_general", "ops_image", "proj.uvim.pp_ops"]): + importlib.import_module(f"big_vision.pp.{m}") + + # This seed makes the Jax part of things (like model init) deterministic. + # However, full training still won't be deterministic, for example due to the + # tf.data pipeline not being deterministic even if we would set TF seed. + # See (internal link) for a fun read on what it takes. + rng = jax.random.PRNGKey(config.get("seed", 0)) + + # These functions do more stuff internally, for OSS release we mock them by + # trivial alternatives in order to minize disruptions in the code. + xid, wid = -1, -1 + fillin = lambda s: s + def info(s, *a): + logging.info("\u001b[33mNOTE\u001b[0m: " + s, *a) + def write_note(note): + if jax.process_index() == 0: + info("%s", note) + + write_note("Initializing...") + + batch_size = config.input.batch_size + if batch_size % jax.device_count() != 0: + raise ValueError(f"Batch size ({batch_size}) must " + f"be divisible by device number ({jax.device_count()})") + info("Global batch size %d on %d hosts results in %d local batch size. With " + "%d dev per host (%d dev total), that's a %d per-device batch size.", + batch_size, jax.process_count(), batch_size // jax.process_count(), + jax.local_device_count(), jax.device_count(), + batch_size // jax.device_count()) + + # First thing after above sanity checks, so we can log "start" ticks. + mw = u.BigVisionMetricWriter(xid, wid, workdir, config) + chrono = u.Chrono() + + write_note("Initializing train dataset...") + train_data = ds_core.get(**config.input.data) + train_ds = input_pipeline.make_for_train( + data=train_data.get_tfdata(ordered=False), + batch_size=batch_size, + preprocess_fn=pp_builder.get_preprocess_fn(config.input.get("pp")), + shuffle_buffer_size=config.input.get("shuffle_buffer_size"), + cache_raw=config.input.get("cache_raw", False), + filter_fn=config.input.get("filter_fn"), + ) + + # Start prefetching already. + n_prefetch = config.get("prefetch_to_device", 1) + train_iter = input_pipeline.start_input_pipeline(train_ds, n_prefetch) + ntrain_img = train_data.total_examples + + def get_steps(name, default=ValueError): # partial doesn't work well here. + return u.steps(name, config, ntrain_img, batch_size, default) + total_steps = get_steps("total") + + info("Running for %d steps, that means %f epochs", + total_steps, total_steps * batch_size / ntrain_img) + + write_note(f"Initializing {config.model_name} model...") + model_mod = importlib.import_module(f"big_vision.models.{config.model_name}") + model = model_mod.Model(**config.model) + + # We want all parameters to be created in host RAM, not on any device, they'll + # be sent there later as needed, otherwise we already encountered two + # situations where we allocate them twice. + @partial(jax.jit, backend="cpu") + def init(rng): + batch = jax.tree_map( + lambda x: jnp.zeros(x.shape, x.dtype.as_numpy_dtype), + train_ds.element_spec) + init_res = flax.core.unfreeze(model.init(rng, **input_pp_fn(batch))) + params, state = init_res["params"], init_res["state"] + + # Set bias in the heads to a low value, such that loss is small initially. + for key in config.model.outputs: + params[f"head_{key}"]["bias"] = jnp.full_like( + params[f"head_{key}"]["bias"], config.get("init_head_bias", 0)) + + return params, state + + rng, rng_init = jax.random.split(rng) + + rng_init_params, rng_init_state = jax.random.split(rng_init) + params_cpu, state_cpu = init({"params": rng_init_params, + "state": rng_init_state}) + + if jax.process_index() == 0: + num_params = sum(p.size for p in jax.tree_leaves(params_cpu)) + parameter_overview.log_parameter_overview(params_cpu, msg="init params") + mw.measure("num_params", num_params) + + write_note(f"Initializing {config.optax_name} optimizer...") + tx, sched_fns = bv_optax.make(config, params_cpu, sched_kw=dict( + total_steps=total_steps, batch_size=batch_size, data_size=ntrain_img)) + + # We jit this, such that the arrays are created on the CPU, not device[0]. + opt_cpu = jax.jit(tx.init, backend="cpu")(params_cpu) + sched_fns_cpu = [jax.jit(sched_fn, backend="cpu") for sched_fn in sched_fns] + + @partial(jax.pmap, axis_name="batch", donate_argnums=(0, 1, 2), + static_broadcasted_argnums=(5,)) + def update_fn(params, opt, state, batch, rng, update_dict=True): + """Update step.""" + measurements = {} + + # Get device-specific loss rng. + rng, rng_model = jax.random.split(rng, 2) + rng_model_local = jax.random.fold_in(rng_model, jax.lax.axis_index("batch")) + + def loss_fn(params, state, batch): + (logits, out), mutated_col = model.apply( + {"params": params, "state": state}, + **input_pp_fn(batch), + train=True, update_dict=update_dict, + rngs={"dropout": rng_model_local, "vqvae": rng_model}, + mutable=["state"]) + btlneck = out["bottleneck"] + btlneck_q = out["bottleneck_q"] + + loss_rec, logs = jax.tree_map(jnp.mean, task_loss_fn(logits, batch)) + loss_commitment = jnp.mean(jnp.square(btlneck - SG(btlneck_q))) + loss = loss_rec + config.get("w_commitment", 0.25) * loss_commitment + aux = { + "loss_rec": jax.lax.pmean(loss_rec, axis_name="batch"), + "loss_commitment": jax.lax.pmean(loss_commitment, axis_name="batch"), + "codebook_zeros_ratio": out["codebook_zeros_ratio"], + "codebook_max_ratio": out["codebook_max_ratio"], + "state": mutated_col["state"], + **jax.tree_map(partial(jax.lax.pmean, axis_name="batch"), logs), + } + return loss, aux + + (l, aux), grads = jax.value_and_grad(loss_fn, has_aux=True)( + params, state, batch) + l, grads = jax.lax.pmean((l, grads), axis_name="batch") + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + state = aux.pop("state") + measurements = {**measurements, **aux} + + gs = jax.tree_leaves(bv_optax.replace_frozen(config.schedule, grads, 0.)) + measurements["l2_grads"] = jnp.sqrt(sum([jnp.vdot(g, g) for g in gs])) + ps = jax.tree_leaves(params) + measurements["l2_params"] = jnp.sqrt(sum([jnp.vdot(p, p) for p in ps])) + us = jax.tree_leaves(updates) + measurements["l2_updates"] = jnp.sqrt(sum([jnp.vdot(u, u) for u in us])) + + return params, opt, state, l, rng, measurements + + # Define evaluators. + def validation_fn(params, batch): + """Compute per-example metrics.""" + logits, out = model.apply(params, **input_pp_fn(batch)) + _, losses = task_loss_fn(logits, batch) + btlneck = out["bottleneck"] + btlneck_q = out["bottleneck_q"] + losses["loss_commitment"] = jnp.square(btlneck - btlneck_q) + return jax.tree_map( + lambda x: jnp.mean(x, axis=tuple(range(1, x.ndim))), + losses) + + def predict_fn(params, batch): + logits, _ = model.apply(params, **input_pp_fn(batch)) + outputs = predict_outputs_fn(logits) + return outputs + + # Only initialize evaluators when they are first needed. + @functools.lru_cache(maxsize=None) + def evaluators(): + return eval_common.from_config( + config, {"predict": predict_fn, "validation": validation_fn}, + lambda s: write_note(f"Initializing evaluator: {s}...\n{chrono.note}") + ) + + # Decide how to initialize training. The order is important. + # 1. Always resumes from the existing checkpoint, e.g. resumes a finetune job. + # 2. Resume from a previous checkpoint, e.g. start a cooldown training job. + # 3. Initialize model from something, e,g, start a fine-tuning job. + # 4. Train from scratch. + resume_ckpt_path = None + if save_ckpt_path and gfile.exists(save_ckpt_path): + resume_ckpt_path = save_ckpt_path + elif config.get("resume"): + resume_ckpt_path = fillin(config.resume) + if resume_ckpt_path: + write_note("Resume training from checkpoint...") + checkpoint = { + "params": params_cpu, + "state": state_cpu, + "opt": opt_cpu, + "chrono": chrono.save(), + } + checkpoint_tree = jax.tree_structure(checkpoint) + loaded = u.load_checkpoint(checkpoint_tree, resume_ckpt_path) + # bfloat16 type gets lost when data is saved to disk, so we recover it. + checkpoint = jax.tree_map(u.recover_dtype, loaded) + params_cpu = checkpoint["params"] + state_cpu = checkpoint["state"] + opt_cpu = checkpoint["opt"] + chrono.load(checkpoint["chrono"]) + elif config.get("model_init"): + write_note(f"Initialize model from {config.model_init}...") + params_cpu, state_cpu = model_mod.load( + {"params": params_cpu, "state": state_cpu}, + config.model_init, config.model, + **config.get("model_load", {})) + if jax.process_index() == 0: + parameter_overview.log_parameter_overview( + params_cpu, msg="restored params") + + write_note("Kicking off misc stuff...") + first_step = bv_optax.get_count(opt_cpu) + chrono.inform(first_step, total_steps, batch_size, ntrain_img / batch_size) + prof = None # Keeps track of start/stop of profiler state. + + write_note(f"Replicating...\n{chrono.note}") + params_repl = flax.jax_utils.replicate(params_cpu) + opt_repl = flax.jax_utils.replicate(opt_cpu) + state_repl = flax.jax_utils.replicate(state_cpu) + + rng, rng_loop = jax.random.split(rng, 2) + rngs_loop = flax.jax_utils.replicate(rng_loop) + ckpt_writer = None + + write_note(f"First step compilations...\n{chrono.note}") + error = None # For exiting with an error after cleanup. Avoids indentation. + + # Using a python integer for step here, because opt.state.step is allocated + # on TPU during replication. + for step, batch in zip(range(first_step + 1, total_steps + 1), train_iter): + mw.step_start(step) + + with jax.profiler.StepTraceAnnotation("train_step", step_num=step): + params_repl, opt_repl, state_repl, loss_value, rngs_loop, measurements = ( + update_fn( + params_repl, + opt_repl, + state_repl, + batch, + rngs_loop, + not config.get("freeze_dict", True))) + + # On the first host, let's always profile a handful of early steps. + if jax.process_index() == 0: + prof = u.startstop_prof(prof, step, first_step, get_steps("log_training")) + + # Report training progress + if (u.itstime(step, get_steps("log_training"), total_steps, host=0) + or chrono.warmup and jax.process_index() == 0): + for i, sched_fn_cpu in enumerate(sched_fns_cpu): + mw.measure(f"global_schedule{i if i else ''}", sched_fn_cpu(step - 1)) + l = mw.measure("training_loss", loss_value[0]) + for name, value in measurements.items(): + mw.measure(name, value[0]) + chrono.tick(step, mw.measure, write_note) + if not np.isfinite(l): + error = (f"The loss became nan or inf somewhere within steps " + f"[{step - get_steps('log_training')}, {step}]") + break + + # Checkpoint saving + if (save_ckpt_path and + (u.itstime(step, get_steps("ckpt", None), total_steps, host=0) or + u.itstime(step, get_steps("keep_ckpt", None), total_steps, host=0))): + chrono.pause(wait_for=(params_repl, opt_repl, state_repl)) + u.checkpointing_timeout(ckpt_writer, config.get("ckpt_timeout", 1)) + # We need to transfer the weights over now or else we risk keeping them + # alive while they'll be updated in a future step, creating hard to debug + # memory errors (see (internal link)). Also, takes device 0's params only. + params_cpu, opt_cpu, state_cpu = jax.tree_map( + lambda x: np.array(x[0]), (params_repl, opt_repl, state_repl)) + + # Check whether we want to keep a copy of the current checkpoint. + copy_step = None + if u.itstime(step, get_steps("keep_ckpt", None), total_steps): + copy_step = step + + ckpt = { + "params": params_cpu, + "state": state_cpu, + "opt": opt_cpu, + "chrono": chrono.save(), + } + ckpt_writer = pool.apply_async( + u.save_checkpoint, (ckpt, save_ckpt_path, copy_step)) + chrono.resume() + + for (name, evaluator, log_steps, prefix) in evaluators(): + if u.itstime(step, log_steps, total_steps): + chrono.pause(wait_for=(params_repl, state_repl)) + write_note(f"{name} evaluation...\n{chrono.note}") + for key, value in evaluator.run( + {"params": params_repl, "state": state_repl}): + mw.measure(f"{prefix}{key}", value) + chrono.resume() + mw.step_end() + + # Always give a chance to stop the profiler, no matter how things ended. + # TODO: can we also do this when dying of an exception like OOM? + if jax.process_index() == 0 and prof is not None: + u.startstop_prof(prof) + + # Support eval only runs: run evaluation if total_steps (or num_epochs) is 0. + if total_steps == 0: + for (name, evaluator, _, prefix) in evaluators(): + write_note(f"{name} evaluation...\n{chrono.note}") + for key, value in evaluator.run( + {"params": params_repl, "state": state_repl}): + mw.measure(f"{prefix}{key}", value) + + # Last note needs to happen before the pool's closed =) + if not error: + write_note(f"Done!\n{chrono.note}") + else: + write_note(f"Failed!\n{error}\n{chrono.note}") + + pool.close() + pool.join() + mw.close() + + # Make sure all hosts stay up until the end of main. + u.sync() + + # Before cleanup, as cleanup should only run for successful jobs. + if error is not None: + raise RuntimeError(error) + + u.maybe_cleanup_workdir(workdir, flags.FLAGS.cleanup, info) + + +if __name__ == "__main__": + app.run(main) diff --git a/big_vision/utils.py b/big_vision/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..14b3b03211500b076d719d3f673baeda7761641c --- /dev/null +++ b/big_vision/utils.py @@ -0,0 +1,1427 @@ +# Copyright 2024 Big Vision 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. + +"""Utils very specific to this project, not generic.""" + +import collections +import contextlib +import dataclasses +import functools +import io +import json +import multiprocessing +import multiprocessing.pool +import os +import re +import sys +import time +from typing import Mapping + +from absl import flags +from absl import logging +from big_vision.pp import registry as pp_registry +import einops +import flax +import flax.jax_utils as flax_utils +import jax +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +import ml_collections as mlc +import numpy as np + +import tensorflow.io.gfile as gfile # pylint: disable=consider-using-from-import + + +Registry = pp_registry.Registry + + +# pylint: disable=logging-fstring-interpolation + + +def pad_shard_unpad(wrapped, static_argnums=(0,), static_argnames=()): + """Wraps a function with code that pads, shards, then un-shards, un-pads. + + Args: + wrapped: the function to be wrapped. Signature is `params, *args, *kwargs`. + static_argnums: indices of arguments to `wrapped` that should _not_ be + padded and sharded, but instead be forwarded as-is. The default is (0,) + because by far the most common use-case is to pass `params` first. + static_argnames: names of kwargs to `wrapped` that should _not_ be padded + and sharded, but instead be forwarded as-is. + + Returns: + A new function that pads and shards its arguments before passing them to + the wrapped function, and un-shards and un-pads the returned pytree. + + This is useful for calling a pmap'ed function with inputs that aren't + divisible by the number of devices. A typical use is: + @pad_shard_unpad + @jax.pmap + def forward(params, x): ... + + Notes: + The padding is done in host-memory before being passed to the function, and + the values returned by the function are transferred back to host memory. + + The returned function is augmented with a new keyword-only argument + `min_device_batch` that, if specified, forces padding inputs to at least + this size per device. This can be useful to avoid recompiles for the last + batch and reduce memory fragmentation. + """ + + def pad_shard_unpad_wrapper(*args, min_device_batch=None, **kw): + d = jax.local_device_count() # d = devices, b = batch + + # Find the batch-sizes of all non-static arguments. + def get_bs(x): + batch_sizes = jax.tree.map(lambda y: y.shape[0], x) + return jax.tree.flatten(batch_sizes)[0] + + bs_a = [get_bs(a) for i, a in enumerate(args) if i not in static_argnums] + bs_kw = [get_bs(v) for k, v in kw.items() if k not in static_argnames] + bs = set([n for b in (bs_a + bs_kw) for n in b]) + assert len(bs) == 1, f"Inconsistent batch-sizes: {bs}" + b = bs.pop() + + def pad(x): + _, *shape = x.shape + db, rest = divmod(b, d) + if rest: + x = np.concatenate([x, np.zeros((d - rest, *shape), x.dtype)], axis=0) + db += 1 + if min_device_batch and db < min_device_batch: + x = np.concatenate( + [x, np.zeros((d * (min_device_batch - db), *shape), x.dtype)]) + db = min_device_batch + return x.reshape(d, db, *shape) + + def maybe_pad(x, actually_pad=True): + if not actually_pad: return x # For call-site convenience below. + return jax.tree.map(pad, x) + + args = [maybe_pad(a, i not in static_argnums) for i, a in enumerate(args)] + kw = {k: maybe_pad(v, k not in static_argnames) for k, v in kw.items()} + out = wrapped(*args, **kw) + + def unpad(x): + # Transfer back before cutting, to reduce on-device shape diversity. + return einops.rearrange(jax.device_get(x), "d b ... -> (d b) ...")[:b] + return jax.tree.map(unpad, out) + + return pad_shard_unpad_wrapper + + +def onehot(labels, num_classes, on_value=1.0, off_value=0.0): + x = (labels[..., None] == jnp.arange(num_classes)[None]) + x = jax.lax.select(x, jnp.full(x.shape, on_value), + jnp.full(x.shape, off_value)) + return x.astype(jnp.float32) + + +def npload(fname): + """Loads `fname` and returns an np.ndarray or dict thereof.""" + # Load the data; use local paths directly if possible: + if os.path.exists(fname): + loaded = np.load(fname, allow_pickle=False) + else: + # For other (remote) paths go via gfile+BytesIO as np.load requires seeks. + with gfile.GFile(fname, "rb") as f: + data = f.read() + loaded = np.load(io.BytesIO(data), allow_pickle=False) + + # Support loading both single-array files (np.save) and zips (np.savez). + if isinstance(loaded, np.ndarray): + return loaded + else: + return dict(loaded) + + +def load_checkpoint_np(npz, tree=None): + """Loads a jax pytree from a npz file. + + Args: + npz: Either path to the checkpoint file (.npz), or a dict-like. + tree: deprecated, use None. + Bwd-compat for old format that only stored values: the pytree structure. + + Returns: + A pytree that is the checkpoint. + """ + if isinstance(npz, str): # If not already loaded, then load. + npz = npload(npz) + keys, values = zip(*list(npz.items())) + if tree: + checkpoint = tree.unflatten(values) + else: + checkpoint = recover_tree(keys, values) + return checkpoint + + +def load_params(ckpt, **kw): + """Loads the parameters of a big_vision checkpoint, both old or new format. + + Args: + ckpt: Path to the checkpoint (.npz, .ts) or dict-like. + **kw: forwarded to the underlying load function (_np or _ts). + + Returns: + A pytree that is the checkpoint, potentially sharded. + + Notes: + The `ckpt` string can contain an colon-separated "submodel" indicator, like + `img` in the example `/path/to/file.npz:img`. + This is used to load sub-parts of a model, for example the image load the + image encoder out of a two_tower (SigLIP) checkpoint, or distillation. + This way, ANY model that uses this function can load itself from a + checkpoint that contains multiple sub-models. + """ + key = None # Whether we want to extract only a sub-key of the model. + + if isinstance(ckpt, str): # Most common case of passing a checkpoint path. + # Potentially read out the sub-part to load from after the colon + # '/path/to/file:img/head' => '/path/to/file', 'img/head' + # 'gs://path/to/file' => 'gs://path/to/file', None + if match := re.match(r"^(.*?/.*?)(?::([\w/]+))?$", ckpt): + ckpt, key = match.groups() + else: + raise ValueError(f"Weird ckpt path: {ckpt} ; Maybe prepend ./ ?") + + # Use the checkpoint filename to detect when we're loading old-style .npz + # checkpoints, as opposed to new-style tensorstore checkpoint folders. + if ".npz" in ckpt: # Not a perfect heuristic, but good enough. + checkpoint = load_checkpoint_np(ckpt, **kw) + checkpoint = jax.tree.map(recover_dtype, checkpoint) + if "params" in checkpoint: + # Checkpoint with optax state (after (internal link)). + params = checkpoint["params"] + elif "opt" in checkpoint: + # Checkpoint with Flax optimizer. + params = checkpoint["opt"]["target"] + else: + # When open-sourcing, we often shared only the params directly. + params = checkpoint + else: + # Here we're now loading new-style tensorstore checkpoints. + # We can be a more efficient and load params and `key` only right away. + regex = f"params/{key}($|/.*)" if key else "params/.*" + checkpoint = load_checkpoint_ts(ckpt, regex=regex) + params = checkpoint["params"] + + if key is not None: + params = tree_get(params, key) + + return params + + +def prefetch_scalar(it, nprefetch=1, devices=None): + n_loc_dev = len(devices) if devices else jax.local_device_count() + repl_iter = (np.ones(n_loc_dev) * i for i in it) + return flax_utils.prefetch_to_device(repl_iter, nprefetch, devices) + + +def sigmoid_xent(*, logits, labels, reduction=True): + # NOTE: This implementation is stable, see these two: + # (internal link) + # https://github.com/google/jax/issues/2140 + log_p = jax.nn.log_sigmoid(logits) + log_not_p = jax.nn.log_sigmoid(-logits) + nll = -jnp.sum(labels * log_p + (1. - labels) * log_not_p, axis=-1) + return jnp.mean(nll) if reduction else nll + + +def bidirectional_contrastive_loss(zimg, ztxt, t, mask=None, reduction=False): + """Bidirectional contrastive loss (e.g. for contrastive trainer/evaluator).""" + # BF.FB = BB + logits = jnp.dot(zimg, ztxt.T) * t + + if mask is not None: + # Set to negative infinity where mask = 0. Masked examples will disappear + # under softmax, and be ignored by ncorrect (NINF will never win argmax). + exclude = jnp.logical_not(mask) # Now 1 if we don't want to keep. + exclude = jnp.logical_or(exclude[:, None], exclude[None, :]) + logits = jnp.where(exclude, -jnp.inf, logits) + + # Note: assumed t is in a good range e.g. already passed through exp/softplus. + l1 = -jnp.diag(jax.nn.log_softmax(logits, axis=1)) # NLL img->txt + l2 = -jnp.diag(jax.nn.log_softmax(logits, axis=0)) # NLL txt->img + l = 0.5 * (l1 + l2) + + if mask is not None: + l = jnp.where(mask, l, 0) + + redux = jnp.mean if reduction else lambda x: x + if reduction and mask is not None: + redux = lambda x: jnp.sum(x * mask) / (jnp.sum(mask) + 1e-8) + + # Also return extra measurements. + return redux(l), { + "ncorrect": redux(jnp.argmax(logits, axis=1) == jnp.arange(len(logits))), + } + + +def softmax_xent(*, logits, labels, reduction=True, kl=False, axis=-1): + log_p = jax.nn.log_softmax(logits, axis=axis) + nll = -jnp.sum(labels * log_p, axis=axis) + if kl: + nll += jnp.sum(labels * jnp.log(jnp.clip(labels, 1e-8)), axis=axis) + return jnp.mean(nll) if reduction else nll + + +def weighted_softmax_xent(*, + logits, + labels, + reduction=True, + weights=None, + label_smoothing=0.0, + normalize=True): + """Compute weighted cross entropy. + + Args: + logits: [batch, length, num_classes] float array. + labels: categorical targets [batch, length] int array. + reduction: reduce across batch dim. + weights: None or array of shape [batch, length]. + label_smoothing: label smoothing constant, used to determine the on and off + values. + normalize: normalize each "sentence" loss by the number of tokens in it. + + Returns: + Tuple of scalar loss and batch normalizing factor. + """ + if logits.ndim != labels.ndim + 1: + raise ValueError("Incorrect shapes. Got shape %s logits and %s targets" % + (str(logits.shape), str(labels.shape))) + vocab_size = logits.shape[-1] + confidence = 1.0 - label_smoothing + low_confidence = (1.0 - confidence) / (vocab_size - 1) + soft_targets = onehot( + labels, vocab_size, on_value=confidence, off_value=low_confidence) + + loss = -jnp.sum(soft_targets * jax.nn.log_softmax(logits), axis=-1) + + normalizing_factor = labels.shape[1] + if weights is not None: + loss = loss * weights + normalizing_factor = jnp.clip(weights.sum(axis=1), 2e-38) + + loss = loss.sum(axis=1) + if normalize: + loss = loss / normalizing_factor + + return loss.mean() if reduction else loss + + +def accumulate_gradient(loss_and_grad_fn, params, images, labels, accum_steps): + """Accumulate gradient over multiple steps to save on memory.""" + # See (internal link) for details and experiments. + if accum_steps and accum_steps > 1: + assert images.shape[0] % accum_steps == 0, ( + f"Bad accum_steps {accum_steps} for batch size {images.shape[0]}") + step_size = images.shape[0] // accum_steps + l, g = loss_and_grad_fn(params, images[:step_size], labels[:step_size]) + def acc_grad_and_loss(i, l_and_g): + imgs = jax.lax.dynamic_slice(images, (i*step_size, 0, 0, 0), + (step_size,) + images.shape[1:]) + lbls = jax.lax.dynamic_slice(labels, (i*step_size, 0), + (step_size, labels.shape[1])) + li, gi = loss_and_grad_fn(params, imgs, lbls) + l, g = l_and_g + return (l + li, jax.tree.map(lambda x, y: x + y, g, gi)) + l, g = jax.lax.fori_loop(1, accum_steps, acc_grad_and_loss, (l, g)) + return jax.tree.map(lambda x: x / accum_steps, (l, g)) + else: + return loss_and_grad_fn(params, images, labels) + + +def itstime(step, every_n_steps, total_steps, host=None, last=True, first=True, + drop_close_to_last=0.25): + """Returns True if it's time to execute an action. + + Args: + step: the current step representing "now". + every_n_steps: the action should run every this many steps. + total_steps: the step number of the last step of training. + host: host number. If provided, only run if we are this process. + last: whether to run on the last step or not. + first: whether to run on the first step or not. + drop_close_to_last: if a step would run, but is this close (in terms of + fraction of every_n_step) to the last one, skip. + + Returns: + True if the action should be executed, False if not. + """ + + # This logic avoids running `itstime` "a few" steps before the last step. + # Canonical example: don't save checkpoint 2 steps before the last, and then + # at the last again; it's pointless and checkpoint timing will time out. + close_to_last = False + if drop_close_to_last and every_n_steps: + close_to_last = abs(step - total_steps) < drop_close_to_last * every_n_steps + + is_host = host is None or jax.process_index() == host + is_step = every_n_steps and (step % every_n_steps == 0) and not close_to_last + is_last = every_n_steps and step == total_steps + is_first = every_n_steps and step == 1 + return is_host and (is_step or (last and is_last) or (first and is_first)) + + +def checkpointing_timeout(writer, timeout): + # Make sure checkpoint writing is not a bottleneck + if writer is not None: + try: + # Note: `writer` is a multiprocessing.AsyncResult, and + # timeout is in seconds. + writer.get(timeout=timeout) + except multiprocessing.TimeoutError as e: + raise TimeoutError( + "Checkpoint writing seems to be a bottleneck. Make sure you do " + "not do something wrong, like writing checkpoints to a distant " + "cell. In a case you are OK with checkpoint writing being a " + "bottleneck, you can configure `ckpt_timeout` parameter") from e + + +def hms(s): + """Format time in hours/minutes/seconds.""" + if s < 60: + return f"{s:.0f}s" + m, s = divmod(s, 60) + if m < 60: + return f"{m:.0f}m{s:.0f}s" + h, m = divmod(m, 60) + if h < 25: + return f"{h:.0f}h{m:.0f}m" # Seconds intentionally omitted. + d, h = divmod(h, 24) + return f"{d:.0f}d{h:.0f}h{m:.0f}m" # Seconds intentionally omitted. + + +class Chrono: + """Measures time and reports progress, hyper-specific to our train loops. + + Some concepts: + 1. This differentiates between three "types" of time: + - training time: the time spent on actual training (fprop/bprop/update) + - program time: overall time the program runs, including all overheads + - pause time: the chronometer can be paused (eg during evals). + 2. This handles a "warmup": the first step is skipped for training time + purposes, as it includes significant compilation overheads, which distort + estimates. + 3. `accum`ulates (i.e. integrates) timings, and save/load them across + restarts. + """ + + def __init__(self): + self._timing_history = collections.defaultdict(list) + self._measure = None + self._write_note = None + + self.program_start_time = time.monotonic() + self.train_start_time = None + self.train_start_step = None # When we started timing (after warmup) + + self.prev_time = None + self.prev_step = None + + self.pause_start = None + self.paused_time = 0 + + self.total_steps = None + self.global_bs = None + self.steps_per_epoch = None + + self.warmup = 2 # How many calls to `tick` to skip. + self.load() # Inits accum integrators. + self.note = "Chrono n/a" + + def inform(self, *, first_step=None, total_steps=None, global_bs=None, + steps_per_epoch=None, measure=None, write_note=None): + """Provide some extra info that's only known later in the program.""" + # The pattern of `self.x = x or self.x` allows one to call `inform` various + # times with various subset of information (args), as they become available. + # Except for `first_step` which can be 0 so is a bit more verbose. + self.prev_step = first_step if first_step is not None else self.prev_step + self.total_steps = total_steps or self.total_steps + self.steps_per_epoch = steps_per_epoch or self.steps_per_epoch + self.global_bs = global_bs or self.global_bs + self._measure = measure or self._measure + self._write_note = write_note or self._write_note + if self.total_steps and self.prev_step is not None: + self.note = (f"Steps:{self.prev_step}/{self.total_steps} " + f"[{self.prev_step/self.total_steps:.1%}]") + + def tick(self, step, measure=None, write_note=None): + """A chronometer tick.""" + if step == self.prev_step: return # Can happen from evals for example. + + measure = measure or self._measure + write_note = write_note or self._write_note + + now = time.monotonic() + measure("uptime", now - self.program_start_time) + self.flush_timings() + + # We do always count examples, regardless of the timing-related warmup that + # happens a few lines below. + ds = step - self.prev_step # Steps between ticks + self.prev_step = step + self.accum_examples_seen += ds * self.global_bs + measure("examples_seen", self.accum_examples_seen) + measure("progress", step / self.total_steps) + if self.steps_per_epoch: + measure("epoch", step / self.steps_per_epoch) + + # We take the start as the second time `tick` is called, so we avoid + # measuring the overhead of compilation and don't include it in time + # estimates. + if self.warmup > 1: + self.warmup -= 1 + write_note(self.note) # This can help debugging. + return + if self.warmup == 1: + self.train_start_time = self.prev_time = now + self.train_start_step = step + self.accum_program_time += now - self.program_start_time + self.paused_time = 0 # Drop pauses that happened before timing starts. + self.warmup = 0 + write_note(self.note) # This can help debugging. + return + + # Measurement with micro-timings of current training steps speed. + # Time between ticks (ignoring pause) + dt = now - self.prev_time - self.paused_time + ncores = jax.device_count() # Global device count + measure("img/sec/core", self.global_bs * ds / dt / ncores) + + # Accumulate (integrate) times, good for plots. + self.accum_train_time += dt + self.accum_pause_time += self.paused_time + self.accum_program_time += dt + self.paused_time + + # Convert to, and log as, core hours. + core_hours = self.accum_train_time * ncores / 60 / 60 + devtype = jax.devices()[0].device_kind + measure(f"core_hours_{devtype}", core_hours) + measure("core_hours", core_hours) # For convenience as x-axis in sweeps. + + # Progress note with "global" full-program average timings + # (eg in program-time minus warmup) + dt = now - self.train_start_time # Time elapsed since end of warmup. + steps_timed = step - self.train_start_step + steps_todo = self.total_steps - step + self.note = f"Steps:{step}/{self.total_steps} [{step/self.total_steps:.1%}]" + self.note += f"\nWalltime:{hms(self.accum_program_time)}" + self.note += f" ({hms(self.accum_pause_time)} eval)" + self.note += f"\nETA:{hms(dt / steps_timed*steps_todo)}" + self.note += f"\nTotal train time:{hms(dt / steps_timed*self.total_steps)}" + write_note(self.note) + + self.prev_time = now + self.paused_time = 0 + + def pause(self, wait_for=()): + assert self.pause_start is None, "Don't pause twice." + jax.block_until_ready(wait_for) + self.pause_start = time.monotonic() + + def resume(self): + self.paused_time += time.monotonic() - self.pause_start + self.pause_start = None + + def save(self): + return dict( + accum_program_time=self.accum_program_time, + accum_train_time=self.accum_train_time, + accum_pause_time=self.accum_pause_time, + accum_examples_seen=self.accum_examples_seen, + ) + + def load(self, ckpt={}): # pylint: disable=dangerous-default-value + self.accum_program_time = float(ckpt.get("accum_program_time", 0.0)) + self.accum_train_time = float(ckpt.get("accum_train_time", 0.0)) + self.accum_pause_time = float(ckpt.get("accum_pause_time", 0.0)) + self.accum_examples_seen = int(ckpt.get("accum_examples_seen", 0)) + + @contextlib.contextmanager + def log_timing(self, name, *, noop=False): + """Use this when you time sth once per step and want instant flushing.""" + t0 = time.monotonic() + yield + dt = time.monotonic() - t0 + if not noop: + if self._measure: # So that timed things still work in colab. + self._measure(name, dt) + logging.info("TIMING[%s]: %s", name, dt) + logging.flush() + + @contextlib.contextmanager + def log_timing_avg(self, name, *, noop=False): + """Use this when you time sth multiple times per step (eg in a loop).""" + t0 = time.monotonic() + yield + dt = time.monotonic() - t0 + if not noop: + self._timing_history[name].append(dt) + logging.info("TIMING[%s]: avg %s current %s", + name, np.mean(self._timing_history[name]), dt) + logging.flush() + + def flush_timings(self): + assert self._measure is not None + for name, times in self._timing_history.items(): + self._measure(name, np.mean(times)) + self._timing_history.clear() + + +# Singleton to use from everywhere. https://stackoverflow.com/a/6760726/2366315 +chrono = Chrono() + + +def _traverse_with_names(tree, with_inner_nodes=False): + """Traverses nested dicts/dataclasses and emits (leaf_name, leaf_val).""" + if dataclasses.is_dataclass(tree): + tree = flax.serialization.to_state_dict(tree) + # Don't output the non-leaf nodes. If the optimizer doesn't have a state + # the tree leaves can be Nones which was interpreted as a leaf by this + # function but not by the other functions (like jax.tree.map). + if tree is None: + return + elif isinstance(tree, Mapping): + keys = sorted(tree.keys()) + for key in keys: + for path, v in _traverse_with_names(tree[key], with_inner_nodes): + yield (key + "/" + path).rstrip("/"), v + if with_inner_nodes: + yield "", tree + elif isinstance(tree, (list, tuple)): + for idx in range(len(tree)): + for path, v in _traverse_with_names(tree[idx], with_inner_nodes): + yield (str(idx) + "/" + path).rstrip("/"), v + if with_inner_nodes: + yield "", tree + else: + yield "", tree + + +def tree_flatten_with_names(tree): + """Populates tree_flatten with leaf names. + + This function populates output of tree_flatten with leaf names, using a + custom traversal that produces names is provided. The custom traversal does + NOT have to traverse tree in the same order as jax, as we take care of + automatically aligning jax' and custom traversals. + + Args: + tree: python tree. + + Returns: + A list of values with names: [(name, value), ...] + """ + vals, tree_def = jax.tree.flatten(tree) + + # "Fake" token tree that is use to track jax internal tree traversal and + # adjust our custom tree traversal to be compatible with it. + tokens = range(len(vals)) + token_tree = tree_def.unflatten(tokens) + val_names, perm = zip(*_traverse_with_names(token_tree)) + inv_perm = np.argsort(perm) + + # Custom traverasal should visit the same number of leaves. + assert len(val_names) == len(vals) + + return [(val_names[i], v) for i, v in zip(inv_perm, vals)], tree_def + + +def tree_unflatten(names_and_vals): + """Reverses `tree_flatten_with_names(tree)[0]`.""" + return recover_tree(*zip(*names_and_vals)) + + +def tree_map_with_names(f, tree, *rest): + """Like jax.tree.map but with a filter on the leaf path name. + + Args: + f: A function with first parameter `name` (path-like "a/b/c") and remaining + parameters values of `tree` and `*rest` corresponding to the given `name` + Should return a new value for parameter `name`. + tree: The tree of parameters `f` should be applied to. + *rest: more trees of the exact same structure. + + Returns: + A tree identical in structure to `tree` and `*rest` but with the leaves the + result of calling `f` on corresponding name/leaves in `tree` and `*rest`. + """ + names_and_vals, tree_def = tree_flatten_with_names(tree) + names, vals = zip(*names_and_vals) + rest_vals = [list(zip(*tree_flatten_with_names(t)[0]))[1] for t in rest] + vals = [f(*name_and_vals) for name_and_vals in zip(names, vals, *rest_vals)] + return tree_def.unflatten(vals) + + +def tree_map_with_regex(f, tree, regex_rules, not_f=lambda x: x, name=None): + """Apply jax-style tree_map based on regex rules. + + Args: + f: a function that is being applied to every variable. + tree: jax tree of arrays. + regex_rules: a list of tuples `(pattern, args)`, where `pattern` is a regex + which used for variable matching and `args` are positional arguments + passed to `f`. If some variable is not matched, we apply `not_f` transform + which is id by default. If multiple patterns match, then only the first + rule is applied. + not_f: optional function which is applied to variables that do not match any + pattern. + name: a name of transform for logging purposes. + + Returns: + a tree, transformed by `f` according to the given rules. + """ + def _f(vname, v): + for pattern, arg in regex_rules: + if re.fullmatch(pattern, vname): + if name and jax.process_index() == 0: + logging.info("Applying %s to %s with %s due to `%s`", + name, vname, arg, pattern) + return f(v, arg) + return not_f(v) + return tree_map_with_names(_f, tree) + + +def tree_get(tree, name): + """Get an entry of pytree by flattened key name, eg a/b/c, with nice error. + + Args: + tree: the pytree to be queried. + name: the path to extract from the tree, see below for examples. + + Returns: + A few examples: + tree = {'a': 1, 'b': {'c': 2, 'd': 3}} + tree_get(tree, 'a') == 1 + tree_get(tree, 'b/c') == 2 + tree_get(tree, 'b') == {'c': 2, 'd': 3} + """ + flattened = dict(_traverse_with_names(tree, with_inner_nodes=True)) + try: + return flattened[name] + except KeyError as e: + class Msg(str): # Reason: https://stackoverflow.com/a/70114007/2366315 + def __repr__(self): + return str(self) + msg = "\n".join([name, "Available keys:", *flattened, ""]) + # Turn into configdict to use its "did you mean?" error message! + msg = mlc.ConfigDict(flattened)._generate_did_you_mean_message(name, msg) # pylint: disable=protected-access + raise KeyError(Msg(msg)) from e + + +def tree_replace(tree, replacements): + """Renames/removes (nested) keys. + + Example usage: + + tree = {'a': {'b': 2, 'c': 3}, 'c': 4} + replacements = { + 'a/b': 'a/b/x', # replaces 'a/b' with 'a/b/x' + '.*c': 'C', # replaces 'c' with 'C' ('a/c' is removed) + 'C': 'D', # replaces 'C' (which was 'c') with 'D' + '.*/c': None, # removes 'a/c' + } + tree2 = rename_remove(tree, replacements) + assert tree2 == {'D': 4, 'a': {'b': {'x': 2}}} + + Args: + tree: A nested dictionary. + replacements: Rules specifying `regex` as keys and `replacement` as values + to be used with `m = re.match(regex, key)` and `m.expand(replacement)` + for every `key` independently. + + Note that: + 1. If any rule matches with `replacement=None`, then the key is removed. + 2. The rules are applied in order. It's possible to have multiple + transformations on a single key. + + Returns: + Updated `tree` according to rules defined in `replacements`. + """ + replacements = { + re.compile(kk): vv for kk, vv in replacements.items() + } + + def rename(k): + for kk, vv in replacements.items(): + m = kk.match(k) + if m: + k = k[:m.start()] + m.expand(vv) + k[m.end():] + return k + + def should_remove(k): + return any(vv is None and kk.match(k) for kk, vv in replacements.items()) + + names_and_vals, _ = tree_flatten_with_names(tree) + names_and_vals = [ + (rename(k), v) for k, v in names_and_vals if not should_remove(k) + ] + return tree_unflatten(names_and_vals) + + +def tree_compare(tree1, tree2): + """Returns `(tree1_only, tree2_only, dtype_shape_mismatch)`.""" + tree1 = flax.traverse_util.flatten_dict(tree1, sep="/") + tree2 = flax.traverse_util.flatten_dict(tree2, sep="/") + return set(tree1) - set(tree2), set(tree2) - set(tree1), { + k: [(v.dtype, v.shape), (tree2[k].dtype, tree2[k].shape)] + for k, v in tree1.items() + if k in tree2 and (v.dtype != tree2[k].dtype or v.shape != tree2[k].shape) + } + + +def tree_filter(tree, mask): + """Returns nested dict structure with only a subset of children.""" + # TODO: The code below only works for nested-dict and only when they + # have same structure. Consider relax this. + if not isinstance(tree, dict): + assert isinstance(mask, bool), f"Mask leaves must be boolean! {mask}" + return tree + assert sorted(tree.keys()) == sorted(mask.keys()), ( + f"Keys in tree and mask are not equal! {tree.keys()} != {mask.keys()}") + return {k: tree_filter(v, mask[k]) for k, v in tree.items() + if mask[k] is not False} + + +def recover_dtype(a): + """Numpy's `save` stores bfloat16 type as "void" type, so we recover it.""" + if hasattr(a, "dtype") and a.dtype.type is np.void: + assert a.itemsize == 2, "Unknown dtype!" + return a.view(jax.numpy.bfloat16) + else: + return a + + +def recover_tree(keys, values): + """Recovers a tree as a nested dict from flat names and values. + + This function is useful to analyze checkpoints that are saved by our programs + without need to access the exact source code of the experiment. In particular, + it can be used to extract an reuse various subtrees of the scheckpoint, e.g. + subtree of parameters. + + Args: + keys: a list of keys, where '/' is used as separator between nodes. + values: a list of leaf values. + + Returns: + A nested tree-like dict. + """ + tree = {} + sub_trees = collections.defaultdict(list) + for k, v in zip(keys, values): + if "/" not in k: + tree[k] = v + else: + k_left, k_right = k.split("/", 1) + sub_trees[k_left].append((k_right, v)) + for k, kv_pairs in sub_trees.items(): + k_subtree, v_subtree = zip(*kv_pairs) + tree[k] = recover_tree(k_subtree, v_subtree) + return tree + + +def tssave(mngr, pytree, path, on_commit=lambda *_, **__: None): + """Save pytree using jax tensorstore-based checkpoint manager. + + NOTE: When overwriting an existing checkpoint with a different pytree, the + result is, counterintuitively, the union of both, not only the new one. + + Args: + mngr: An instance of GlobalAsyncCheckpointManager. + pytree: What to store; any pytree of arrays. + path: Where to save the pytree. Creates subfolders as needed. + on_commit: A callback when writing is done, see `mngr.serialize`. + """ + names, vals = zip(*tree_flatten_with_names(pytree)[0]) + + for name in names: + if "~" in name: + raise ValueError(f"Symbol '~' is not allowed in names. Found in {name}.") + + gfile.makedirs(path) + with jax.transfer_guard("allow"): + names = [name.replace("/", "~") for name in names] + mngr.serialize_with_paths( + list(vals), [os.path.join(path, name) for name in names], + on_commit_callback=functools.partial(on_commit, array_names=names)) + + +def save_checkpoint_ts(mngr, checkpoint, path, step, keep=True): + """Preemption-safe saving of checkpoints using tssave.""" + # The tensorstore checkpoint format is a folder with (potentially) many files. + # On some file-systems, operations on these (copy, rename, delete) are slow, + # so we implement a flow that's both robust to pre-emptions/crashes during + # checkpointing and makes minimal use of these slow operations. + + # The logic goes as follows. It's infaillible :) + # (...if file move is atomic, which it is.) + # We always write the current checkpoint to a new folder, which contains the + # step number in its name. If we don't need to keep it indefinitely, we append + # "-tmp" to its name. + # After writing the next checkpoint, we remove the previous one if it had + # "-tmp" in its name. + # We also have a -LAST file that contains a pointer to the latest complete + # checkpoint. File operations are cheap to make atomic, that's why. + + def _on_commit_callback(array_names): # Runs after writing ckpt is done. + with gfile.GFile(f"{path}-CUR", "w") as f: + f.write(curr) + + last = "" + if gfile.exists(f"{path}-LAST"): + with gfile.GFile(f"{path}-LAST", "r") as f: + last = f.read().strip() + + gfile.rename(f"{path}-CUR", f"{path}-LAST", overwrite=True) + + if last.endswith("-tmp"): + # If pre-emption happens here, some old checkpoints may not be deleted. + multiprocessing.pool.ThreadPool().map( + gfile.rmtree, + [f"{path}-{last}/{name}" for name in array_names]) + gfile.rmtree(f"{path}-{last}") + + # NOTE: The jax checkpoint manager automatically waits for the previous save + # to be finished before writing again, so we don't need to do it here. + + # Always write to path with step number in it. + curr = f"{step:09d}{'-tmp' if not keep else ''}" + tssave(mngr, checkpoint, f"{path}-{curr}", _on_commit_callback) + + +def load_checkpoint_ts(path, **tsload_kw): + """Loads a big_vision checkpoint saved by `save_checkpoint_ts`.""" + to_load = path + + try: + # When passing a general path (not a specific step), get the last available. + with gfile.GFile(f"{path}-LAST", "r") as f: + to_load = f"{path}-{f.read().strip()}" + except Exception: # Differs based on backend, so blanket catch. pylint:disable=broad-exception-caught + pass + + return tsload(to_load, **tsload_kw) + + +def tsload(path, *, tree=None, shardings=None, regex=None): + """Loads tensorstore-based array-tree from disk. + + If `tree` argument is provided, then array names to load and target structure + is derived from the tree. If `tree` is None, then array names to load are + derived from array filenames on the disk, and, optionally, `regex` is applied + to filter these names. The`tree` argument is then automatically derived from + array names with `recover_tree` util. + + Arrays are loaded to CPU/TPU/GPU memory as specified by the `shardings` + argument, which is a pytree of CPU/TPU/GPU shardings (can be mixed within a + single pytree). `shardings` should a prefix tree of the `tree` argument. We + automatically broadcast `shardings` to a full `tree`. For example, a user can + specify `shardings=jax.sharding.SingleDeviceSharing(jax.devices('cpu')[0])`, + which will be broadcasted to a full tree. + + Args: + path: a directory where the checkpoint arrays are stored. + tree: a target pytree, which defines array names to load and the target tree + structure. If tree is None, then `tree` is inferred from the names of + arrays stored on the disk. + shardings: a prefix pytree (with respect to `tree`) of the target shardings. + regex: regex to filter array names from the disk, if `tree` is not provided. + + Returns: + A pytree of loaded arrays that has the same structure as `shardings` arg. + """ + if (tree is not None) and (regex is not None): + raise ValueError("If tree is specified, regex filtering is not allowed.") + + if tree is None: + # Some file-systems (gs://) list folders with a trailing /, get rid of it. + path_names = set([p.rstrip("/").replace("~", "/") + for p in gfile.listdir(path)]) + regex = re.compile(regex) if regex is not None else re.compile(".*") + path_names = [p for p in path_names if regex.match(p)] + tree = recover_tree(path_names, [0] * len(path_names)) + + names_and_vals, tree_def = tree_flatten_with_names(tree) + names_to_load, _ = zip(*names_and_vals) + + if shardings is None: + shardings = jax.sharding.SingleDeviceSharding( + jax.local_devices(backend="cpu")[0] + ) + shardings = list(jax.tree.leaves(tree_broadcast(shardings, tree))) + + names_to_load = [os.path.join(path, name.replace("/", "~")) + for name in names_to_load] + specs = [array_serial.get_tensorstore_spec(n) for n in names_to_load] + arrays = array_serial.run_deserialization(shardings, specs) + return tree_def.unflatten(arrays) + + +def steps(prefix, config, data_size=None, batch_size=None, total_steps=None, + default=ValueError): + """Gets duration named `prefix` out of `config` and converts it to steps. + + Using this function to access a configuration value that denotes some kind + of duration (eg training time, warmup, checkpoint frequency, ...) allows the + duration to be specified in terms of steps, epochs, examples, or percent of + training time, and converts any of these into steps, such that the training + code only deals with steps. + If the result is not an integer step number, it is rounded to the nearest one. + + Args: + prefix: The name of the duration to query. The actual config fields can + then be one of `prefix_steps`, `prefix_examples`, or `prefix_epochs`. + config: The dictionary (config) from which to read the duration. + data_size: The total number of training examples in one epoch. + batch_size: The number of examples processed per step. + total_steps: The total number of training steps to run. + default: The default value to return when no duration of the name `prefix` + is found in the `config`. Set to `ValueError` (the default) to raise an + error instead of returning a default value. + + Returns: + The number of steps from the config, or the default value. + + Raises: + ValueError if there is no such duration in the config and no default is set. + """ + # Be helpful and make sure only match one of the following suffixes. + suffixes = {"steps", "examples", "epochs", "percent"} + matches = {f"{prefix}_{s}" for s in suffixes if f"{prefix}_{s}" in config + and config[f"{prefix}_{s}"] is not None} + # Note that steps=0 is also a valid value (e.g. to only run evaluators). + assert len(matches) <= 1, f"Only one of '{matches}' should be defined." + + if f"{prefix}_steps" in config: + return config[f"{prefix}_steps"] + + def to_integer(x): + # Round to nearest but always executed at least one step unless explictily + # asked for 0. E.g. total_epochs=0 vs total_epochs=0.0001 + return max(1, round(x)) if x else 0 + + if batch_size and f"{prefix}_examples" in config: + return to_integer(config[f"{prefix}_examples"] / batch_size) + + if batch_size and data_size and f"{prefix}_epochs" in config: + steps_per_epoch = data_size / batch_size + return to_integer(config[f"{prefix}_epochs"] * steps_per_epoch) + + if total_steps and f"{prefix}_percent" in config: + pct = config[f"{prefix}_percent"] + assert 0.0 <= pct <= 1.0, ( # Be helpful, since it's not obvious. + f"Percents should lie in [0.0, 1.0], but {prefix}_percent is {pct}") + return to_integer(pct * total_steps) + + if default is ValueError: + raise ValueError( + f"Cannot convert {prefix} to steps, due to missing batch_size " + f"({batch_size}), data_size ({data_size}), total_steps ({total_steps})" + ", or corresponding entry in config:\n" + "\n".join(config.keys())) + + return default + + +def create_learning_rate_schedule( + total_steps, batch_size=None, data_size=None, + base=1.0, decay_type="stair", + scale_with_batchsize=False, **kw): + """Creates learning rate schedule, see (internal link). + + Args: + total_steps: The total number of steps to run. + batch_size: The global batch-size optionally used for scaling. + data_size: Number of examples in the training data (for epoch conversion). + base: The starting learning-rate (without warmup). + decay_type: 'linear' or 'cosine', 'rsqrt', 'stair'. + scale_with_batchsize: Whether or not to scale lr automatically. + **kw: extra arguments specific to individual decay_types. Also contains + declaration of `{warmup,cooldown}_{steps,epochs,examples}` that applies + on top of any/all decay_type. + + Returns: + A function learning_rate(step): float -> {"learning_rate": float}. + """ + + warmup_steps = steps( + "warmup", kw, data_size, batch_size, total_steps, default=0) + cooldown_steps = steps( + "cooldown", kw, data_size, batch_size, total_steps, default=0) + + # Early catch hard to backtrack errors due to warmup_steps >= total_steps, + # but let it run for 0 and 1 steps used to eval and debug runs. + assert (total_steps <= 1) or (warmup_steps < total_steps), ( + "warmup_steps is >= total_steps") + + def step_fn(step): + """Step to learning rate function.""" + lr = base + + # This implements the linear scaling rule following + # Goyal et al. at arxiv.org/abs/1706.02677. + # The reference batch size in literature is 256, so we scale the lr to + # adjust to the literature lr when bach_size changes. + if scale_with_batchsize: + lr = lr * batch_size / 256.0 + + progress = (step - warmup_steps) / float(total_steps - warmup_steps) + progress = jnp.clip(progress, 0.0, 1.0) + if decay_type in ("linear", "polynomial"): + power = kw.get("power", 1) + zero = kw.get("end", kw.get("linear_end", 0)) + lr = zero + (lr - zero) * (1.0 - progress) ** power + elif decay_type == "cosine": + lr = lr * 0.5 * (1. + jnp.cos(jnp.pi * progress)) + elif decay_type == "rsqrt": + # See (internal link) for details, especially how to set timescale + # and shift in order to continue smoothly when changing batch-size. + if "timescale_examples" in kw: + t = kw["timescale_examples"] / batch_size + else: + t = kw.get("timescale", 10_000) # bwd-compat default. + shift = kw.get("shift", 0) + lr = jnp.where( + warmup_steps <= step, + lr / jnp.sqrt(1 + (step + shift - warmup_steps) / t), # In decay + lr / jnp.sqrt(1 + shift / t)) # In warmup. + elif decay_type == "stair": + i = jnp.searchsorted(jnp.array(kw.get("steps", [])), step + 1) + lr = lr * jnp.take(jnp.array([1.0] + list(kw.get("mults", []))), i) + else: + raise ValueError(f"Unknown lr type {decay_type}") + + if warmup_steps: + lr = lr * jnp.minimum(1., step / warmup_steps) + if cooldown_steps: + lr = lr * jnp.minimum(1., (total_steps - step) / cooldown_steps) + + return jnp.asarray(lr, dtype=jnp.float32) + + return step_fn + + +def get_mixup(rng, p): + """Perform mixup https://arxiv.org/abs/1710.09412.""" + rng, rng_mixup = jax.random.split(rng) + a = jax.random.beta(rng_mixup, p, p) + a = jnp.maximum(a, 1.0 - a) # see (internal link) for the context. + def _mixup(*things, **more_things): + mix = lambda thing: a * thing + (1 - a) * jnp.roll(thing, shift=1, axis=0) + return rng, *jax.tree.map(mix, (things, more_things)) + return _mixup + + +# For backwards compatability with legacy code. +def mixup(rng, *things, p, **more_things): + return get_mixup(rng, p)(*things, **more_things) + + +def sync(): + """Syncs hosts and empties async computation queue.""" + x = reshard(np.ones(jax.device_count()), + jax.sharding.PositionalSharding(jax.devices())) + jax.jit(jnp.sum)(x).block_until_ready() + + +def check_and_compile_patterns(patterns): + """Validates and compiles a list of param-patterns. + + The validation consists of checking for common mistakes, currently only that + the pattern does not start with a slash, because unlike FLAX, our parameter + names don't start with a slash. + + Args: + patterns: a single (string) pattern (regex), or a list of patterns. + + Returns: + A list of compiled and verified regexes. + """ + if isinstance(patterns, str): + patterns = [patterns] + + assert isinstance(patterns, (list, tuple)), patterns + + def check_and_compile(pattern): + assert not pattern.startswith("/"), ( + f"Big vision parameter names never start with '/': '{pattern}") + return re.compile(pattern) + + return list(map(check_and_compile, patterns)) + + +def make_mask_trees(tree, patterns, *, log=None): + """Returns a boolean mask tree for every pattern (only first match).""" + compiled_patterns = check_and_compile_patterns(patterns) + + def matchfirst(name, _): + matches = [] + for pattern in compiled_patterns: + matches.append(not any(matches) and bool(pattern.fullmatch(name))) + if log is not None and True in matches and jax.process_index() == 0: + logging.info("%s: %s - matched by %s", log, name, + patterns[matches.index(True)]) + return np.array(matches) + + multimask = tree_map_with_names(matchfirst, tree) + return [ + jax.tree.map(lambda matches, i=idx: matches[i], multimask) + for idx in range(len(patterns)) + ] + + +@contextlib.contextmanager +def profile(name, ttl=3 * 365 * 24 * 3600, noop=False): + if not noop: + sess = startstop_prof_at_steps(None, name=name, ttl=ttl) + yield + if not noop: + startstop_prof_at_steps(sess, name=name, ttl=ttl) + + +def startstop_prof(sess, step=None, first_step=0, + log_steps=1, surround=20, **kw): + """Runs the profiler for `surround` steps around the next `log_steps`.""" + first_log = first_step + log_steps - (first_step % log_steps) + # don't start before first! + start = max(first_log - surround//2, first_step + 1) + return startstop_prof_at_steps(sess, step, start, start + surround, **kw) + + +def startstop_prof_at_steps( + sess, step=None, first_step=None, last_step=None, + name="steps", ttl=3 * 365 * 24 * 3600): + del sess, step, first_step, last_step, name, ttl + pass # TODO: implement using `jax.profiler` API. Needs workdir. + + +# This is a very minimal variant for open-sourcing. Our internal code makes use +# of multiple internal logging tools instead. +class BigVisionMetricWriter: + """A class for logging metrics.""" + + def __init__(self, xid=-1, wid=-1, workdir=None, config=None): + self.step_start(0) + if jax.process_index() != 0: return # Only one host shall write stuff. + + self.pool = multiprocessing.pool.ThreadPool(1) # 1 is important here. + self.fname = None + if workdir: + if xid != -1 and wid != -1: + self.fname = os.path.join(workdir, + f"big_vision_{xid}_{wid}_metrics.txt") + else: + self.fname = os.path.join(workdir, "big_vision_metrics.txt") + if config: + with gfile.GFile(os.path.join(workdir, "config.json"), "w") as f: + f.write(config.to_json()) + + def step_start(self, step): + self.step = step + self.step_metrics = {} + + def measure(self, name, value): + """Logs the metric value.""" + if jax.process_index() != 0: return # Only one host shall write stuff. + + # Convenience for accepting scalar np/DeviceArrays, as well as N-d single + # scalars, like [[[123]]] or similar, avoiding silly mistakes. + value = np.array(value).squeeze() + + # If the value is a scalar, we keep it in mind to append a line to the logs. + # If it has any structure, we instead just log its shape. + value = float(value) if value.ndim == 0 else value.shape + + logging.info(f"\u001b[35m[{self.step}]\u001b[0m {name} = {value}") + logging.flush() + self.step_metrics[name] = value + + return value # Just for convenience + + def step_end(self): + """Ends a training step, write its full row.""" + if not self.step_metrics: return + + def write(metrics): + with gfile.GFile(self.fname, "a") as f: + f.write(json.dumps({"step": self.step, **metrics}) + "\n") + + if self.fname: + self.pool.apply(lambda: None) # Potentially wait for past writes. + self.pool.apply_async(write, (self.step_metrics,)) + + def close(self): + self.step_end() + if jax.process_index() == 0: + self.pool.close() + self.pool.join() + + +def maybe_cleanup_workdir(workdir, cleanup, info): + """Potentially removes workdirs at end of run for cleanup.""" + if not workdir: + return + + if not cleanup: + info("Logs/checkpoints are in %s", workdir) + elif jax.process_index() == 0: + gfile.rmtree(workdir) + try: # Only need this on the last work-unit, if already empty. + gfile.remove(os.path.join(workdir, "..")) + except tf.errors.OpError: + pass + + +def tree_broadcast(prefix, target): + """Broadcasts a prefix tree to a full tree. + + Input-output examples: + 1. prefix: {"x": 10, "y": 20} + target: {"x": {"a": 1, "b": 2}, "y": 3} + + Result: {"x": {"a": 10, "b": 10}, "y": 20} + + 2. prefix: 100 + target: {"x": {"a": 1, "b": 2}, "y": 3} + + Result: {"x": {"a": 100, "b": 100}, "y": 100} + + 3. prefix: {"x": 10} + target: {"x": {"a": 1, "b": 2}, "y": 3} + + Result: ValueError + + Args: + prefix: prefix pytree. + target: boradcast target for a prefix tree. + + Returns: + prefix tree broadcasted to a target tree. + """ + def _broadcast(leaf, subtree): + return jax.tree.map(lambda _: leaf, subtree) + return jax.tree.map(_broadcast, prefix, target) + + +def reshard(tree, shardings): + """Take an arbitrarily* sharded pytree and shard it according to `shardings`. + + This is a no-op for tree elements which are already sharded as requested. + + *Arrays that are fully addressable (for example, CPU arrays) are assumed to be + identical (i.e. replicated) across hosts. + + *It does not work if an element of `tree` is not fully-addressable, unless its + sharding is already consistent with the target sharding. + If this is needed, please ping lbeyer@ or akolesnikov@. + + Args: + tree: a pytree of arrays. + shardings: a (prefix) pytree of jax array shardings. + Returns: + A pytree of global jax arrays that follows provided shardings. + """ + def _make_global_arr(x, shard, shape): + # Avoid unnecessary copies and transfers: + if hasattr(x, "sharding") and x.sharding.is_equivalent_to(shard, len(shape)): # pylint: disable=line-too-long + return x + if not getattr(x, "is_fully_addressable", True): + raise RuntimeError("Trying to reshard a non-fully-addressable array. " + "Please see the doc-comment for detailed explanation.") + x = jax.device_get(x) # Might be on local devices. + xs = [jax.device_put(x[s], device=d) + for d, s in shard.addressable_devices_indices_map(shape).items()] + return jax.make_array_from_single_device_arrays(shape, shard, xs) + + shapes = jax.tree.map(np.shape, tree) + shardings = tree_broadcast(shardings, tree) + return jax.tree.map(_make_global_arr, tree, shardings, shapes) + + +def put_cpu(x): + """Places array/pytree on a CPU device.""" + return jax.device_put(x, jax.local_devices(backend="cpu")[0]) + + +def make_fsarray_from_local_slice(local_slice, global_devices): + """Create a fully-sharded global device array from local host arrays. + + Args: + local_slice: Something convertible to a numpy array (eg also TF tensors) + that is this host's slice of the global array. + global_devices: The list of global devices. Needed for consistent ordering. + + Returns: + The global on-device array which consists of all local slices stacked + together in the order consistent with the devices. + """ + mesh = jax.sharding.Mesh(global_devices, ("devices",)) + sharding = jax.sharding.NamedSharding( + mesh, jax.sharding.PartitionSpec("devices")) + local_ds = mesh.local_devices + + x = np.asarray(memoryview(local_slice)) # No-copy: http://(internal link) + xs = jax.device_put(np.split(x, len(local_ds), axis=0), local_ds) + + global_shape = (x.shape[0] * jax.process_count(), *x.shape[1:]) + return jax.make_array_from_single_device_arrays(global_shape, sharding, xs) + + +def get_local_slice_from_fsarray(global_array): + """Return numpy array for the host-local slice of fully-sharded array. + + Args: + global_array: JAX array, globally sharded on devices across hosts. + + Returns: + NumPy array that holds the part of `global_array` that is held by the + devices on the host that calls this function. + """ + # For now, for simplicity, we only implement slicing along the first axis. + for shard in global_array.addressable_shards: + assert all(idx == slice(None) for idx in shard.index[1:]), ( + f"global_array is sharded along non-first dimensions:\n{shard.index}") + + # Get the shards back in the same order in which the global array was created + # in the first place. This makes sure it's consistent with other things in the + # batch, for example (assuming the whole batch is consistent). + m = {s.device: s for s in global_array.addressable_shards} + local_shards = [m[d] for d in global_array.sharding.mesh.local_devices] + return np.concatenate([jax.device_get(s.data) for s in local_shards], axis=0) + + +def assert_local_slices_same(*global_arrays): + """Check whether all `global_arrays` have local slices at the same indices.""" + slices = [ + tuple( + tuple((idx.start, idx.end, idx.step) for idx in s.index) + for s in a.addressable_shards) + for a in global_arrays] + assert len(set(slices)) == 1, f"Not all slices are the same: {slices}" + + +# TODO: remove this logic when the +# issue is github fixed https://github.com/google/jax/issues/15600. +def jit_cpu(**extra_kwargs): + def _decorator(fun): + def _wrapped(*args, **kwargs): + sh = jax.sharding.SingleDeviceSharding( + jax.local_devices(backend="cpu")[0] + ) + return jax.jit(fun, **extra_kwargs, out_shardings=sh)(*args, **kwargs) + return _wrapped + return _decorator diff --git a/big_vision/utils_test.py b/big_vision/utils_test.py new file mode 100644 index 0000000000000000000000000000000000000000..571a0d01d8de655021a30cc282f5f50d7bb2a9c0 --- /dev/null +++ b/big_vision/utils_test.py @@ -0,0 +1,357 @@ +# Copyright 2024 Big Vision 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. + +"""Tests for utils.""" + +from functools import partial +import os + +from absl.testing import parameterized +from big_vision import utils +import chex +import flax +import jax +from jax.experimental.array_serialization import serialization as array_serial +import jax.numpy as jnp +import numpy as np +import tensorflow as tf + +from tensorflow.io import gfile + + +NDEV = 4 + + +def setUpModule(): + chex.set_n_cpu_devices(NDEV) + + +class PadShardUnpadTest(chex.TestCase, tf.test.TestCase): + BATCH_SIZES = [NDEV, NDEV + 1, NDEV - 1, 5 * NDEV, 5 * NDEV + 1, 5 * NDEV - 1] + DTYPES = [np.float32, np.uint8, jax.numpy.bfloat16, np.int32] + + def tearDown(self): + chex.clear_trace_counter() + super().tearDown() + + @parameterized.product(dtype=DTYPES, bs=BATCH_SIZES) + def test_basics(self, dtype, bs): + # Just tests that basic calling works without exploring caveats. + @partial(utils.pad_shard_unpad, static_argnums=()) + def add(a, b): + return a + b + + x = np.arange(bs, dtype=dtype) + y = add(x, 10*x) + chex.assert_type(y.dtype, x.dtype) + np.testing.assert_allclose(np.float64(y), np.float64(x + 10*x)) + + @parameterized.parameters(DTYPES) + def test_min_device_batch_avoids_recompile(self, dtype): + @partial(utils.pad_shard_unpad, static_argnums=()) + @jax.jit + @chex.assert_max_traces(n=1) + def add(a, b): + return a + b + + chex.clear_trace_counter() + + for bs in self.BATCH_SIZES: + x = np.arange(bs, dtype=dtype) + y = add(x, 10*x, min_device_batch=9) # pylint: disable=unexpected-keyword-arg + chex.assert_type(y.dtype, x.dtype) + np.testing.assert_allclose(np.float64(y), np.float64(x + 10*x)) + + @parameterized.product(dtype=DTYPES, bs=BATCH_SIZES) + def test_static_argnum(self, dtype, bs): + @partial(utils.pad_shard_unpad, static_argnums=(1,)) + def add(a, b): + return a + b + + x = np.arange(bs, dtype=dtype) + y = add(x, 10) + chex.assert_type(y.dtype, x.dtype) + np.testing.assert_allclose(np.float64(y), np.float64(x + 10)) + + @parameterized.product(dtype=DTYPES, bs=BATCH_SIZES) + def test_static_argnames(self, dtype, bs): + # In this test, leave static_argnums at the default value too, in order to + # test the default/most canonical path where `params` are the first arg. + @partial(utils.pad_shard_unpad, static_argnames=('b',)) + def add(params, a, *, b): + return params * a + b + + x = np.arange(bs, dtype=dtype) + y = add(5, x, b=10) + chex.assert_type(y.dtype, x.dtype) + np.testing.assert_allclose(np.float64(y), np.float64(5 * x + 10)) + + +class TreeTest(tf.test.TestCase): + + def setUp(self): + super().setUp() + + self.d1 = {'w1': 1, 'w2': 2, 'w34': (3, 4)} + self.d1_flat = [1, 2] + self.d1_flat_jax = jax.tree.flatten(self.d1)[0] + self.d1_named_flat = [('w1', 1), ('w2', 2), ('w34/0', 3), ('w34/1', 4)] + self.d1_named_flat_jax = [('w1', 1), ('w2', 2), ('w34/0', 3), ('w34/1', 4)] + + self.d2 = {'conv1': {'kernel': 0, 'bias': 1}, + 'conv2': {'kernel': 2, 'bias': 3}} + self.d2_flat = [1, 0, 3, 2] + self.d2_flat_jax = jax.tree.flatten(self.d2)[0] + self.d2_named_flat = [('conv1/bias', 1), ('conv1/kernel', 0), + ('conv2/bias', 3), ('conv2/kernel', 2)] + self.d2_named_flat_jax = [('conv1/bias', 1), ('conv1/kernel', 0), + ('conv2/bias', 3), ('conv2/kernel', 2)] + self.d2_named_flat_inner = [ + ('conv1/bias', 1), ('conv1/kernel', 0), ('conv1', self.d2['conv1']), + ('conv2/bias', 3), ('conv2/kernel', 2), ('conv2', self.d2['conv2']), + ('', self.d2), + ] + + # This is a very important testcase that checks whether we correctly + # recover jax' traversal order, even though our custom traversal may not + # be consistent with jax' traversal order. In particular, jax traverses + # FlaxStruct in the order of attribute definition, while our custom + # traversal is alphabetical. + @flax.struct.dataclass + class FlaxStruct(): + v3: float + v2: int + v1: str + self.d3 = {'a': 0, 'flax': FlaxStruct(2.0, 1, 's')} + self.d3_flat = [0, 1, 2.0, 's'] + self.d3_flat_jax = jax.tree.flatten(self.d3)[0] + self.d3_named_flat = [ + ('a', 0), ('flax/v1', 's'), ('flax/v2', 1), ('flax/v3', 2.0)] + self.d3_named_flat_jax = [ + ('a', 0), ('flax/v3', 2.0), ('flax/v2', 1), ('flax/v1', 's')] + + def test_traverse_with_names(self): + names_and_vals = list(utils._traverse_with_names(self.d1)) + self.assertEqual(names_and_vals, self.d1_named_flat) + + names_and_vals = list(utils._traverse_with_names(self.d2)) + self.assertEqual(names_and_vals, self.d2_named_flat) + + names_and_vals = list(utils._traverse_with_names( + self.d2, with_inner_nodes=True)) + self.assertEqual(names_and_vals, self.d2_named_flat_inner) + + names_and_vals = list(utils._traverse_with_names(self.d3)) + self.assertEqual(names_and_vals, self.d3_named_flat) + + def test_tree_flatten_with_names(self): + names_and_vals = utils.tree_flatten_with_names(self.d1)[0] + self.assertEqual(names_and_vals, self.d1_named_flat_jax) + self.assertEqual([x for _, x in names_and_vals], self.d1_flat_jax) + + names_and_vals = utils.tree_flatten_with_names(self.d2)[0] + self.assertEqual(names_and_vals, self.d2_named_flat_jax) + self.assertEqual([x for _, x in names_and_vals], self.d2_flat_jax) + + names_and_vals = utils.tree_flatten_with_names(self.d3)[0] + self.assertEqual(names_and_vals, self.d3_named_flat_jax) + self.assertEqual([x for _, x in names_and_vals], self.d3_flat_jax) + + def test_tree_map_with_names(self): + d1 = utils.tree_map_with_names( + lambda name, x: -x if 'w2' in name else x, self.d1) + self.assertEqual(d1, {'w1': 1, 'w2': -2, 'w34': (3, 4)}) + + d1 = utils.tree_map_with_names( + lambda name, x1, x2: x1 + x2 if 'w2' in name else x1, self.d1, self.d1) + self.assertEqual(d1, {'w1': 1, 'w2': 4, 'w34': (3, 4)}) + + def test_recover_tree(self): + keys = ['a/b', 'a/c/x', 'a/c/y', 'd'] + values = [0, 1, 2, 3] + self.assertEqual(utils.recover_tree(keys, values), + {'a': {'b': 0, 'c': {'x': 1, 'y': 2}}, 'd': 3}) + + def test_make_mask_trees(self): + F, T = False, True # pylint: disable=invalid-name + tree = {'a': {'b': 0, 'x': 1}, 'b': {'x': 2, 'y': 3}} + msk1 = {'a': {'b': F, 'x': T}, 'b': {'x': T, 'y': F}} + msk2 = {'a': {'b': F, 'x': F}, 'b': {'x': F, 'y': T}} + # Note that 'b' matches '^b' only and not '.*/b'. + # Also note that "b/x" is matched by rule 1 only (because it comes first). + self.assertEqual( + utils.make_mask_trees(tree, ('.*/x', 'b/.*')), [msk1, msk2]) + + def test_tree_get(self): + tree = {'a': {'b': 0, 'x': 1}, 'b': {'x': 2, 'y': 3}} + self.assertEqual(utils.tree_get(tree, 'a/b'), 0) + self.assertEqual(utils.tree_get(tree, 'a/x'), 1) + self.assertEqual(utils.tree_get(tree, 'b/x'), 2) + self.assertEqual(utils.tree_get(tree, 'b/y'), 3) + self.assertEqual(utils.tree_get(tree, 'a'), tree['a']) + self.assertEqual(utils.tree_get(tree, 'b'), tree['b']) + + def test_tree_replace(self): + tree = {'a': {'b': 2, 'c': 3}, 'c': 4} + replacements = { + 'a/b': 'a/b/x', # replaces 'a/b' with 'a/b/x' + '.*c': 'C', # replaces 'c' with 'C' ('a/c' is removed) + 'C': 'D', # replaces 'C' (which was 'c') with 'D' + '.*/c': None, # removes 'a/c' + } + tree2 = utils.tree_replace(tree, replacements) + self.assertEqual(tree2, {'D': 4, 'a': {'b': {'x': 2}}}) + + def test_tree_compare(self): + tree1_only, tree2_only, dtype_shape_mismatch = utils.tree_compare( + {'a': {'b': jnp.array(2), 'c': jnp.array(3)}}, + {'a': {'B': jnp.array(2), 'c': jnp.array(3.)}}, + ) + self.assertEqual(tree1_only, {'a/b'}) + self.assertEqual(tree2_only, {'a/B'}) + self.assertEqual( + dtype_shape_mismatch, + {'a/c': [(jnp.dtype('int32'), ()), (jnp.dtype('float32'), ())]}) + + +class StepConversionTest(parameterized.TestCase, tf.test.TestCase): + + @parameterized.named_parameters( + ('nice_steps', 1000, None, None, dict(foo_steps=3), 3), + ('nice_epochs', 1000, 100, None, dict(foo_epochs=3), 30), + ('nice_examples', None, 100, None, dict(foo_examples=300), 3), + ('nice_percent', None, None, 10, dict(foo_percent=0.30), 3), + ('offbyone_steps', 1001, None, None, dict(foo_steps=3), 3), + ('offbyone_epochs', 1001, 100, None, dict(foo_epochs=3), 30), + ('offbyone_examples', None, 101, None, dict(foo_examples=300), 3), + ('offbyone_percent', None, None, 11, dict(foo_percent=0.30), 3), + ) + def test_steps(self, data_size, batch_size, total, cfg, expected): + # Correct default usage: + step = utils.steps('foo', cfg, data_size=data_size, batch_size=batch_size, + total_steps=total) + self.assertEqual(step, expected) + + # Inexitent entry: + with self.assertRaises(ValueError): + step = utils.steps('bar', cfg, data_size=data_size, batch_size=batch_size, + total_steps=total) + step = utils.steps('bar', cfg, data_size=data_size, batch_size=batch_size, + total_steps=total, default=1234) + self.assertEqual(step, 1234) + + +class CreateLearningRateScheduleTest(parameterized.TestCase, tf.test.TestCase): + + @parameterized.named_parameters( + ('linear', 'linear', {}, 13, .5), + ('polynomial', 'polynomial', {'end': .1, 'power': 2}, 13, .325), + ('cosine', 'cosine', {}, 13, .5), + ('rsqrt', 'rsqrt', {'timescale': 1}, 13, 0.3333333), + ('stair_5', 'stair', {'steps': [10], 'mults': [.5]}, 5, 1.), + ('stair_10', 'stair', {'steps': [10], 'mults': [.5]}, 10, .5), + ('warmup_before', 'rsqrt', {'timescale': 1}, 3, .6), + ('cooldown_after', 'rsqrt', {'timescale': 1}, 20, .05), + ) + def test_schedule(self, decay_type, extra_kwargs, step, expected_lr): + lr_fn = utils.create_learning_rate_schedule( + total_steps=21, + batch_size=512, + base=.5, + decay_type=decay_type, + scale_with_batchsize=True, + warmup_steps=5, + cooldown_steps=5, + **extra_kwargs) + lr = lr_fn(step) + self.assertAlmostEqual(lr, expected_lr) + + +class CheckpointTest(tf.test.TestCase): + + def setup(self): + gacm = array_serial.GlobalAsyncCheckpointManager() + + save_path = os.path.join(self.create_tempdir('workdir'), 'checkpoint.bv') + x = utils.put_cpu(np.array([1, 2, 3, 4])) + y = utils.put_cpu(np.array([5, 6, 7, 8])) + ckpt = {'x': x, 'y': {'z': y}} + + sharding = jax.sharding.SingleDeviceSharding( + jax.local_devices(backend='cpu')[0] + ) + shardings = jax.tree.map(lambda _: sharding, ckpt) + + return gacm, save_path, ckpt, shardings + + def test_save_and_load(self): + gacm, save_path, ckpt, shardings = self.setup() + step = 100 + utils.save_checkpoint_ts(gacm, ckpt, save_path, step, keep=True) + gacm.wait_until_finished() + ckpt_loaded = utils.load_checkpoint_ts(save_path, + tree=ckpt, shardings=shardings) + chex.assert_trees_all_equal(ckpt_loaded, ckpt) + + save_path_step = f'{save_path}-{step:09d}' + ckpt_loaded_step = utils.tsload(save_path_step, shardings=shardings) + chex.assert_trees_all_equal(ckpt_loaded_step, ckpt) + + def test_save_and_partial_load(self): + gacm, save_path, ckpt, shardings = self.setup() + utils.save_checkpoint_ts(gacm, ckpt, save_path, step=100) + gacm.wait_until_finished() + _ = shardings.pop('x'), ckpt.pop('x') + ckpt_loaded = utils.load_checkpoint_ts(save_path, + tree=ckpt, shardings=shardings) + chex.assert_trees_all_equal(ckpt_loaded, ckpt) + + def test_save_and_cpu_load(self): + gacm, save_path, ckpt, _ = self.setup() + utils.save_checkpoint_ts(gacm, ckpt, save_path, step=100) + gacm.wait_until_finished() + ckpt_loaded = utils.load_checkpoint_ts(save_path) + chex.assert_trees_all_equal(ckpt_loaded, ckpt) + + def test_save_and_partial_cpu_load(self): + gacm, save_path, ckpt, _ = self.setup() + utils.save_checkpoint_ts(gacm, ckpt, save_path, step=100) + gacm.wait_until_finished() + ckpt.pop('y') + ckpt_loaded = utils.load_checkpoint_ts(save_path, regex='x.*') + chex.assert_trees_all_equal(ckpt_loaded, ckpt) + + def test_keep_deletes(self): + def x(tree, factor): # x as in "times" for multiplying. + return jax.tree.map(lambda a: a * factor, tree) + + gacm, save_path, ckpt, _ = self.setup() + utils.save_checkpoint_ts(gacm, ckpt, save_path, step=100, keep=False) + utils.save_checkpoint_ts(gacm, x(ckpt, 2), save_path, step=200, keep=True) + utils.save_checkpoint_ts(gacm, x(ckpt, 3), save_path, step=300, keep=False) + gacm.wait_until_finished() + ckpt_loaded_200 = utils.tsload(f'{save_path}-{200:09d}') + chex.assert_trees_all_equal(ckpt_loaded_200, x(ckpt, 2)) + ckpt_loaded_300 = utils.tsload(f'{save_path}-{300:09d}-tmp') + chex.assert_trees_all_equal(ckpt_loaded_300, x(ckpt, 3)) + ckpt_loaded_last = utils.load_checkpoint_ts(save_path) + chex.assert_trees_all_equal(ckpt_loaded_last, x(ckpt, 3)) + with self.assertRaises(Exception): # Can different types depending on fs. + _ = utils.tsload(f'{save_path}-{100:09d}') + # Test that ckpt@100 was deleted + self.assertFalse(gfile.exists(f'{save_path}-{100:09d}-tmp')) + + +if __name__ == '__main__': + tf.test.main() diff --git a/log.py b/log.py new file mode 100644 index 0000000000000000000000000000000000000000..def11cbe05a96286380e39ddac762f6dfb17122e --- /dev/null +++ b/log.py @@ -0,0 +1,234 @@ + +import os +import io +import jax +import base64 +import warnings +import functools +import numpy as np +import sentencepiece +import ml_collections +from PIL import Image +import big_vision.utils +import tensorflow as tf +import supervision as sv +import big_vision.sharding +from typing import Tuple, List, Optional +from big_vision.models.proj.paligemma import paligemma +from big_vision.trainers.proj.paligemma import predict_fns + + + +SEQLEN = 128 + +class PaliGemmaManager: + _instance = None + + def __new__(cls, *args, **kwargs): + if not cls._instance: + cls._instance = super(PaliGemmaManager, cls).__new__(cls) + return cls._instance + + def __init__(self, model, params, tokenizer): + self.model = model + self.params = params + self.tokenizer = tokenizer + self.decode_fn = None + self.decode = None + self.mesh = None + self.data_sharding = None + self.params_sharding = None + self.trainable_mask = None + + self.initialise_model() + + + def initialise_model(self): + self.decode_fn = predict_fns.get_all(self.model)['decode'] + self.decode = functools.partial(self.decode_fn, devices=jax.devices(), eos_token=self.tokenizer.eos_id()) + + def is_trainable_param(name, param): + if name.startswith("llm/layers/attn/"): return True + if name.startswith("llm/"): return False + if name.startswith("img/"): return False + raise ValueError(f"Unexpected param name {name}") + self.trainable_mask = big_vision.utils.tree_map_with_names(is_trainable_param, self.params) + + self.mesh = jax.sharding.Mesh(jax.devices(), ("data")) + + self.data_sharding = jax.sharding.NamedSharding( + self.mesh, jax.sharding.PartitionSpec("data")) + + self.params_sharding = big_vision.sharding.infer_sharding( + self.params, strategy=[('.*', 'fsdp(axis="data")')], mesh=self.mesh) + def preprocess_image(self,image, size=224): + image = np.asarray(image) + if image.ndim == 2: # Convert image without last channel into greyscale. + image = np.stack((image,)*3, axis=-1) + + image = image[..., :3] # Remove alpha layer. + assert image.shape[-1] == 3 + + image = tf.constant(image) + image = tf.image.resize(image, (size, size), method='bilinear', antialias=True) + return image.numpy() / 127.5 - 1.0 + + def preprocess_tokens(self, prefix, suffix=None, seqlen=None): + separator = "\n" + tokens = self.tokenizer.encode(prefix, add_bos=True) + self.tokenizer.encode(separator) + mask_ar = [0] * len(tokens) # 0 to use full attention for prefix. + mask_loss = [0] * len(tokens) # 0 to not use prefix tokens in the loss. + + if suffix: + suffix = self.tokenizer.encode(suffix, add_eos=True) + tokens += suffix + mask_ar += [1] * len(suffix) # 1 to use causal attention for suffix. + mask_loss += [1] * len(suffix) # 1 to use suffix tokens in the loss. + + mask_input = [1] * len(tokens) # 1 if its a token, 0 if padding. + if seqlen: + padding = [0] * max(0, seqlen - len(tokens)) + tokens = tokens[:seqlen] + padding + mask_ar = mask_ar[:seqlen] + padding + mask_loss = mask_loss[:seqlen] + padding + mask_input = mask_input[:seqlen] + padding + + return jax.tree.map(np.array, (tokens, mask_ar, mask_loss, mask_input)) + + def postprocess_tokens(self, tokens): + tokens = tokens.tolist() # np.array to list[int] + try: # Remove tokens at and after EOS if any. + eos_pos = tokens.index(self.tokenizer.eos_id()) + tokens = tokens[:eos_pos] + except ValueError: + pass + return self.tokenizer.decode(tokens) + + def split_and_keep_second_part(s): + parts = s.split('\n', 1) + if len(parts) > 1: + return parts[1] + return s + + def data_iterator(self, image_bytes, caption): + image = Image.open(io.BytesIO(image_bytes)) + image = self.preprocess_image(image) + tokens, mask_ar, _, mask_input = self.preprocess_tokens(caption, seqlen=SEQLEN) + + yield { + "image": np.asarray(image), + "text": np.asarray(tokens), + "mask_ar": np.asarray(mask_ar), + "mask_input": np.asarray(mask_input), + } + + def make_predictions(self, data_iterator, *, num_examples=None, + batch_size=4, seqlen=SEQLEN, sampler="greedy"): + outputs = [] + while True: + examples = [] + try: + for _ in range(batch_size): + examples.append(next(data_iterator)) + examples[-1]["_mask"] = np.array(True) # Indicates true example. + except StopIteration: + if len(examples) == 0: + return outputs + + + while len(examples) % batch_size: + examples.append(dict(examples[-1])) + examples[-1]["_mask"] = np.array(False) # Indicates padding example. + + + batch = jax.tree.map(lambda *x: np.stack(x), *examples) + batch = big_vision.utils.reshard(batch, self.data_sharding) + tokens = self.decode({"params": self.params}, batch=batch, + max_decode_len=seqlen, sampler=sampler) + + # Fetch model predictions to device and detokenize. + tokens, mask = jax.device_get((tokens, batch["_mask"])) + tokens = tokens[mask] # remove padding examples. + responses = [self.postprocess_tokens(t) for t in tokens] + + for example, response in zip(examples, responses): + outputs.append((example["image"], response)) + if num_examples and len(outputs) >= num_examples: + return outputs + + def process_result_to_bbox(self, image, caption, classes, w, h): + image = ((image + 1)/2 * 255).astype(np.uint8) # [-1,1] -> [0, 255] + + try: + detections = sv.Detections.from_lmm( + lmm='paligemma', + result=caption, + resolution_wh=(w, h), + classes=caption) + + xyxy = list(detections.xyxy[0]) + x1, y1, x2, y2 = xyxy[0], xyxy[1], xyxy[2], xyxy[3] #The number here could be result of 224x224 + width = x2 - x1 + height = y2 - y1 + output = [x1, y1, width, height] + except Exception as e: + print('Error detection') + print(e) + output = [0,0,0,0] + + return output + + def predict(self, image: bytes, caption: str) -> List[int]: + image_original = Image.open(io.BytesIO(image)) + original_width, original_height = image_original.size + if "detect" not in caption: + caption = f"detect {caption}" + # print("Making predictions...") + for image, response in self.make_predictions(self.data_iterator(image, caption), num_examples=1): + classes = response.replace("detect ", "") + output = self.process_result_to_bbox(image, response, classes, original_width, original_height) + + return (output, response) + + + + + + +INFERENCE_IMAGE = '3_(backup)AdityaBY_img_14.png' +INFERENCE_PROMPT = "A mother takes a picture of her daughter holding a colourful wind spinner in front of the entrance." + + + + +TOKENIZER_PATH = '/home/lyka/air/Paligemma/pali-package/pali_open_vocab_annotations_tokenizer.model' +MODEL_PATH = '/home/lyka/air/Paligemma/pali-package/pali_open_vocab_annotations_segmentation.npz' + + +model_config = ml_collections.FrozenConfigDict({ + "llm": {"vocab_size": 257_152}, + "img": {"variant": "So400m/14", "pool_type": "none", "scan": True, "dtype_mm": "float16"} +}) +model = paligemma.Model(**model_config) +tokenizer = sentencepiece.SentencePieceProcessor(TOKENIZER_PATH) + +# Load params - this can take up to 1 minute in T4 colabs. +params = paligemma.load(None, MODEL_PATH, model_config) +paligemma_manager = PaliGemmaManager(model, params, tokenizer) + +with open(INFERENCE_IMAGE, 'rb') as f: + image_bytes = f.read() + +output, response = paligemma_manager.predict(image_bytes, + INFERENCE_PROMPT) +image = Image.open(INFERENCE_IMAGE) +detections = sv.Detections.from_lmm( + lmm='paligemma', + result=response, + resolution_wh=image.size, + classes=response) + +coordinates = detections.xyxy[0] # assuming we want the first detection +x1, y1, x2, y2 = coordinates + +print('x1,y1,x2,y2:',coordinates) \ No newline at end of file diff --git a/pali_open_vocab_annotations_segmentation.npz b/pali_open_vocab_annotations_segmentation.npz new file mode 100644 index 0000000000000000000000000000000000000000..faba5abe78b9a1353be1be7c055d3c8c42f0cca2 --- /dev/null +++ b/pali_open_vocab_annotations_segmentation.npz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c4762c80454621cefbf9616fdc6f580ce991714c344ff125c54c0b1fd7e37776 +size 6186419752 diff --git a/pali_open_vocab_annotations_tokenizer.model b/pali_open_vocab_annotations_tokenizer.model new file mode 100644 index 0000000000000000000000000000000000000000..22ad048d392b4f5191be7cec47d03eb3cdb360f4 --- /dev/null +++ b/pali_open_vocab_annotations_tokenizer.model @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8986bb4f423f07f8c7f70d0dbe3526fb2316056c17bae71b1ea975e77a168fc6 +size 4264023 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..25683d98daa71b8ac8c624aa890d851a17d91ae4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,261 @@ +absl-py==2.1.0 +anyio==4.4.0 +aqtp==0.7.5 +argon2-cffi==23.1.0 +argon2-cffi-bindings==21.2.0 +array_record==0.5.1 +arrow==1.3.0 +asttokens==2.4.1 +astunparse==1.6.3 +async-lru==2.0.4 +attrs==23.2.0 +Automat==20.2.0 +Babel==2.15.0 +bcrypt==3.2.0 +beautifulsoup4==4.12.3 +bleach==6.1.0 +blinker==1.4 +certifi==2020.6.20 +cffi==1.16.0 +chardet==4.0.0 +charset-normalizer==3.3.2 +chex==0.1.86 +click==8.0.3 +cloud-init==23.3.3 +cloudpickle==3.0.0 +clu @ git+https://github.com/google/CommonLoopUtils@b64aa2985d47a9d362131f60b34600c204e51fbd +colorama==0.4.4 +comm==0.2.2 +command-not-found==0.3 +configobj==5.0.6 +constantly==15.1.0 +contextlib2==21.6.0 +contourpy==1.2.1 +cryptography==3.4.8 +cycler==0.12.1 +dbus-python==1.2.18 +debugpy==1.8.2 +decorator==5.1.1 +defusedxml==0.7.1 +distrax==0.1.5 +distro==1.7.0 +distro-info==1.1+ubuntu0.2 +dm-tree==0.1.8 +docstring_parser==0.16 +editdistance==0.8.1 +einops==0.8.0 +et-xmlfile==1.1.0 +etils==1.7.0 +exceptiongroup==1.2.2 +executing==2.0.1 +fastjsonschema==2.20.0 +filelock==3.15.4 +filetype==1.2.0 +flatbuffers==24.3.25 +flax==0.8.5 +flaxformer @ git+https://github.com/google/flaxformer@399ea3a85e9807ada653fd0de1a9de627eb0acde +fonttools==4.53.1 +fqdn==1.5.1 +fsspec==2024.6.1 +gast==0.6.0 +google-pasta==0.2.0 +grpcio==1.65.1 +h11==0.14.0 +h5py==3.11.0 +httpcore==1.0.5 +httplib2==0.20.2 +httpx==0.27.0 +huggingface-hub==0.23.5 +hyperlink==21.0.0 +idna==3.7 +immutabledict==4.2.0 +importlib-metadata==4.6.4 +importlib_resources==6.4.0 +incremental==21.3.0 +ipykernel==6.29.5 +ipython==8.26.0 +ipython_genutils==0.2.0 +isoduration==20.11.0 +jax==0.4.30 +jax-cuda12-pjrt==0.4.30 +jax-cuda12-plugin==0.4.30 +jaxlib==0.4.30 +jedi==0.19.1 +jeepney==0.7.1 +Jinja2==3.0.3 +json5==0.9.25 +jsonpatch==1.32 +jsonpointer==2.0 +jsonschema==4.23.0 +jsonschema-specifications==2023.12.1 +jupyter-events==0.10.0 +jupyter-lsp==2.2.5 +jupyter_client==8.6.2 +jupyter_core==5.7.2 +jupyter_server==2.14.2 +jupyter_server_terminals==0.5.3 +jupyterlab==4.2.4 +jupyterlab_pygments==0.3.0 +jupyterlab_server==2.27.3 +keras==3.4.1 +keyring==23.5.0 +kiwisolver==1.4.5 +launchpadlib==1.10.16 +lazr.restfulclient==0.14.4 +lazr.uri==1.0.6 +libclang==18.1.1 +Markdown==3.6 +markdown-it-py==3.0.0 +MarkupSafe==2.1.5 +matplotlib==3.9.1 +matplotlib-inline==0.1.7 +mdurl==0.1.2 +mistune==3.0.2 +ml-collections==0.1.1 +ml-dtypes==0.4.0 +more-itertools==8.10.0 +mpmath==1.3.0 +msgpack==1.0.8 +namex==0.0.8 +nbclient==0.10.0 +nbconvert==7.16.4 +nbformat==5.10.4 +nest-asyncio==1.6.0 +netifaces==0.11.0 +networkx==3.3 +notebook==7.2.1 +notebook_shim==0.2.4 +numpy==1.26.4 +nvidia-cublas-cu12==12.1.3.1 +nvidia-cuda-cupti-cu12==12.1.105 +nvidia-cuda-nvcc-cu12==12.5.82 +nvidia-cuda-nvrtc-cu12==12.1.105 +nvidia-cuda-runtime-cu12==12.1.105 +nvidia-cudnn-cu12==9.2.1.18 +nvidia-cufft-cu12==11.0.2.54 +nvidia-curand-cu12==10.3.2.106 +nvidia-cusolver-cu12==11.4.5.107 +nvidia-cusparse-cu12==12.1.0.106 +nvidia-nccl-cu12==2.20.5 +nvidia-nvjitlink-cu12==12.5.82 +nvidia-nvtx-cu12==12.1.105 +oauthlib==3.2.0 +opencv-python-headless==4.10.0.84 +openpyxl==3.1.5 +opt-einsum==3.3.0 +optax==0.2.3 +optree==0.12.1 +orbax-checkpoint==0.5.22 +overrides==7.7.0 +packaging==24.1 +pandas==2.2.2 +pandocfilters==1.5.1 +panopticapi @ git+https://github.com/akolesnikoff/panopticapi.git@a698a12deb21e4cf0f99ef0581b2c30c466bf355 +parso==0.8.4 +pexpect==4.8.0 +pillow==10.4.0 +platformdirs==4.2.2 +prometheus_client==0.20.0 +promise==2.3 +prompt_toolkit==3.0.47 +protobuf==3.20.3 +psutil==6.0.0 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pyarrow==17.0.0 +pyasn1==0.4.8 +pyasn1-modules==0.2.1 +pycocoevalcap==1.2 +pycocotools==2.0.8 +pycparser==2.22 +Pygments==2.18.0 +PyGObject==3.42.1 +PyHamcrest==2.0.2 +PyJWT==2.3.0 +pyOpenSSL==21.0.0 +pyparsing==2.4.7 +pyrsistent==0.18.1 +pyserial==3.5 +python-apt==2.4.0+ubuntu3 +python-dateutil==2.9.0.post0 +python-debian==0.1.43+ubuntu1.1 +python-dotenv==1.0.1 +python-json-logger==2.0.7 +python-magic==0.4.24 +pytz==2022.1 +PyYAML==5.4.1 +pyzmq==26.0.3 +referencing==0.35.1 +regex==2024.5.15 +requests==2.32.3 +requests-toolbelt==1.0.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rich==13.7.1 +roboflow==1.1.36 +rpds-py==0.19.1 +safetensors==0.4.3 +scipy==1.14.0 +screen-resolution-extra==0.0.0 +SecretStorage==3.3.1 +Send2Trash==1.8.3 +sentencepiece==0.2.0 +service-identity==18.1.0 +simple_parsing==0.1.5 +six==1.16.0 +sniffio==1.3.1 +sos==4.5.6 +soupsieve==2.5 +ssh-import-id==5.11 +stack-data==0.6.3 +supervision @ git+https://github.com/roboflow/supervision.git@ad2220bc1da2e018d1ce08685359eb02ab3c5bd4 +sympy==1.13.0 +systemd-python==234 +tensorboard==2.17.0 +tensorboard-data-server==0.7.2 +tensorflow==2.17.0 +tensorflow-cpu==2.17.0 +tensorflow-gan==2.1.0 +tensorflow-hub==0.16.1 +tensorflow-io-gcs-filesystem==0.37.1 +tensorflow-metadata==1.15.0 +tensorflow-probability==0.24.0 +tensorflow-text==2.17.0 +tensorstore==0.1.63 +termcolor==2.4.0 +terminado==0.18.1 +tf_keras==2.17.0 +tfds-nightly==4.9.6.dev202407220044 +tinycss2==1.3.0 +tokenizers==0.19.1 +toml==0.10.2 +tomli==2.0.1 +toolz==0.12.1 +torch==2.3.1 +torchaudio==2.3.1 +torchvision==0.18.1 +tornado==6.4.1 +tqdm==4.66.4 +traitlets==5.14.3 +transformers==4.42.4 +triton==2.3.1 +Twisted==22.1.0 +types-python-dateutil==2.9.0.20240316 +typing_extensions==4.12.2 +tzdata==2024.1 +ubuntu-advantage-tools==8001 +ubuntu-drivers-common==0.0.0 +ufw==0.36.1 +unattended-upgrades==0.1 +uri-template==1.3.0 +urllib3==2.2.2 +wadllib==1.3.6 +wcwidth==0.2.13 +webcolors==24.6.0 +webencodings==0.5.1 +websocket-client==1.8.0 +Werkzeug==3.0.3 +wrapt==1.16.0 +xkit==0.0.0 +zipp==1.0.0 +zope.interface==5.4.0