diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..a6344aac8c09253b3b630fb776ae94478aa0275b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,35 @@ +*.7z filter=lfs diff=lfs merge=lfs -text +*.arrow filter=lfs diff=lfs merge=lfs -text +*.bin filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.ckpt filter=lfs diff=lfs merge=lfs -text +*.ftz filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.h5 filter=lfs diff=lfs merge=lfs -text +*.joblib filter=lfs diff=lfs merge=lfs -text +*.lfs.* filter=lfs diff=lfs merge=lfs -text +*.mlmodel filter=lfs diff=lfs merge=lfs -text +*.model filter=lfs diff=lfs merge=lfs -text +*.msgpack filter=lfs diff=lfs merge=lfs -text +*.npy filter=lfs diff=lfs merge=lfs -text +*.npz filter=lfs diff=lfs merge=lfs -text +*.onnx filter=lfs diff=lfs merge=lfs -text +*.ot filter=lfs diff=lfs merge=lfs -text +*.parquet filter=lfs diff=lfs merge=lfs -text +*.pb filter=lfs diff=lfs merge=lfs -text +*.pickle filter=lfs diff=lfs merge=lfs -text +*.pkl filter=lfs diff=lfs merge=lfs -text +*.pt filter=lfs diff=lfs merge=lfs -text +*.pth filter=lfs diff=lfs merge=lfs -text +*.rar filter=lfs diff=lfs merge=lfs -text +*.safetensors filter=lfs diff=lfs merge=lfs -text +saved_model/**/* filter=lfs diff=lfs merge=lfs -text +*.tar.* filter=lfs diff=lfs merge=lfs -text +*.tar filter=lfs diff=lfs merge=lfs -text +*.tflite filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.wasm filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text +*tfevents* filter=lfs diff=lfs merge=lfs -text diff --git a/README.md b/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ff5057c50e306e26174d0f930994567275364d46 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +--- +title: Tipsomaly +emoji: โšก +colorFrom: gray +colorTo: green +sdk: gradio +sdk_version: 6.6.0 +app_file: app.py +pinned: false +license: mit +short_description: TIPS Over Tricks for Zero-shot Anomaly Detection +--- + +Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference diff --git a/Tipsomaly/.gitignore b/Tipsomaly/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..6b307357a8aaa5a688904a8a78d7d3719502ca89 --- /dev/null +++ b/Tipsomaly/.gitignore @@ -0,0 +1,4 @@ +__pycache__/ +*.pyc +*.jpg +*.png diff --git a/Tipsomaly/LICENSE b/Tipsomaly/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..87eaeee68e91cf59957187236f8e950c6dd8a79b --- /dev/null +++ b/Tipsomaly/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Alireza Salehi et al. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Tipsomaly/README.md b/Tipsomaly/README.md new file mode 100644 index 0000000000000000000000000000000000000000..cfb130d8f5304bd680e4557124fe6919ba9fb225 --- /dev/null +++ b/Tipsomaly/README.md @@ -0,0 +1,131 @@ +# Tipsomaly (ICASSP 2026) + +Official PyTorch implementation of [TIPS Over Tricks: Simple Prompts for Effective Zero-shot Anomaly Detection](https://arxiv.org/abs/2602.03594) โ€” a spatially-aware zero-shot anomaly detection pipeline built on the [TIPS](https://arxiv.org/abs/2410.16512) vision-language model, using decoupled prompts and local evidence injection to improve image-level and pixel-level performance. + +--- + +## Table of Contents + +- [๐Ÿ“– Introduction](#-introduction) +- [๐Ÿ“Š Results](#-results) +- [๐Ÿš€ Quickstart](#-quickstart) +- [๐Ÿ“ Checkpoints](#-checkpoints) +- [๐Ÿ”ง Setup](#-setup) +- [๐Ÿ—‚๏ธ Datasets](#๏ธ-datasets) +- [๐Ÿ› ๏ธ Training](#๏ธ-training) +- [โš–๏ธ License](#๏ธ-license) +- [๐Ÿ”— Citation](#-citation) + + +--- + +## ๐Ÿ“– Introduction +Anomaly detection identifies departures from expected behavior in safety-critical settings. When target-domain normal data are unavailable, zero-shot anomaly detection (ZSAD) leverages vision-language models (VLMs). However, CLIP's coarse image-text alignment limits both localization and detection due to (i) spatial misalignment and (ii) weak sensitivity to fine-grained anomalies; prior work compensates with complex auxiliary modules yet largely overlooks the choice of backbone. We revisit the backbone and use TIPS-a VLM trained with spatially aware objectives. While TIPS alleviates CLIP's issues, it exposes a distributional gap between global and local features. We address this with decoupled prompts-fixed for image-level detection and learnable for pixel-level localization-and by injecting local evidence into the global score. Without CLIP-specific tricks, our TIPS-based pipeline improves image-level performance by 1.1-3.9% and pixel-level by 1.5-6.9% across seven industrial datasets, delivering strong generalization with a lean architecture. + +![Architecture](imgs/Models_Architecture_page-0001.jpg) + +--- + +## ๐Ÿ“Š Results +Across 14 industrial and medical benchmarks, Tipsomaly consistently outperforms prior CLIP-based zero-shot methods while remaining lightweight. The figure below summarizes its performance across datasets. + +![Results](imgs/results-table.png) + +We compare pixel-level anomaly maps from Tipsomaly with prior CLIP-based methods (AdaCLIP and AnomalyCLIP) on industrial and medical samples. As shown in the following figure, Tipsomaly more accurately localizes anomalous regions across both domains. + +![Qualitative-results](imgs/Qualitative_results_page-0001.jpg) + +--- + +## ๐Ÿš€ Quickstart +**Quick start (recommended):** +Use our ready-to-run [Kaggle notebook](https://www.kaggle.com/code/sepehrnoey/tipsomaly-reproduction) to reproduce the results with the provided checkpoints. + +## ๐Ÿ“ Checkpoints +You can find our checkpoints trained on MVTec and VisA in the directory [workspaces](./workspaces/) or on google drive at [this link](https://drive.google.com/file/d/1yvgZYHFljFkGwTD0DYOdrZ_95ZbmjDcC/view?usp=sharing). + +--- + + + + +## ๐Ÿ”ง Setup +To setup the environment and run experiments, follow these steps. +1. Install the required dependencies with the following command. +``` +pip install -r requirements.txt +``` +2. Download your desired checkpoints of the TIPS model. For this purpose, edit and run the mentioned script to download model components. After downloading the components, place them in a desired directory. +``` +bash model/tips/checkpoints/download_checkpoints.sh +``` +``` +mkdir tips && mv tips_oss_l14_highres_distilled_text.npz tips_oss_l14_highres_distilled_vision.npz tokenizer.model tips/ +``` +3. Prepare your datasets in the format described in the [Datasets](#๏ธ-datasets) section and place them in a desired directory. +``` +mkdir data-root && mv mvtec visa data-root/ +``` +4. Now, by setting the correct paths for the model, checkpoints, and the data root path, you can edit the runnable script `reproduce.sh` to run experiments. Here is a sample: +``` +models_dir="path/to/tips" +data_root_dir="path/to/data-root" +model_version='l14h' +checkpoint_path="./workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints" + +python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset visa --epoch 2 --model_version $model_version --fixed_prompt_type industrial + +``` + +## ๐Ÿ—‚๏ธ Datasets +The datasets should be set in the following format. Each dataset, must contain a file named `meta.json` which contains information about the dataset. Common datasets for anomaly detection and instructions to generate `meta.json` files can be found in the [AdaCLIP GitHub repository](https://github.com/caoyunkang/AdaCLIP). The general expected structure of the datasets is like the following: +``` +data-root/ +โ”œโ”€โ”€ mvtec/ +โ”‚ โ”œโ”€โ”€ meta.json +โ”‚ โ”œโ”€โ”€ bottle/ +โ”‚ โ”‚ โ”œโ”€โ”€ train/ +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ good/ +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 000.png +โ”‚ โ”‚ โ”œโ”€โ”€ test/ +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ good/ +โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 000.png +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ broken_large/ +โ”‚ โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 000.png +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ broken_small/ +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 000.png +โ”‚ โ”‚ โ””โ”€โ”€ ground_truth/ +โ”‚ โ”‚ โ”œโ”€โ”€ broken_large/ +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ 000_mask.png +โ”‚ โ”‚ โ””โ”€โ”€ broken_small/ +โ”‚ โ”‚ โ””โ”€โ”€ 000_mask.png +โ”‚ +โ”œโ”€โ”€ visa/ +โ”œโ”€โ”€ mpdd/ +โ”œโ”€โ”€ .... +.... +``` + +## ๐Ÿ› ๏ธ Training +You can use our ready-to-run [Kaggle notebook](https://www.kaggle.com/code/sepehrnoey/tipsomaly-train) to train your model and test on your desired dataset. Steps to prepare the datasets and downloading the base model components can be found at the [Setup](#-setup) section. You can also find the commands to train the model and test multiple datasets in a loop in the [train_test.sh](train_test.sh) + +## ๐Ÿ”— Citation +If you find this project helpful for your research, please consider citing the following BibTeX entry. + + + + +**BibTeX:** +```bibtex +@article{salehi2026tips, + title={TIPS Over Tricks: Simple Prompts for Effective Zero-shot Anomaly Detection}, + author={Salehi, Alireza and Karami, Ehsan and Noey, Sepehr and Noey, Sahand and Yamada, Makoto and Hosseini, Reshad and Sabokrou, Mohammad}, + journal={arXiv preprint arXiv:2602.03594}, + year={2026} +} +``` + + + +## โš–๏ธ License +This project is licensed under the MIT License. Please refer to the [LICENSE](LICENSE) file for more information. diff --git a/Tipsomaly/__init__.py b/Tipsomaly/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/datasets/__init__.py b/Tipsomaly/datasets/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/datasets/dataset.py b/Tipsomaly/datasets/dataset.py new file mode 100644 index 0000000000000000000000000000000000000000..ab04d7db07355a6828789a65f75ab342d1e8dd7b --- /dev/null +++ b/Tipsomaly/datasets/dataset.py @@ -0,0 +1,115 @@ +import torch.utils.data as data +import json +import random +from PIL import Image +import numpy as np +import os + +def sample_data(meta_info, k_shot): + sampled_data = [] + complement_data = [] + + for cls_name, data_list in meta_info.items(): + nrm_smpls = [item for item in data_list if item['anomaly'] == 0] + anm_smpls = [item for item in data_list if item['anomaly'] == 1] + + if k_shot > 0: + n_samples = k_shot + n_nrm_smpls = min(int(n_samples / 2), len(nrm_smpls)) + n_anm_smpls = min(int(n_samples / 2), len(anm_smpls)) + + cls_data = [] + cls_data.extend(random.sample(nrm_smpls, n_nrm_smpls)) + cls_data.extend(random.sample(anm_smpls, n_anm_smpls)) + sampled_data.extend(cls_data) + + complement_class_data = [item for item in data_list if item not in cls_data] + complement_data.extend(complement_class_data) + + print(f'num samples for cls {cls_name}, norm: {n_nrm_smpls}, anom: {n_anm_smpls}') + + return sampled_data, complement_data + +class Dataset(data.Dataset): + def __init__(self, roots, transform, target_transform, kwargs=None): + self.roots = roots + self.transform = transform + self.target_transform = target_transform + split='test' + + meta_infos = {} + for root in roots: + with open(f'{root}/meta.json', 'r') as f: + meta_info = json.load(f) + for cls in meta_info[split]: + meta_info[split][cls] = [{**s, 'root': root} for s in meta_info[split][cls]] + + if cls in meta_infos: + meta_infos[cls].extend(meta_info[split][cls]) + meta_infos[cls].extend(meta_info[split][cls]) + else: + meta_infos[cls] = meta_info[split][cls] + + meta_info_classes = list(meta_infos.keys()) + + self.selected_class = kwargs.class_name + self.cls_names = meta_info_classes if self.selected_class == ['all'] else self.selected_class + + self.data_all = [] + for cls_name in self.cls_names: + self.data_all.extend(meta_infos[cls_name]) + + self.dataset_name = kwargs.dataset + self.class_ids = list(range(len(self.cls_names))) + self.class_name_map_class_id = {k: index for k, index in zip(self.cls_names, self.class_ids)} + + # Few-shot dataset (splitting...) + self.k_shot = kwargs.k_shot + if not self.k_shot == 0: + sampled_sets = sample_data(meta_infos, self.k_shot) + + if kwargs.type == 'train': + self.data_all = sampled_sets[0] + + elif kwargs.type == 'test' and kwargs.train_dataset == self.dataset_name: + self.data_all = sampled_sets[1] + + self.length = len(self.data_all) + print(f"number of dataset samples: {self.length}") + + def _process_image(self, data): + img_path, mask_path, cls_name, specie_name, anomaly = data['img_path'], data['mask_path'], \ + data['cls_name'], data['specie_name'], data['anomaly'] + + root = data['root'] + full_img_path = os.path.join(root, img_path) + img = Image.open(full_img_path).convert('RGB') + + if anomaly == 0 or (not os.path.isfile(os.path.join(root, mask_path))): + img_mask = Image.fromarray(np.zeros((img.size[1], img.size[0])), mode='L') + else: + img_mask = np.array(Image.open(os.path.join(root, mask_path)).convert('L')) > 0 + img_mask = Image.fromarray(img_mask.astype(np.uint8) * 255, mode='L') + + img = self.transform(img) if self.transform is not None else img + img_mask = self.target_transform(img_mask) + img_mask = np.where(img_mask > 0.5, 1, 0) + + result = { + 'img': img, + 'abnorm_mask': img_mask, + 'cls_name': cls_name, + 'anomaly': anomaly, + 'img_path': os.path.join(root, img_path), + "cls_id": self.class_name_map_class_id[cls_name] + } + + return result + + def __len__(self): + return self.length + + def __getitem__(self, index): + data = self.data_all[index] + result = self._process_image(data) + return result \ No newline at end of file diff --git a/Tipsomaly/datasets/input_transforms.py b/Tipsomaly/datasets/input_transforms.py new file mode 100644 index 0000000000000000000000000000000000000000..bd23ffe3be52bd845a9202e0642297afd7a14484 --- /dev/null +++ b/Tipsomaly/datasets/input_transforms.py @@ -0,0 +1,23 @@ +from torchvision import transforms + +IMAGE_MEAN = (0, 0, 0) +IMAGE_STD = (1.0, 1.0, 1.0) + +class Ensure3Channels: + def __call__(self, img): + return img.convert('RGB') + +def create_transforms_tips(image_size): + transform = transforms.Compose([ + Ensure3Channels(), + transforms.Resize((image_size, image_size)), + transforms.ToTensor(), + transforms.Normalize(IMAGE_MEAN, IMAGE_STD), + ]) + + target_transform = transforms.Compose([ + transforms.Resize((image_size, image_size)), + transforms.ToTensor(), + ]) + + return transform, target_transform \ No newline at end of file diff --git a/Tipsomaly/model/__init__.py b/Tipsomaly/model/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/__init__.py b/Tipsomaly/model/big_vision/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/configs/__init__.py b/Tipsomaly/model/big_vision/configs/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/configs/bit_i1k.py b/Tipsomaly/model/big_vision/configs/bit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..8bd53c318b108bab483923a95e8d3c0df42d709d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/bit_i21k.py b/Tipsomaly/model/big_vision/configs/bit_i21k.py new file mode 100644 index 0000000000000000000000000000000000000000..c42342e9ab8ff513211954efab79dd4309fbe101 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/common.py b/Tipsomaly/model/big_vision/configs/common.py new file mode 100644 index 0000000000000000000000000000000000000000..c1628c3ccaa554eb5d2a39e2317fb06953542a6d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/common_fewshot.py b/Tipsomaly/model/big_vision/configs/common_fewshot.py new file mode 100644 index 0000000000000000000000000000000000000000..c430383639adcf6103e0976b190eda5b2740321a --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/common_fewshot.py @@ -0,0 +1,60 @@ +# 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, pp=None, **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'), + } + + pp = pp or '|'.join([ + 'decode', + f'resize({resize_resolution})', + f'central_crop({target_resolution})', + 'value_range(-1,1)' + ]) + pp += '|keep("image", "label")' + config.pp_train = pp + config.pp_eval = pp + config.display_first = [('imagenet', 10)] if not runlocal else [('pets', 10)] + + return config diff --git a/Tipsomaly/model/big_vision/configs/load_and_eval.py b/Tipsomaly/model/big_vision/configs/load_and_eval.py new file mode 100644 index 0000000000000000000000000000000000000000..7e102b0f561f2cc6ec59439f831e9e289488b7b0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/mlp_mixer_i1k.py b/Tipsomaly/model/big_vision/configs/mlp_mixer_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..8afe9abfd31f4ecb4e53466ea3e2b2794e8af7e7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/cappa/README.md b/Tipsomaly/model/big_vision/configs/proj/cappa/README.md new file mode 100644 index 0000000000000000000000000000000000000000..d5aa147205f0f4139bf40b2ac5d9825120c22522 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/cappa/cappa_architecture.png b/Tipsomaly/model/big_vision/configs/proj/cappa/cappa_architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..5cf85b74beb97abd4e97d71bd10ca1d8a83c45ab Binary files /dev/null and b/Tipsomaly/model/big_vision/configs/proj/cappa/cappa_architecture.png differ diff --git a/Tipsomaly/model/big_vision/configs/proj/cappa/pretrain.py b/Tipsomaly/model/big_vision/configs/proj/cappa/pretrain.py new file mode 100644 index 0000000000000000000000000000000000000000..8b0df3c1df1c12c50f820ca05237d52f14ce20ad --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/clippo/README.md b/Tipsomaly/model/big_vision/configs/proj/clippo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..e33f891363053fad22b1ad4f84c6af6d316e72c5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/clippo/clippo_colab.ipynb b/Tipsomaly/model/big_vision/configs/proj/clippo/clippo_colab.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..dd540a85114007630b052ee024bd1cd62da45204 --- /dev/null +++ b/Tipsomaly/model/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)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ARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRATKwEwRBEARBEARBEARBEARBEEQGyMBOEARBEARBEARBEARBEARBEBkgAztBEARBEARBEARBEARBEARBZIAM7ARBEARBEARBEARBEARBEASRAavXxQ9/+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+HkqpNX2Dbdu2Ye/evbj00kuxZcsWlEqlJM1SSuzatWtNHvbs2bNGW3v1afOq/2nun/SYpV84WdOTtt0l7cwWzrD5moR2xnaG/fv3J32HeOx30UUXAeg+wfPMZz5zzTfeamDfs2cPGGNwHKevvae1/9M+VvQ8b43Bu1MeWvtDrQb2PXv2QAiBxcVFXHrppYmr0lae+cxnJtoZj1la+z+A6Vft3LlzzW/xGOiss85CrVZLxmuxdrb3wxYWFhK97/QdprH3tLJt2zY897nPTf7upZ3bt29fZysEjIE9TtPc3BwYY+vGeq1jdt/3k+vt91UqFZRKpaSv3S0P8bi/k82qPV6ge79zx44dWFpaWuPSdN++fdi+fXsy/l1cXOyYXsbYmn5+6/tYWFhIxr+taK1x/vnnJy4k47rSnq59+/Ylfem43Ft38beO8UulEqrVatJut+d/z549iZ0lrhvtBvbYtggg+YbabSq7du3qOYbpNYFbVO1knTo5MR/+8IfHsgflO9/5Dh566CH8z//5P8cR3VTTbDZx7bXX4t3vfnfP2cK8Zt+llOv8Lccn/7785S/v6ueQmF7GsfK8CHzve9/D/fffj7/4i79IftsseScIIn866cdPfvIT3HPPPXjb29627v6bbroJz3nOc/C85z1vXEkcGaSdBEFkZTPrx2bOOzEcVHeKz1e+8hUsLy/jPe95z0jC38x1YDPnnRiOvOrOhz/84Y6BjPyQ024ZmOaPoohpH3bLYDeeeOIJ/Mu//Mua3zjn+Ju/+ZtcTu/e6HTbyjLuLdGDUJR0TIL2vE/yvWxE7SwioypPek/5Mg3fQ6d0XHbZZXja056Gj3zkI+uu/dVf/dWa1ePTDGnn5oO0czoo4vcwqjHLNFKkvBexrhDdKVK7uxEp4vdA2nmGIuV9Gu09m5lRa2cuK9iH9Vu2srICz/PWuDIoknhlfW4U/tyUUjh27FiylTJtWtLS/lynrd+MsWQ7d6/nB90O2C5GeeWh17VB4hiVW4FxxDVIvoct9zzeW9b0DZKGYXUnj3c9Co0YdT0dZd3p9Pe424JR6M6wYfcLaxLamVe4eYQ/yrjy0M4wDHH8+PF1v2/fvh1CiFTvLe21cWjnoGGP+t6i1p1Bns2r3R2Vdo6ybIqgncPGs9G0M+/+TZ73dnpu1No5bBrSPJ/m2mbRzkmMWTaqdubJuLVz1IxDO5eWliClXOdCJa90TardJe0k7ZykvWeQe/J8rp2RrmBvTeAgiY0zNzs7u8Z/cqdw0hZElg++/b5ez/QKs/33rOXSK07OeeJna5C0tIbV63r777ZtJ76a0jBo2Xcqo/b/dyJL3jtdy3sAkVcnIe/OxiD5HrbuZE17q1hnSV/aNMT5SKM77c91etfDiPSw2tkvzEHSl1Y7s76bSWtn1rTk+VyadzMt2tktzHG3u4OkMwt5aKcQAjt37hxKO/td66fPaa+l0c5O94xbO9PG3S8tvcKZJu3s9P6GGUxmeWfTrJ2DpKVfmINcK6p25tHuDvJ+RtnvzEM7ezFK7cx7zDJI3O2MQjvTpmuza2e3e7LkfVRjllFoZy+mRTsH2aU4Ce3sRZrvLo/6NA3aOS57TxG0cxrsPWnumaR2rl+C3IM4oXkxyIsZZQdxFOlIS68yzUMcOz2Tdx66xTPN4aeNcxLpSBN33t9qHN9GeK9Z89HvO0zzLefFsJrV77dRvIdp185RPJfm3Uyy7mQJM0t9GyT8PNio2lnUOIbRzkkalDaLdg7yfZJ25su0aedGHLNMcxzTrp2jZqNo52Yas2QJk+w9nZ/ZKPaecWvHJPWrCNpJ9p7s2hkzkIF9ksbEjcqwMzeTZhTGCGJ4pqHuDMI01LNp/5anjWkv77R1ehryspGYhvIeRA+nXTuJ/Elb3kWtO6Sdk2OztLuD3jsppqHMNxLTrp2tTPu3PG1Me3lPQ50mpp9pqGdpvteBDOxZSFtQWQq00zOtv/W7Pkw8k2ZSncQ0s/fTQHs+Bi2jvOv1qL6TYd99Uer+KFemFSWP7Yy77rRr57DfSNZnRs2k0rSRtbNfu9vr+aLdl/X+vJ/vxSRXYbUy7m+pqHVno2rnqFeEp6WI2pnHO50W7Rw3pJ35P19U7ex3f1HraD+KMmYpYvltBHtPEcsV2Bz2HtLOdPcXtY72Y6No58gN7K0F1evl57WEnzGWdFrz2maRNg950i/cYTqJw6R5VAI76MBi2HJvz8cg2z46PZ/mvjzq/6DlP+z76ld38hoQDkqvcu3HKDsNeeZ33HWn9RvotK1qM2rnoGEPE2/WsIugnXG72+l6v+fT3LcRtbMf4+gcF0k701DUulM07RyVlmw07ezHqMcs06KdaRhV3ydL2EXQzn7fbN7tS173T6rd7fYNDFtOG1E785wkmlbtjH+blL1no2pnr7DyoijauVHsPdOunf2YVu3MZGDPWoh5vvxeK0FaBzitv3d7dhDyykMeFTGv95AXoxTyvIUjpr1+DGO47cck605eeRk0D+MYkI/LcJaHjhRRO9t/G5VRfDNr5yg7JpPSTiB9ven2W1o2onbmdf8wZU3aOXwaiqido2x3p1E7i1h3NqN2jqvuFEE7RzlmGcczwGj7DkDvb2AjjlmKpp2jDIPsPcOlYVLamZZpHLNMo72nG5tNO7MySe3MZGBvnTUcF/1mITqtGur2e54zfFnoNtvaSuuqlixhdHu29fcsq0L6DRLShD9IvFnSOMj1TvWj22xgp7DG8R3E4Q+arlYGqfPd8tTr3eY9CG6v3706WGnj7Fc3+4WZRkfSftdF0s7W39Lo7Lj1spVxaOco6JSmdu1Mk6YiaSfQXVfSrCYi7ewfXrfr/drdYQ12k9DOfmlq16hx1J2ia2feetCuQ53qwTRqZxHbraJrZ3tcWcp4kAE6aWe68KZJOwdNV+vvg47T8+7vDHrvKLRz2PF0Hn3hSWtnnIb2v6fB3jNO7ez3e5bvo1+7O2j4RdDOabD3FEE78253+4U/rL2nX/iDpjMP7cy8gj2tQSAv+lWYXp2zfpWjCAOGdvq9vH5hdHu29fdB892v8WpPc5o0pIlz0DQOcr3XIDqNwI3jO4jDHzRdrQxS53t1Xro9n8fMZnv47Y15mjT2irNf3ewXZhodSftdF0k7W39Lo7Pj1stWxqGdo6CfgSVtB6JI2gl015U0hgnSzv7hdbver93tpZ1Z4hyHdqYZ4LTGP466U3TtzFsP2nWoUz2YRu0sYrtVdO1sjytLGQ8yMCftTBfeNGnnoOlq/X3QcXre/Z1B7x2Fdg47ns6jLzxp7YzT0P73NNh7xqmd/X7P8n30a3cHDb8I2jkN9p4iaGfe7W6/8Ie19/QLf9B05qGdmVewZ2HQWZBB0tD6AlorWD+RGJS88pDHR5rXe8iLPMu13/W88tBeP/p1rodhknUnr7yMYiazG2nTPMzM5iDkoSNF1M723/o1dFnZzNqZ9+CwlUlpJ5C+3nT7LS0bUTvzun+YsibtHD4NRdTOUba706idRaw7m1E7x1V3iqCdoxyzjOMZYLR9B6D3N7ARxyxF085RhkH2nuHSMCntTMs0jlmm0d7Tjc2mnVmZpHaO/JDTVtJ2MPOIJ8+XNapBU9Y0DMuohHqcHZNRCcioDBOdws6ShyzxplnhlZasM5lZ053m+jAzm8OQZ7n2Iw/tHGbQmRXSznRMo3YOmo6YLPV1I2pn2vuztLsbVTvzbnc3qnaOcvCblSJqZz/GOWZJw6S0MwubXTtHOWbZDNo56TFLFqZBO6dpzJI2nqK0u3mmYZITcnEahrne6xmy96RjWrVzlPaeQZmkdo7VwN6LcQlXFiaRtnEIex4f4ShnALMwaJ46pTlNGJPKa5p4OzUig6Z3lJMpacm7Q5Z3nvJeLZGVtHFmqTvDUoTyKIIudWJUqw6yMo5vDMi3vg7CRtLOQVa8Tot2TqLdJe1cH+eoDCF5UgTtHPWKt2HiLEJ5kHamC4O0s3P68647o2YU2jmKPBWhfzzpvm4vilAeo2jTptHeMwrtzBLGNNl70j7XL4xBrufBRtTOgQzsnWbV8p7FnkaGKYNJlsugYtOvMzoqBimjPNLTK4xJva9BvrtRl0GWOIZN06B1L0ujM8q6PC3aOe6ORBG1M2u4eXT28gg367Pj0I1JMA3aOcp2d1q0c5A0FbE/WVTtHObZcRm5xqmdRWx3genRznHuGEhzvUjamTaMYcIbNIyiaOek2920acnr2Ty0c5KaME3aOU6ylsGotbMfRbD3TLt2TopJa+dGtPeMg4FXsHey4BdlBmhSBuy0s0xFeOH9KOIgcVLlNuxHO2jdG0REp6EuTZIils+0aec4KKJ2Zo2LtPMMw9anzaadRUpTkdISM8g7zLPujOP5rExiVX0/pkE7i9TudqIo9bFTuzsN2kCspSjamXX16CjYSNo5ToqkndNm7yni+y1imiZFkew9adJRFON/EShqPe5rYG/fEjJIJRzXyrte6chS8KNId7d05L0toltY3cIex0eaZfZt3HWnE1lEbZgtVIPUhVG+t3ELd5EailF9g9OoneOKu9+z49bOdiahnXHYvSYXilJ3OjFsu0vaOXx8RUrLqMMeV93p9/wwjFs7WzVmVExSO4F0bUe3Z4eJdxjGrZ2D1J329miUY5ZRQ9q5/vq4213SzsHTNcodCdOkndNm7xmndhbV3lPk+KbN3lME7SxqfJPQzk4MvYI9zb2THLRkYRiRHtc2tX6knT0dx/aYLPVgmPc/yrozji1L44xn3GG3kuWbSRPeMBSlXEk70z0zDkg7x0Neg6Np18683vW4v6eitbubQTuzPDvOdneS2pklPGp3849nnGFPQjvzYDNoZ1HrzrRr5yjLlbSzM0XXzmkbs+QR3yTCJntPejabdgIpDOx5JCDvTOTx4eb98WfJ47hf9qDknZ6i5S8rRVpxPc1s9Pq1UbUzb0g7+z9ftPxlpQjtbtGZRJ6oHPtTRO3MQt7aUrR2PI/8FfF7KKJ2Fq2cipaeaYW0szNF185hKaJ2bhR7zyjCyJOiteNFYdLaNen482Iz1q+BV7B3Y1yVoNeW+UFgLJ0vMWL8jOq9THIrUVamsY5OMs1UXr0p0m4BIn+KrkPj7BRNYx0l7RwM0s7Rh0sMB2nneCDtHIxx6hBpJ5GFabT3UF0ajKKX16TtPaSd00suBvZWEczLx03rc/38HKWJo1/F7RbfIPTK+7CVcFAfTaPIwyjoVEbDbjEapO7kxbD1v9tz4942m+X+9ufGvUUsz7gHfX95fNfj0s5+9w5yD2nn6MLJEt8ktDNr3cnj3l7PkXZOJu5xaycwfLs7TJqKpp2dymKcGjRMfEXQzmHj3mjaOW3t7kbSzrzv78SotHNc7gxIO7OFk+d3PU3aOa4xyzRq57DhDBP3uLUzD4ps7+nGOLVz1EyLduZiYG99cVm2GPUTw34imyaObpUrS3xp4kgT5iCk8dE0aJmkiWfUq2aGrTuDhpmGYTq7nfKQ5qPtlfdhO9+DfoPTUndGEXd7uaf57oahaNo5SMc0bXxpIO0cLr5JaGeadrJf3cljMEXaORxF0c68291Bnu/03KD1dSNq5zBxTyKcLPGNSzsH1bpJaye1u+OLe9A2axq1c9Dw+kHauf65aRizDBomQPaeTtdJO9c/l0VHNpq9Jw2j1s5RMy3amZuLmGHIUjjDvMxxxzcupiGN08Cg5TjsR5n3R71R6/e4mYYyyftdj7pzXlQ2ar7GzaB1a5yDwVHcn/WZjc44ynEcg9hBntms2kmkY5j6MQntHAfTkMZxs1G1M8/7ic5s1HIke896piGN46YI2jkN9h5iPaMox6nzwU6kY1LvY5xb7PIkry1deeU5r/DGMaObF0WoO5OIt6jfSVHTRRSLvDUvr3BIOyeXhnHHWdQ6SBC9KGp920z9oGnVThqzEACVV9Eg7ZxcGkb5TKfnSTuzM611ZxByM7DTLMpw5P2iR/k+eqW1fevJtHzwWbdFtj+fV7mnCS/LNrZB4h4n7dtzhs13ViaR90mVdz+mRdOnRWOAwdI6LfkaVjsHCScNG1U7B2l3s4QxLKPK+yTa3azpGSScIjBNejRNaU1LUevbRusH5aGdo2RUbc1mGbNMgqLoUd7lNS3aWVQ2s3ZOou6Qdq4Pp+hsFntP7i5iSJzPMEhZFOXDyPsjHuTeotedoryjmKKlZxjyrlNFr0udGGeai153JqmdWd8DaWd3ilbfipaecXWep1E7i/auipaedkalnaOsO6Sd3RlnfSt6WXRiIxke8qZo+S5aetqZRu3Mm42kneNkGstiku1u0SlaXoqWnmHYqNrZzkAGdurIDMY0VoxJvj+qO0ReFK0ukXYOxiS1M+t7mMQK3lHHTYyHIr2/IqWFGBzSzjOQdg4GjVmIzcw0aueoIO0cDNJOgpgMRa3HAxnYi5qJaSZrmU7KV1Pa56bNt2TRGr68GFd+x/2+09zX6Z5Jvedp1M6ifyvj1s5h/eRlrduTaiPShleU+pA3o8jvKDVJaz1SHd6o73kUFL3dbdeUUffrpk0782YjaeekynSatXMU4U1j3UkDaWc+zw37fFG0cyNRJO3M8znSzmJQdO3MGs60aWfMRA85ncZKXpQ0D+rvqvXeVv9Hg+YnbQMxap+oeXcaWn8fdV0ete++1mfS5Ddr2HmElzWcNPd1umcYP3Gj+vaLpJ2TqDvjJqt2xs9l0c7W59KkLw+mRTvTkMVAV0Tt7KZJecAYG2ndGbV2jqvd3UjaOYl2dzNoZ6+0ZGEzaOcow5127QSKV3dGpZ1FbHfzDC9rOMNqZ/vvacMoiqG8KP3/QSjq2GyQe8ne0/t3svcMnp4i2ntaKYp2ZjawZ10pkqYyFJleH+a4Z/uy+jHq9u9xkUc5jdJPXBZDwqj8lGURlqxiNGx6pqUDNept7P3KocjaOa66s9m0M6/3NQ3aOQricAfR5mnSzmlhlNo5yna39d1Ou3ZmDYe0M1s5ZW13N5J2DtqnSRtuWqZFOzfimGVQ7Zymdpe0Mx1F185RkcUQWwTtLJK9Jy0bUTtHeS8wHdo5CYqinZkN7J0iT1PgRf7AB6U9L2nLZCOVQRaKMiua5plJikjW1Q6DMsly3oj0K4eiaecwdTyvATNpZzqmQTtjRqWdadI0bdpJGLK0u5tNO7OGs9nratY2ZhJG4aJpZ5Y+zWZk2sYsRdDOaRizbPb6PU3aOSpIO0fLtGnnJJkm7dzM5OYiBqAC78QkykTr/PwJbnYmWacnsVIBmJ5GZhRM6tuZlno2TialnZN8fiMxLXWatDMf8tTOSb2/QSmqdk6CadTOor6/zVanN/uYJc+8k3ZuLjbjmKWokHaOn0lp5ySZlnQWnVF/OwMZ2PNMyDQLQtHLgbHs/gR7pWea31k/RpG3TmHGv/W6Nuo09GMU4j3uvGWNb5hvpxdF14xJxpOVUWln1nDz8NnWLdwiM27tnGQa+jEt7e40aeegDJq3otSdcULaWQyKoJ15hD8s0zRmGVX+i5AO0s7+jCp9RWt3i/4exgVpZ35sNu2cBnvPKChC25SGUY9ZBjKw57mNoggDsazkkfYsWzzG8fH0Ss+gvsWmiVHUx05hxr/1utaPvHxojZM8v5k0YRYl3zHTqJ1FK8OYUWvnOLa1TuM33I1xa+cwaShquY9by6ZJO2PSvrtB8zbu8i0CRdOqUaenqH3VImhnWobZlj5KiqSd42QU3wxpZ3+Kpp2DsJG0c1yQdhqmaezaj2my9wybhkmwEe09Wep/KgP7MB9WXuI8SpGflgZk0h/PIBW+qKR51+33DDNzlrZu5fmNZcnjqBlF4zwN3+20aWcR604eFE2ripaeNORdN4rQN9gs2jmOMIcly7tIG9Y4wili3cmDomlV0dKThs2gnUVkI2hn3mOWPOIbNpxpaHfzoGhaVbT0TALSznSMwt4z7do5LnvPtGvnRrT3ZKn/qQzs7QFPwsfbKBuGSTQ609jQTWOagbUfZpo8DFLf+4WXtsyG+aayfJ+t90x6Z0SRwsybadPOItadXvETo2VY7czr3lHFSdpZHPJ4N1nuzSucItYdwjCJ+r7ZtLOITKt25j1mKVrdmoZ2lzBMWjvzhrQzHaSd/e8d9Hoe4UyDdk5r3cmbXA857US3lznoSy7SCnZq3KeLNB9m1nfab6ax22xjp98ntdpmGoVrMzBp7aS6Q0xSO9PeT9pJtNKrPgxSr/pd65eGflDd2dhMi3YSxWOUdadfmKSdxKQZ5fvNa/xE2llMNoK9h7RzYzByFzHdXuagL7lIK9ipgnanVVCmqQHq9U575aPfTGO32cZOv2etV1nTVzRa68w01Z1eTLN2TlPd2UhMW90fRf3qdK3X90Daqdf8txHImo9e9WGQetXvWr80TBObvc5MiiJoZ1amray7Ma3amXXM0i/McWjnRml3gY3zHWwW8ho/kXaSvaf9Wl72ns2gnRvR3tNOJhcxxOYmzQc+jNGjaBQ9H0VPX1pa68woBg+TYKO8GyIfaIVBsZimsu7X7rb+lyWMojFN72bamdSK2UEg7SwW01TWeWjnNFH0fBQ9fYMwDdpJFItpqv9k7ykWRU9fWjaivaedgVzEDJvRQZ3zZ3HmP21pGuVWkFE9O+gHnsd7S1Puk/4Qi1Cf8yRNGY/i3faDMTax+j+pOCetU2ni6HeNtDObdo6j7oz7myhifc6TUZRxp7oz6Dc4bOe86OXeiSLWtSJo5zS1u1m0c9g4Oz0/6HdNY5Z8KIJ2DkoR291BGbaujRvSzvyeJe0k7SR7T3fyrjtFzM9mt/cMm9+BDOzDDs56OedPMxjsd397w9etIWz9Lc2BAYPePwij3oLcXh6tz2apPFnSm8eMW5pyz3tmL4sQDHK96DORvcq4vS7lHc8onulV/0fduE2jdnailxZ2im8zaGfaupO1no+y7vT6bRCmrd0dNZ2+t2Ha3TTxDHJtUCapncNC2tk5jlG2u0XQzmGe6/Z8r++63/Np6sU0aOeotTfvMUs3Rq2dafrK09SOtf+dxwRu3m1HmjIuuna2Qtq59v5WNpO9Jy0bzd4zTu2ctL2nyNq5We09w+Z3JIecDvris8ySdRPreLYjDjPNYCVNXEUYRGaZ7W3Nf6cPfpgZ5Cwz+YOSpS7lFV/W9z6JWfK830O/8Nrr0jjqwbDl2KvB6/aux/3dT0I7O2llt87tMHFNklFqZ7dOer84p107u2llezvces8gZNHfImpnv452Fv0fJM1Zwpu0do7jPW4E7Zx0u9vt+rRrZxryGLPEv0+rdg5DN81o15ZWiqid/Z7p9+7G3XfP45ks2tkvjDzHbqO4f1CyjFnarw2qnWnCLAIbzd5TBO0ENp69Z9TaOc32nnFq5zTYe9qvFUE7MxnY834ZRTHC9CLvD32YytOJQWdiO1W0QcUt7T3DMGi5552ePGbb8p7p7DWgzYuifcNZ6mK/byJNHHnPKhdRO8e5qiLL9VGHl1Y749/Saueg333enZZp1M5Bw8ijQz7OfKeJP+92d1q0c5j4Oz2zGbQz7/gH/Z466VwRtXPSbcygTIt2DnK9nUE1o6jaOegzk2h3N4N2TnrMksf4uZd2dnomj/7MRtPOPJhG7SR7z+jjT/NM3vaeNGEMy6S1M0v406CdmQzsw8w+joL2Qmud2ZxEGvK4P48Ba6/704bfrQLlsYIhD4ogsONmEmnMEmevupM1DcOGOWkdKLp2xr/lqZ3jLpNRaWf826jDHxd5vtfW9mCa291Rkzb+vLUzjzCnpeyy3j8oRdDOvNmo2jmONiZr+BtFO4uS3iJqZyvT0H+fhjSOO7ws8Q07YdQvDNLOjWvvGZSi2nuKrJ2T7tPmRRHKZdL2niJqZ08De5FmGrMMAoftkE2CTmnJewZ5ELqV4SCVs0jl24sss8yTzFu/rUaTLve0dWeQdOZRH/uR5XsrWtm3krbeZtHOIlEE7dyMq32A/nWHtHMw8tDOtKt9iq6dRaon3X6bRu0c98rFvJ+f9DeaF9Omne10+l6nYcxSNO3MwkbQzkGuF4U88jHpdnfYMIrARrP3TIt29mJatTPN9XHVnWnUzs1k70mL1evipD+AVkaZlqLncxpWH/SiaOnpxjhWxuZJe9z9/i4qRUvnNK5G7kXR05cXRdDOIq5sKQJFq4ObQTunYRVimnpRpHrS7bdpZNwrF/N+fqO8h34UvQ7m8b0WTauKVL69KHI7No7V0pOiaPnYaGOWUVKkfE6rdvaiaOnpRpHtPZtZO8fNKPudqVzEjMLCP+nZlmEYxRbEIhHnY9D32D77mseKplHPzI+irqaJb9g0DXr/oPFlXYmY1zbcLHWpvWxHvRJg3HUna5ijJK+0FClPeZBFO/OOf6Otairi95bnrok8213SznzCHCV5pGXYNrdbmJ3+neb+vONPc88k4h9nOHkwLd/bNGpn+7MbbcySNcxRxpfnmCVPhm13px3SzsmFmZUi2Hs2qnaSvad3GO3hDZKeadDOVAb2YSz83QqhaLMYg9Bty0PeeZpUxYnzkSY/7Vu08twqHYc3SDmMwg9THvW//VqvbTN5zKgN8r3ltfK3/f0PyrB1qb1sR70iLs3Wp42uncOmZaNpZ0wW7cw7/lFr57jplpaNoJ2d/p/mmfjfpJ29292sYY6SPLRz2Da3E1nrzijiT3PPqOIftg2Zhro2Se1MG2aa+yepne3Pjno18UbQzmF1Zdh2t2jaOSmtyLufvBm0s9O4rAjamZUi2HsmpZ1Z7+l2/6TsPYPcs1HtPeMmjcZlOuR0ENIWQhZBbn8mzcxMHvG0M6oXPMqKM87VCMPOdI2iHMY1g5w27ZMYpBaBNHVj3CtnsjKqdzgK7cwSxmbXzknMmBdxoDIu7UwbxjRpZ95a1m+lymbXzn5sZu2chI5NeuJzEMYxZskr3KxhkHZmu14kSDsHZxq1cxRMqv+9Eew9m1k7+12fFu1sZdrsPZPSzmHYbPaeXAzs46yYvZ5JMzOTRzxZGLaM8q50w+Rp0JnWXjNdWQdeeRue8lgFkpZxd84mKVhpOitp6k+vMAddpVskAZ+UdqYJI48VFRtJO+O6k6d2pomzX1gbVTvT1MlRMgrtHHSVXa+/+2lnp+sbSTvziG+jamcabRg0/GEXSYxbO0cdVh7vtMjaWZR2Nw5r0trZL45B0zQM4zZiTZt29gov7f1F0868KMJ3Tfae0T7fHlZR7D3dwhw0DcOyEew9RdbObuRh7xmESWtnz0NO0zItq2UnybBllGcZFzEto9gmM8nwNmpck35PncKcZJqK9C3lTVHSVqQyLmJaJv1NTrOejTOuYd/TKAwa06ydRY5v0to5ilV2k9a+IuUlL4pcJ6f5fY1CO/vFkff94wqraPEVpd0t0vvaqGnZqBSpjIuUlqxhTloTNmtck35PeYQ5yW9p5C5iiOEp2jYPYnC6vUN6t9MPvcPiUvR3U/T0FQHSzo3LqN8h1Z3sFL2Mip6+IjCp+k/vZvoh7cxO0cuo6OnbzNC7mX5IOzcug7zDkRjY01auSWyjyTvOYcJL+2zes0Jp481LDLK6MmgPo/X5UdSdvPPbSqetKt22bo0yHXmGOew21TzqRafw+l3Lu1zy3IY0Lu3MAmln/tqZditl0bVzlGxU7cwj7nGHM23aOUjdGSV5fnPjanfTllHempi3tufZzpB29mcU31eRtbNXGKPUzjzDLIp2jmJctpm0My2bRTvHNWaZFu3M47604RRpzNItjlGFU5QxyzRq56Dh5XVfWgb5PkdiYO+WgGG27XWrxIMybAXPc+vhpLZapY03r/TlseWu3XfTsD7FOj2fJcxB6mXaupN3ubczjOC0v4M86v+o89t+bdi6M2jcgzAu7cxC3lu1SDvTh1N07cwaVlo2onYOGveg944iHNLObOSpndPU7o7yvrTk2c4UZbsyaedgcQ96bx5lkXe72y2OPCiKdnYbl23GMUuW+/Jm2rVzXPaeadHOPO5LG06Rxizd4sgjrFHYe0g704eX132jYKIuYoYRukGf7/XMJAZcxOQYVnTaw5o2pjHNRaEoZTesdo46zlE8T0yeSWlnUepOUb7/aaQoZUfaSWSlKO+RtHNzUZSym5R2FiX/RHaKsoKdtHNzkWfZjes9kHZuDHIzsHda/j/MNqo0wtbP6J5VXHvF3e/5PLa1ZAljVA1BlvjHme9+4XQjb9EZ1faXojTwWdhM9T+POMapnf2eI+0cns2knXnnIU2c/cLZjNpZpLgmpZ39IO3ML4xJ151x53sSY5Y05F13xvntTku4k44rb0alnXmXybDaOcizpJ3Z4yqidk6i3d0M2jnqNqvoTIu9Z9TamUc4G1k7czOwd1r+P+x2giwFnyX+9njy2haVNZwsYYxqlipL/OPMd79wRk1cd+L6WqRtVpNmM9X/POKYRu3sFcYwz26murMRtHPS7W6ntGxG7SxSXJPSzmHDI+0k7Rw2nFHHP+oxyzi/3WkJd9Jx5c2otDMOb5wGNNLOwcMl7STtzBrupPs7k2KU9p5p1M48wtnI2jlRFzFA+tnDUX6MaeOZ5hm3jUgR3sewdacIeSCmkyJoZ1qonheLIrwP0k5i1GSpO6SdRC+K8D5IO4lRQ9pJ5E0R3gfZe4isjLLdLZp2EsOTysA+SqHJUpHySE+WMKjSD0+edSmP95EmPe335Fl3JpWHSVO09GRh0uU+Ke3MAmnn8BRNO7NA2jk8WdIzjXnII8151p2NoJ1FqwfTCGmngbRzMmwE7Zx0HrJQtHowjZC9xzCN2pmFouVhGu09k9LOPClaesZFKgN7v0rRyw9Vr4JNe639vjyErz2MtNuC01zrlPdBK9iwFTKPcs8jHf3oVUajqDvtpKlL7fcMMnPZKf40vw1S7lny0ItpqTtZ4856rZ1OZTqoVk2jdg4SX7f7ul3bSNqZZ7z97p+EdmYh64qhXt/EIN/NIOkZ5J72tAx7bTNqZ5Z72uMepJ70CmfY9KQlT+0cNMxB4xn2+aJr5yjb3TwYt3YO2nfudD9p5/DX2tkI2plHHqZdOydRd/J4fhrHLEWz95B2To92TtreM43aOWqmRTtzcRHTyw9V67WsH0JWoRl3fL3yPmgjMWyjkpdfrLwHlb3C73VtHO+yH93S0KkOdPo7vq+Tn60seR+VoA1b/9OEM856NUw4o35+UtrZr9En7Rzfc2mfL7J29qs7WbSzV/rS1p0ia2ev5/LWzkHCm1T978YgdadXHRh0YDJI3SmCdqYJc1CNGZSiamev+4s+ZpmUdk5ru1sU7czb8JOFadDOrEy7dhal3d3I2jnK+DabduYRftq4p007O/1dZHvPqLWzV3x5MC3aOZCBfdgXP+qPuT3MUYc/SBzjHuj3YlSClIUi1Z2YbmnqFmfatGTNw6DxjrJOjfM9jIpJpLlo2tnpecbYQAOqYeMk7UxH0epOrzi07nzwD2nndGplO9TuknZmhbSzdxxF087NXneGhcYs6X8v4phls9f/adLOcYQ/SBybve4MC2ln+t+LqJ1FYpJpHsjAPmyF28xQ2XWmiPkvYpoGYdrTvxGZlu+/aOkBpqfsRsU05b+IaRqEaU//NDPtZV/E9E+TdoyCacp/0dI0TWW32Zn2d1LE9G/2+r/Z8z8MVHbTw7S/k2lP/0Ymk4uYcczCDRJHJ388w/gHKlr+8gg3r98nSdo0DZP2Iua7naKksSjpSENR0lo0bRnWP9wwcWeFtHNwRqGdaepJ0cqiKOkpSjrSUJS0FiUdMZPSzqKVA1B87Sxau0TamZ2ipCMNRUlrUdLRi3GM24pYDqSdZO8ZV3wbccxS9DjGAWnnaH4flkwG9nHMmLRuuU1zb0y8zbL9+UG29owrf2kZ5OUPOnM6TTOtw26xGfWz4xKgoryboqQjDZPYYjbudLTGkVU7Oz0/SJpJO0eXvmEYhXZ2anfjfw8aXtHKYdQUJR1p2EzaCWTbAj9O7SxaOQDF186itUsbXTvJ1ZahKGktoma0M45xWxHLgbRzeO3crPaeQePbiPaeScWRdxtH2jk4RdfOXA45HRVZXmjrM8M+T4yWUQ8M+oWfd/xFrzvjLo9xh58n05TWTvz/7Z3rjqW6rkYpab//K9f5scRpms7Fd38OHtJSr6qZOCGYEQgTytqd1cfjNLKP1ffnp8+7J7mzUl8zOM2d2e03f/M1d+6o5KPsvqIfy9a5mD0e6OP9ddqddfIz+1hFX+9pd54H9AJ78x9ekwD3AOA+Er2L77Fdu7vYlLvcFuNiiVV/Rt+443yu7dNuf69yx+IRas42oX1rOwpp7qCCvG80355pd/rEr+JOzrw7WtBDdOdXc8cbjWNQXR/1zUMrKrpTg5cDvujOirmDOO9KyHan136q5M6v4XXtibDe0+60j1/lmmUVM2O9RwrnmOsF9g/DnWQpj7Fq7yhr2bVJ6ZN0XLzw6I92nLz7tMqrO/+s+9AnnT7HR/MH6UJkBO1ObHdK590q7uzcwSP75sATZHfuqOjODJDdqe2D1+cefdKCOO9GY+FOtG1q6oKw3tPutI+PfM1CLZNxzRJBL7ADk31BMwMhcZs1qLljAWr+nTzm1UDdF6i52/zh5NxBzT/UMW/+kJ072e03e1CP45NzB3XMmz+g5l/nDg6o+wI1d5tvgJp/lOO1F9iB2T3amA1af5o/oEpJA3q+nTjmVWl3NlL6OI6nxxwXNFeh9af5Qx/H8fSY44LmqtErjxoM0PcFWi43Z4OWbxJ3ihfYf39/QwfAs63oHUltb7VDpe9M4rwza1T3/biQ5J1cnHY939mlbfMej6h3cnH7RYnJqc99Zxbnc0r8d/5z29zlpnZfWu4fS6yPt8yJL8KdXuzcyY1hWZZS3jo32p37eLPPOW6TvM8ww53cYzsid9Dd6emDu84X3WlNRXdK2vJwwNfdSWkP1Z2WeWNxfiItn+VOi+vptzs5dbnlKhB97RXlzt3vJcfHbt7lxkdzJ7XMs62vuNN63uXG5673cOJT+6l15/+2JSZE323zbA91W57vwKK8G5DyOfedWbuYkndycdrV9FEybpw2o/LmbseyX6My93hJcsf6XWi7+JJt3OXmLndm7MZt1y9vrI+3zG9aRLjTi1E/OHk2imFV9l3e253cslJOdSfHbZT6mvjv33m689mWd/54v3fSAs/j7cvutKaiOyVtrebdGe1O3bz7ZXc+x8ujTUnZd7+kMd51pNfTkvMVbhuVaHfy+rP6HTc+mjupZTjltKC403re1cSv6k7WN9ildwGikHzbIArN2J00ub3x3D/acUPJnShOzjMu1iffFd2JwsnuRM2ddicP9DyLBHWh0AvkXG93Ys+7I1D6FpU76HkWydfGQpvrnuPV7sR2Z6/34OdZJF+7ZkF258lwxo21wC69CxCF5NsGUaCPXRbI24/ct6YW6Mc/Sj9GoI+dNxW3H7lvTWMJcq5XdIclFbcfpW8Vx66pBXIufT3/0be/13saS6rtm2r9/SLmf+S02l0gT5DGgnpHNbPPSOPV1KZiLlXssxdIHmp3Nt4g7T+kvjR8kDzU7qxFxbGo2OcGEyQPoX+D/Lqw+pIN0lhUmHebxgpUd5q8IuYJwl2ViD9EQEHzGJY11DuqmfsvO3ekeRPxRy8i651AdC5Z/NGP7Py/QcibTHdSnRjlziq50+6Ug7D/btDcaV3PM367s90pASXXLHLny+6M5jR3Zvuzoju9qOTO7Ly5Lix3Zs+7FThpvQflGMgCzZ03Jq+IiWSVRPdnzxfQU8pnIhlThH5XZDdu7z9cUH2cZ3+godExGkOLP/qRyXObkG4MrviKOxFyh3uxVXGcn7Q7fZC4E52vuLMiCNt5mjulfxis0fEFd+62ByGPKo65ZNwQtvNr6z3tTh9m7qw0776p6M6KaMYt7BUx3E7Oyq+SiPtOrtln0r5yH7+R7jjuRRynHc9HiLjjMRtXSTu/v/S/zH1DLe8hae5FO5V3rNlYrT6fEZX/Fm3Pyu7G47riv21n5c4VVsfGae7k4HkC0+6k8wV3WlLVnSi58xV3cvoUVc8itqc7r8suP1blPPLfknbnum67k8bPzw9prHYguNMzd95IcwfZndnrPdo4VKq6U5o73NhW7kSZd9HdOavX7vwX8wV2q6/mV3pVyV2e+/iN5zY+Y3Pa8XyEiDses3GVtGOxHd4HIzeOhbB2Y+WZO565Je23V+5w+6FpH+kxV2p5VHdG1LOI3e7kxanuTkvQ3Xli7kjbHpVvd+pie7rTArT892qn3bmOLQEtdyLmXU6MU69ZrKjuTo+2ra7TpFRxpzR3uLHbnbp2rM7Z2p3/Yr7AbkHkNxAs+4DQ75PQjKfHvsg8CRjh0R+0MW94UPeBZ+60O/NBO46p+RaVB2jubOZEzbvtzua6dPvgC+5s5lSdd7UguDO6bvMvaO7k9qHJA+047vUev7pfAXKBfZZIHjvU6rFOaZ1mjmY8e1/I6DGvTaY7pd9ekdZp5qAfx5bzLgqV+960O5v/0OyDd130a5bGFvR514tK30C2qtv8S7uzkdLHcTw95r6EvYPdIo7HDh3FjHonkSWrtlHeyRXVtuV7pizazowjaedrubPa3t/fuL/O3e7Ux8ls+8Rt0MRaYZk7XnEk7bQ7//6s3Tmm3ekTJ7PtirnjFYfSztevWXbbe4I7PbAcqxNzp8o27Oq1O+fttDvbnVbttDtlcSiEvYM9K05k26jvAvR4J1fEoovVftDEsmg7M46kHeTcoX4DweqdXD8/+7/ObUW13LFsG9WdGXEy22536tppd/79WeeOf9sneueEbYikWv6P3NDuXMf2omLuWLVd1Tu7eTeKdqd9HEo77c52p1U73u6slDvW+8Fkgd16sVVyV2FVRlLfe5us40XfAbNKRO9+78Yp8lt2mWTebXyjyZ27LsIdaos+oHuBcse73ZnTTrY7PfqA6OLT3BmFtzuticjldidGO9HjUtGd3uc30Q6o5M4nSPPPDI/ci3blqA/I8ajtoG9Hr/fwY1Ra70FzJ6I/LUFw55PK6z3adsUL7M+Gf35+TGWwuqtAvaNB+ey9DdR4s1ir7aRsA4ddf70S0kKWq3GflaOUGZV/j/vsW3ZWC5FW424VbzZBUsZ9N7azehF4TdrvbZB4hdNGpDu5jI6XO+bOnZzcQXOnFR7tILnzLmPpPMm8O/sdiju/QoQ7Z21aLGBG5LK1O3dl2p1/4LiTMmc9/393zVLFnbN40n5yHHCCM6XzLvUYfv7sec2icadFv2bf0t0dw5XcacVu3q3uzhEI6z2rz05xZ+TieJQ7T1/vQXCnNVXdKV5g53TE8kTScqAtL/JGSe3dbnYbIygHKXecqALdxfW84L/rW427VTxJ/VHblDjavnIF70HGsRnlTi6a44WaO4juROZEd2rizBaupPGs+hOdp19x56zNVR5I4mmJciclHrXd0+GM027ePdWds8++fs1ChXuerJkvPK9ZMue+dicPpGMz0p1crNd7qO0g7R9r2p111nus3Mkps+IL1yzm72CfgTxBaam2bZ7JzD1IuTFvTvi2S0Wq5c6IarlTzS8cqm3bCblTbRtOod3ZWNLu9KXa+J5Mu7ORsns6rwLVcqfa+HKotm3tzkZK546OsAX2JpfZ4yGVxEV9TK3xo3On+RrtzsaCzp3GghNyp9I2NPlUzZ12Zz6n5U6lbWhyOCF32p35nJY70dsAt8COtBOR+sKBc8feWlyej31UussljYfSDwqn5s5Jxz1SvApo3Yk6Zl9wpzXtzv9od+bHe4J6sXmqOz2plHeecPp9qju5oOzrSjmM6s4R7c5Yqo5hr/fwQXEWSj8onJA70e6EW2Bf3W2gDsTvL/0vBa/aQHivlQRKv72S6vn+tYonM5pxscgd6zGrug+8+k3Zv5XG7ImFO59ld+OAeELq6c47NvX9khIQx3SHxJ2I8+4bj354zruj/7ek3bmn3bmO/TV37vp0qju5ZF+zcPqBAkpfrb4hWPG68cbTPRHzbkV3PstVXu/Rku3Oisct4nrP19x5ynrPqg3RAnuUjKV/LOLnR/5H86ISdHehMZoIZnUo++NdnyOY2cXRrC7yQU694JHEjJIE9/gblY8+obL4hpJ2HN6xLcaA2yd0d1LKZk/mWe68t/dug+NOyu89TzQssHTne/452Z3aE3EPZyC6k1vee8FNkpvo7lwxcud18Rw2KoPgzqwFJOp5NqI733DPbawdUdmd1u1Vcaek7gzEReAbL3fesZ+/07pzlAvI7qSUq7zeM/pcO+ao6z0I7nyCtN5zkjs913squVO0wI54cVAN7hiuJhFKrFl9at1R2Yp54NFnzR1kyR02Se5QfueJRXvW+efRp4g2s8neBgR3Uk/qNe2i0e60ixFZ3yMmgjujc8eCk477Su7MHveK7pS0pynvEeOEY9hjDmp3xqB15+h3Wnd+8Zolm4xzRsT1HgR3RsT8ujs913squdPsFTHId5ejsBwDTYJq7tIgEfWtCyqr9qV3SjV3HtHGI7s/KyR3PyWgfMsKsc0VFdxJyaHo3KH2I3t/e4xLRXdyPIRClDslMb3mXWl/IvA8ttud+fv3DZo7uW1R0HyzEolK825fs+C4cwXiNYtHP04B2Z3ItDu/604vst0pXmB/dyrjLqdkYDyTYjYG0YmpuUsTgcejR9o+UFj15/7Mat9SHkNC+2YBNf8zQTg2ENyJdiOggjspd9Cjc+cEd2riruIguhPN2Ryy3Jkx70r7o4krpd1Jo90pb/Pr1yxvMnJHwgnXLF9w5wqEY6OqOzP2W7tzTbvzX052ZybZxwZpgX00YFad+v2l/4EKiz5I+23Zz6xvT3l/u+w9RjuRUqTyjGE5cXH2AaedUdxV7sw+8+qfJc8+rvpAnYwkuUSNN9svUqh1Ud0pIcNbqO60iCXJ72ru1MbduZMaZ0bE8bNzgORbFjtXrnKL2x6SO2dwT/Yt5l0Olt6SzrvUPoxy5yR3zn7m9kECuju58SlYuwLBndJ5Nwp0d1r0T9JONXeu4kfWrerOjPUeSh+y3KlFc43+FXcirfdYjiH6es+OjGOJtMDumeg/P/z32mSA0k/NXSPvb5e9x2jXnvbukuX2UIQhZZU7FnlFnbA8sbhTqMklbXsztIt4ld1pmTunudMidzT5XcWdWnbutIg/w/MCi/P5qMzOlavcsnb1DAR3es67K6Lc6fENnVHunOROLe1OXgzPa5aK7rTC85plFaPSNQuyOyVEzLueIPSBgvf5AYI7JWSu91jS6z06ENd7tP25Lnt3mr2DvfHDQ6YRd1Ct8b4YyWxfQtYkS20/cryofbH45ha3bUSycycKy+3k5hBS7rQ7/yYr/6k51O7klYkk251IzviCOz05dTs9r1kqutODirnT7uSV4ZS7qZgXzR++st7T7uSB7k6k3OGWu7HOi9IL7LPH97hl0JEcNFYT/AmLbE+eJ+lcYaCNhWaSXW17xdzJXMxBywsK7z5TjgXpQlsm6PsGvX9P2p3//v+qnKZMu5NXJpJ2Jwbo/XtykjspfOWaJcKdmkfn0XKn3ckr09csf+j1njntzv1n1efdTHd+MXc0bd/8jx0dhN/fX9Lje6Myo7rVeW+TxeM6aFg+9ljxwl4LNT9OHBvpI3lfcMV10X3xlfEYwX3kF2msENyJNB5crNxZcfvbnX9AcCfSmH7BnRZwrjtOc+eMvmahlfN+dD4T7msEvNtDQ+KLL7jiunq9p925L3fyGEW6s9rYXBeGO5ffYEe++6cZBORk4Yz5syzyNmlAzkE0MscKcT9Z9Ol5XEmPTTTanTHulOaOFWg5eErunNT2jHbnGDR3ZnvlVHd6gJY7VmTOu4i50XNJDbLd6UXPu3Z1vUG7ZskGOQebP5y6n6zPO9WviNEInFrX47Efj75wH0GhMqsnlWyVg2N1N0m6v7T90cR819ee/CCdIN7fFEBCmzve8TLd6XFcaMqd7k7r3NnR7vy37u533ljnoidI7tTuP03urOJYoD0WPefdU92pmWvanbp61rmD5k6LedeSdme70zJehjtR13u4dbX1er0nlsx517Ifq5jScojrPTMy3blcYLd+VQT30Vptvei+WDyCEnHCyDk4Mg+i0eMbs8+oMSyQxnw/Jq05diz7xWF1h2+1v7yhnvitJnFLTnbnrg1JuQruvHOnwolFu5PXZvS863VDScJX3CnJHWmb1jE58y4iSO7UzDXtTrt+cajiTot5V0O7c10OyZ2ca/AquePhTtT1Hm5daRktqOs9aO6ckTHvWrRvEbPqes+zXGbulP4jp1+BkiDc5M68UJDi2RfK+CGNxXX55IVl+5HjRe0Lt08V84JCdu5EYbmd3BxCyp12599k5T81h9qdvDKRZLsTyRlfcKcnp26n5zVLRXd6UDF32p28MpxyNxXzovnDV9Z72p080N2JlDvccjfWeUFaYPfcab+/vyUWclD6qXl8yKP/z5jvMbL4ponFI2cUVuOnbWeVOxZ5taofJT3tvnx/zs0lbXszZuOH8I0AbydZ5s5p7rTIHU1+V3Gnlp07LeLPsNouj3lw58pVblm7egaCOz3n3RVR7rTYl6vyu28NV3SnlnYnL4bnNUtFd1rhec2yilHpmgXZnRIi5l1PEPpAwfv8AMGdEjLXeyzp9R4diOs92v5cl707SQvso0atBuDn50ecFB6LbjMs+6kZO80B5P2oyXuMdsl6f04VxrucZns4+4DTzijuKndmn3n1z5JnHykTFucOKDWXqPFm+0WK5g4qgjslZHgL1Z0WsST5Xc2d2rg7d1LjzIg4fnYOkHzbbefKVW5x20Ny5wzuhajFvMvB0lvSeZfah1HunOTO2c/cPkhAdyc3PgVrVyC4UzrvRoHuTov+Sdqp5s5V/Mi6Vd2Zsd5D6UOWO7VortG/4k6k9R6vm3uI6z07Mo4l8StiVhNXFNpFN2uoJyreF/VIdwpHUMfDs4+SfUD5NoTVvqVMRmjfBtDcbY8C4dhAcKekza+7k/KNpOjcOcGdmrirOIjuRHM2hyx3Zsy70v5o4kppd9Jod8rb/Po1y5uM3JFwwjXLF9y5AuHYqOrOjP3W7lzT7vyXk92ZSfaxYfYOdqRBzcJyDLy/qVlhfyGd5Oza3/Vttj93+1nTpjdo+2cF59sk0d+yyBg3tH1VwZ2UHEL5diPasekxLhXd6fGtNm+i3CmJ6TXvSvsTgeex3e7M379v0NzJbYsCxy1o++dJpXm3r1lw3LkC8ZrFox+ngOxOZNqd33WnF9nuFC2wo91pqgh3DH9/de9TnNXn3OGxekwsk+hv2N3MDmLJN+YkuUP5nScW7Vnnn0efItrMJnsbENy5ikmJnz2GEtqddjEi63vERHBndO5YcNJxX8md2eNe0Z2S9jTlPWKccAx7zEHtzhi07hz9TuvOL16zZJNxzoi43oPgzoiYX3en53pPJXeKFtij7lxID3DOQGQtGq/G8Pf3d3i3SHPQvuvf26n55kiVR32eSO88UmJGfYOBe/yNykfffeS0NxtHizuoz9gWY8DtE7o7KWW9831HljufE/wqJiX+7Jj8ijvf88/J7hzNu9o+aEF0J7e8l0s1uYnuzhUjd16X7mIRxZ1Z37ainmcjuvMN99zG2hGV3WndXhV3SurOQP7GpJc779jP32ndOcoFZHdSylVe7xl9rh1z1PUeBHc+QVrvOcmdnus9ldxp9ooYK1YXcdSdRh3IXRuIC0gUNHfYtDxPNBAvImdo5HZjkTte0q7Acx9ELbit+lENC3c+y6JN2hQ83UmZ4LW5gzimOyTuRJx333j0w3th4/3/lrQ797Q717G/5k7qBf5p7uSSfc3C6QcKKH21uvFa8brxxtM9EfNuRXc+y1Ve79GS7c6Kxy3ies/X3HnKes+qDZMFdsvB8RxsDrM70pp4UUgPNos+Pses0gW59QQkjYfSjwy4uRPRD2+Q3Skdh8o3ibTurJo7GqznSRRnofSDwhfdaUm7U8+X3SmNh+zOjNzJAMWdXFD6iuJOazLOr9qdOZzgzgrrPSigrLOg9CMD69xBdKfJAntGknAe9ZE8FmT9bR6Pxz52vNtE/FYVt48eZD025gXnkTnPPqCDcKKI4M7VZ+3OcTyLPmqp5s7Mb9pQ8XBnBRdy8Rij7HFqd9rwRXdaXLOguTNj3vWO50F2H6u4cwXCNRiil95Y99HimgXBndKynD5wQLxm2ZXPAOE4z3anFoTtQRhDRHduF9g9J29NXM5jvJpHft9Q+2whWG4MqlAl467ZV5y61EnC8xsTCAtXHEZ5Y507u3Ka3PXKrV1fuULeMcqbCu5cfdbu/Due5K63xrfV3Un9bEfUceThzt28q1lYi67r6c6oefckd1rMu97ng1nu5EDtk+c1S0V3Zl+zZLnzifW8e6o7V3EsXblri9oHSswoP0Ves3BBcKe07I6T3amJbVU3w50nrvesfrZsi9oHSUyvulHuHLFdYJdccM7gboDmBCkqpudihbY+d4FvVVbTt8i6HiexVicQ75Myq9yxOD61kwxifnClb32HE9mdEX2IJNOdu7KSvt11qrvTq+2vuHPXv6y6TzzcaRXjuvg5U8mdFn7Q+GkEijsROc2d2dcsCP7T1v2iOz0WZDKuWTzcScXrmgWVk9Z7ENx54npPpDut13u+5k7rutnuHBH6DnbqnZb3HYdZ/Gd96rcRLNtcxVv1wYJ3vFH8mcRnfaZsi/d2UKFuw2ycnvs5arHmfVIWeTJDyZcnmhyJwPLbEPfvRr+3+paTNoa1O6mM3LnLJao7JX2wwNqd1Nw5zZ2aPsza3uVOuzMXJHdSoeYO9ZsuVNqd//6eWraaO6n7tZI7vc9vLK5ZKiGdF7yPhRWZ7tydy1IXSFDdOYKzDV9x56j+F9Z7vN2Zsd4jxdKdqOs9J7nTmqruLPEOdkr8VRlJfe9tso4XdSBYLdy943mxG6fZgXYaWfkyQpM71ifyO7Re0cS3iKeNTzlha3fmtJPtTo8+ILr4NHdG4e1OayJyud2J0U70uFR0p/f5TbQDKrnzCdL8M8Mj96JdOeoDcjxqO+jb0es9/BiV1nvQ3InoT0sQ3Pmk8nqPtl2TBfYnVgdT5kEpbTvzmwartjWPDVl/o8qibW4cTSyLtjPjSNpBzh3ttyB2dT3veHP6gRAnsm1Ud2bEyWy73alrp93592edO/5tn+idE7Yhkmr5P3JDu3Md24uKuWPVdlXv7ObdKNqd9nEo7bQ7251W7Xi7s1LuWO8H8wV2q4VXytf3LRjFlG5D5l2iVducfqBugyaOJpZF25lxJO18LXdW2xt5x7vdqY+T2faJ26CJtcIyd7ziSNppd/79WbtzTLvTJ05m2xVzxysOpZ2vX7PstvcEd3pgOVYn5k6VbdjVa3fO22l3tjut2ml3yuJQMF9gtyDyW9PUOyQUMu/CnohmPHtfyOgxr02mO++22535oB/HlvMuCpX73rQ7m//Q7IN3XfRrlsYW9HnXi6rfQK485oi0OxspfRzH02PuC+QCe+SdEMs+IPT7JDTjGfWty0w8+oM25g0P6j7wzJ12Zz5oxzE136LyAM2dzZyoebfd2VyXbh98wZ3NnKrzrhYEd0bXbf4FzZ3cPjR5oB3Hvd7jV/crhL2DnZscle6Iz+7A7uJ4bqP3O7m0feK0bZE7FttBEUrkO7ksBLcbK8/c8cytk97n5uFOr8nxNHdG1LOI3e7kxanuTkvQ3Xli7kjbHpVvd+pie7rTArT892qn3bmOLQEtdyLmXU6MU69ZrKjuTo+2ra7TpFRxpzR3uLHbnbp2rM7Z2p3/EvYOdm5ySB7l4SbP7DNpX7nv85EeMNyDEeWdXNzxkN7Znr1nymvSf5bzeCeXp5h27+TyzB3P3PJ8n1v0hbSVO1dYHRunuZOD5x39diedL7jTkqruRMmdr7iT06eoehaxPd15XX6LTd75b0m7c1233Unj9/eXNFY7ENzpmTtvpLmD7M7s9R5tHCpV3SnNHW5sK3eizLvo7pzVa3f+C2uBPfMu481qQO7Pfn///JVlSvlMJGOK0O+K7MbtmTeU8uiMcqv6NiEwGsPMbw1awJ3gEfLoK+5EyB3uiUjFcX7S7vRB4k50vuLOiiBs52nulJzroG9TBb7gzt32IORRxTGP+NKNB19b72l3+jBzZ6V5901Fd1ZEM26sBfYqO+jn59+/soxKlX7OQJiErZDmjfc+RPxWWDToeZb5rUFrqvS1Sj9nUHO6yna2O+PQPAaMBuo+QsxnK6j9RM2ddmfetlU/12l34roTPXeuq747qVTYF9dVJ2+u61vuROek9Z5KxwCFU/LM/Y+cej3uMKvj8W4mxJ2ddZJIeVypAtp3j1nndfTj6FH1rYh6XBaJ6Nx5u1N7jEjreJPVp5PdyXmvXbtTj+YxYEuijyXU3DnVnZ7zLgdEd1rs0yrujKbdaV8f1Z278qg5ugPlmgVx/E5Y70Ec1+v6xnpPu5NWHjVHd5ziTvcFduoBafUunJ+fn/8/abWMmYn2sSHPdx9VPYDfPB8345RHLSctb13fCs+JXhvL6xiwdif12xN3WcorlSz65gnSI5cjd57gzztXKI/p3p9b5s4X3YlygR3tzoyc+LI7US6wEd1pcX1RxZ1ctPum3WlfH/2ahXoMVMDbndrr/xPcecfJWu/5gjtR13us3HnKes9J7rwunPUerTvdF9hXaC+uZgO1usChxpiRIRjrg8ZyG7wP6FlfvSahUwTlhdf+2CHZL1F9zcgZr4WpVdl2p707MxYSENxZIXessdwf3jfY251/QHan9wKg5f6p5M4Zntcs0pg7Tp53v+zODDJuOHjdxP2qO6+r5rzb6z0yer1nzinrPSe587ps90umO8UL7BbfIOF01HOHcmJb7HiLx0N2MVYHBvfu3bPu7hGU3SNbknZHzPaDdFy4Ze5yEd+kuuNz+0WJyamvzR1OGUqeSfsw+xaYdl9a7p8dXu60PD7eVHHnCs027HJaEkMCgjvvstbfIJn15zR3cuZdSn1qHzzdyT22vT3E3aZT3Lmq0+7UeUhb5lnW252Stjwc8HV3UuYjVHda5o10Xq7szl3uUOJanAtnutMazrZUcufs99nrPZR+7sp7uZPTJ67TpKC403reXZWh5Jl17lD6qc2t/21LTIiWpWd7qNsyejzn/h3lUdLd77WPGD77w4nv+Q0S6bhI2ozKG+o+f5eXlpHkjvW4j+Jz8n/Xx1F97f5EyxtJe5bHhwcR7vTi2e7IndwYlmUp5a1zI2I/nOpOjtskrkN157Osd/5YHz+SmJbxpL75ojutqehOSVuafrU7afG4n4/KnuhOjzalse86WndaXE+v3Il6zeJJu1MWU7res4q5+9zbndQynHJaUNxZbb2HE5/aT607U18R06x53yFBm+TQ+tP8wfsuawbo+XbimFel3dlI6eM4nh5zXNBchdaf5g99HMfTY44LmqtGi6YNBuj7Ai2Xm7NByzeJO3uBHRi0BLtBnwga3NyxADX/Th7zaqDuC9Tcbf5wcu6g5h/qmDd/yM6d7PabPajH8cm5gzrmzR9Q869zBwfUfYGau803QM2//gZ7s0TzTqNZDO47uazRvFOJU0ZTnov3+688Ptf2aZVXd/5Z9wFV5JF4HB/NH1buzKbdie1O6bxbxZ2dO3iMcicLZHfuqOjODJDdqe2D1+cefdKCOO9GY+FOtG1q6oKw3tPutI+PfM1CLZNxzRJBL7AXwCtRLN71pHknl8d27S7CKBdpnu/yk2DVn90JH+eEUNKn3f62fhcmt7z0Ah5B5FZIcwcV5H1DOSZmtDt94ldxJ2fepbxLk9IH6udSd341d7zROAbV9Rp3ZlDRnRq8HPBFd1bMHcR5V0K2O732UyV3fg2va0+E9Z52p338Ktcsq5gZ6z1SOMcc9AK7ZBLg3lGzaLORkS0q6/bRcyd6PKLjW1KpryOs3Vl9PE4j+1hdvY/uxHn3JHdW6msGp7kzu/3mb77mzh2VfJTdV/Rj2ToXs8cDfby/TruzTn5mH6vo6z3tzvMQLbBHSIlzt+t9J+b39/ef+tQ+R30zxuvO4yyu1e8zofZJ03dN3SihoOwblH5Q8OyrxfFpicado/po3+Y/yZ1oztB885DySOkMtHHwBqUfFL7kzuui9ynLnWjjcF347kSbl053p2ef2p18EJ3xJuK6DXEc2p16d351vYfb3onrPVltWM9x7U4+6O78H7mksAEpnDasHy9A2z6LuFa/z4TaJ03fEbf7DUofUfpBAaWvaG7hPgZo2baUdicfD3d6PNbnDUp/UPpBAaWvKP24yXIn2jhcF7470ealdqcclH5QQOkrSj9WRFy3IY5Du7PXe6LaO/GaBb2NCNqdPr/XAv2KmBkWd1IQ78Y0NejcaarSudtk0vnXVKVzt8mk86+pSuduI6XXe5pMsnMnu/1GjtkCe1QSSP5QwoioR4MaPl77xSruKP/Q+xxJZp97vNZY3amtOM5fAN1Dkd+CqJij7U4e7U7/uI2OdmcM7U4ekR5qdzYSKq73dC7xQB+v7PWedmddzBbYn++gfCJ9v8+snjTZOYnrvQ0eWLWNsA2zXOLGeeN5geKV/xmP5WjHXbv/MtvOyP8od1LqUsu0O/3iaNqOdKc0d6hl250xtDv59ahlI92ZmTuzn6PjRLadcc2i7Qu1XoQ7rXPnJHdGUPGahepOKgi5M/s5Ok5k2yev91R0pzaOpu12Z7tT2vbs5+g4M8xfESN9B6X3uyut3/FlWc8Cq7YRtkErm8x3RVXMnTfacc9Y4PLKnQrHcLtTR7vz3zhWZU/PnTftzvnPnrQ7c3Nn9nN0nMi2T8odSZtarHPnJHdGUjF33lTOndnP0XEi22536jgpd9qdWNvArdfunJP+DvaMu09SKvW1MtQkr7Q/Zn2ttA1UKk4WFamUO5X6Wpl2Z23anbackDvUvp64/xA5IXcqbQOVdqctJ+ROuxOLE3On0XHisXeCO6mcuP+sSF9gX+2cZzJ6Jia1nU4kLBD2hzZ3ELahqQmCO6l0nmOBsD/anY03ktxpdzYrEPZHu7Pxpt3ZWIOwP3q9p5HiOe+iubPRY/5HTq2S5Pf37z9uIflmnuTu9c+P7o9hWGy/JIbXASlpP3K7d3G8eT4mo82dJ6PjqRpfyn+LNiq6cxVDU/dLuXOCO7Pn3VFfvuhOpLay3KmN1+5sd2rjeLfvfc0SeexWiZvdljVe7sx4ZUC7kx+33dnulMbNPt/JwnO9p6I7LeKc7E7zP3LKuTjR3j0c1d+1TxlA6l2mXV2L9wJJ3+lkhaT9yO3exZlhfYCO8h8pXgZfyn+LNiLduavX7tTzJXdabwOlzV2cL7oTqa0sd+5od9rFyM6d6O3OuGahYJ07kcdulbjZbVnj5U7rMdG6k1O33SlvC9GdGfPuF9zpPWehU2W9x9udFnFOdmfqK2K0E7tkkKUS1rTZYPH+lpo2VjUq9hkFlLGzuijyatOjfpNPljtRcgfl+K8Iyti1OxspKPux3fktUMYuy50o29/IyXDnKes9nf9yLMcuaj+0O8/AZYF9tkPfv9eKTpI42oV6zTZQ+hJB9GMYFo9O/f7+mj2ONpOOJCYnL6m54/3Ijuak4b0PLPI/4hEly9zhts0hyp0StNvY7pS3h+5OaSwqJ7qT2za3rEecdqcMS3dWmnc9y1GxnGes50Ap7U5e29yyFmNhPe/O2rAAxZ2z67IvXrNIyllT3Z1R6z1V3GlRjhoH6Zpl1oZFLI/1nnYnPZ5VOQ9cFthnO/T9e5Q7mpr63o/jX5d9gkQ/hmHx+MvPz4/JY0mr+p6PyoxEN1usyn5khxrzvU+ksSK21zJ33mgnsSdR7pTQ7rR3565/VdzpyanutGg7Ok41d3JyxxPLYy5q3qWOkbUTrd1uOc+0O/d4HF/I7lzF8HSnZUwUd3pcl33JnVS+4s6oa5Yq7rQoR42DdM0ya8MrDso1S0V3cuNZlaPCOT5TXxHT0MhYTGts8Z6Ymzx6H+KCvm/Q+4dAu/NcvPdh544c9DFC7x8CWfnf+6Y+7U456GOE3r8v0/umPu3Oc+HsQ5MF9syv4FcB5dFSi1gefeHGRHqMGL2tzNzx2Fbto4enHkvWoPQNaYwR+5J9TLY76fE05a1eW2DZJ8u+eHNqnq7aR9pf2fW9YmlAzsnK+8vDnbs2rMvvYiHnDlJb0v2E5JtT+3IqSGOM1BdpzGwnfLWt7P1kETPzWDJZYO+7MnsiH4/anXwhPeYqfWzEOuciczi6LW57lrkT8WiXZZ+4Fy5Ix5I1KH2LdqcmlrY+h3anf1tI7rR6bYFln55Eu5PLqXm6at/DNzMqujMb5JysfM3i4c5dG5ry3At37eP6XCq3Jd1PSL5BmndR3IlMZXda17eIaelOSfsaKreVfW1pETPTnawFdqS7apmgj4Pm5GvVn8r7bEfUnbfVtyNQ7v55SDJ626TteV24oDsjsx0pXu6Uxh29d8+CL+4Hrh+ruxNp29DcyYW7bSi5E0m7EwMEd1rE11LpmsVr+xH60e7c49U/tHkXfT9E0e6042vurLDe4wHC3ETB+5qFtcCuOYjfAxD54nnrnf3uu/Rxvd1FBSXmrgy1X3e5VX+0+0xz8UX5zOrizipfRuO1+naE1THxzEdNflHHljJeo35ox5mSq9z8l36+K4Psznd7z3Ysjtld7qC5U5o7XvvFaj+sPqvkznf7s88keLiTUs5z3uXGzHKnde6M4nnPu8+YEe6Uxvd01d0GtS/Peh79kc4vX3GnRXxOzJPdeWOVO5RzynbnOM5X3Cn9fFcG3Z2SzyRYrfeMYr3j7qjkTqpDKPVGn0nH4sT1nmdMBHd+fb3n5n+k3iiJ+FZT9EmWRfxdPUpcixiccho8xun5mVV8hG/habDa59Sx9cp/CzzGQtsWh+hctDoWsnOn3bmvd4I7rduL9AWnXAbtTl17Vd1pAVL+S9qwOjfitJGdr1Exv+5OTu60O+Vxvu7OyGNRGtP7vNYD7xssmjJI7sy+Zpl9nj3vWtPu5LcRNe8uv8FufSdQg2df0LeT2z+k7bkuvP7MkNzVyty2d9u7n1FB66fkeEMee7S89QLBndbtnbKf0HLwC+7M2AYPdyLlyex3FbF2D5o7TwE9B0fHa4V9ieZOCae5s+q4cz+3xuJ4qzL2WpC2s6o7V6D1Zwbyes+X3RmN5/G2XGBHurND7Yvk8SP07eT2z3J7Vo/ZUEEa3xWSu1qZ27a7c5k97tTc4fTTIh93SI43tLF/Qs1bi0c3M0Fwp3V7SHm0Ypc77U4eFu58f1bVnUh5MvtdRXdauwfNnVWo5s43o+O1wjULmjslnOBOzucoWGxH9ryrjYHAaes9Vdy5oqo7KZ9H5U5Fd35pvYcK6x3sN2h32EbvIPr99XnPEqUPFuW9v41Jjb96zCZbkNdlv18RtmkH2rd9ZlAf0eL0QRsz2wPo7rx/Z+nO6DHxcuf9O+/4UVju1+d8UHne9UZ7Eag5iUZ3p3X7X3CnNae6M2KOkcY/xZ0o/UV055MK5+8V+hgdT9Leqg+7G7WUGO3Oc9d7uKCu9yC7M/uc1gqEccle70F0p2iBfTdg3DsWFZJ818fsby7uZDnbJ6OLxFVMSruWcMcdQTTvOtzJVZJr3tudfQxLcpFzAjErz90u7XGdMe7e+y76m5Je7rx/R3Un97i3/gZCRXdyY3C3IcOd3Pat590q7tS0P6rzBXdat889nkaeQ3Rn9hzDpYo7OZ+/8ViQzHAnt07GvPsFd2Zfs1hcP6/cOapjcT5zmjstqOjOXu/xb59Sx3q9hxJDS7Y7JfEruFO0wK5tdFReczfhKY/7rsfzzua7T5K2ECaFVR9mnz23f3RgS2I+P/ceF8/HN3b1pfv9XYc7uVq0qcX6hFwLJdd247ia2Gb7Ovq4z3DnyJWzicjirm8Gnu6cnezu2qzuzpkr3/PwswwHiX8R3bk7oZb4n9NnSbxsd0bsxxPcmT3vzj6v7k4KFtcs9++rulPDzBlvtzxBdOeujnbxTdu+Rx2JO3cxLK/dPMpzkVyzvD/jupMSE4HT1nsQ3Hld5633eLuz8npPpDsrrPe8P0NwJ2uBXTuhrk6WVifp1PLvnTLbSSv5cu8MWo+Jdb33eDzrShIm624bZdy9v5GwI+LOZSSrMd6JStuOR51V/ntPGBXdOYJ64ko50T3FndTckea5Z+6sfseh2rzrzeyC8PmzdTucz7hkulNLu3Pchue8i+BOTb1Z/dVxvatPyYsK7vR2r/U1ywxvd1LOlSvNY++fKe7cYT13UMYY3Z1P2p1/l3/ypfUeKqet90S6M3u9B9mdX13v0W4va4Hd+i5JxJ0e9D5J62va1dbNEAFl3LMv9BHy2RLKGHvs2x3SO83S9iz4gjt3n7U7Ze6MyJ3oYwIxny3xGOPdxSDlM8sbKVVAzDUEd1aadyXu1LY5qs89rvuaxQYEd3JBnHe5aHMtmnanXd12Z7uz13vmWOcO4vZ8fb1Hu72kBXb0u+xNLKuku3PF4puXKKBvB3r/qDxzZrVN2RMRh1P2TWMDJXc7Z+KoNNa7eff5nyQGGpX2TXUoY52dO+1OLCqNtYU7K4G+Hej941DBnQ0WlfK/13uwQO8flRPXe96QFtg1GzgbOG6SeCYVUl+qc+eKxTcvI5Ee4LtH3GaPCY1+L80raf/QeOZMpdxZUdmdlXLnJKrlvkd+cR4JbXf+cWe1eXeFdDtW+cB91Pgr7vx6zmSB4E4p1cZ6RlV3eixKRLnzlHn3us45Dr6C1fVTu7PXe96fWa33fMGdJ673vFkusFvsrNnAVR5Qz75bvGsK7SBD688IyqNQFq93mAlF+tjh7ucKxxlafkiON+771ih4uvPEb90guNO6PbRjY4THN0s5j4S2O/mfeeHhTot59/kZ5XcrTnGntXvQ3FkBBHdqsfiyCJqrKuTW6JqlujtPmHcpn1tjcbxVyPkM2p080Poz4oT1ntHnJ7gzGs/j7X+rD5F2lmdf0LeT2z+k7bkuvP7MkLwjK3Pbdu/HOmXco7E43pC2Cb1/ViC407q9U/YTWg5+wZ0Z2+DhTqQ8mf2uItbuQXPnKaDnoMXxiuYqpPFdgTyPSfK26rhzP7fmtGsWT5C2s6o7V6D1Zwbyes+X3RmN53kn64+cStE8ws1pQ/KZVduSNizuoFndhYu4q+T9OKxVfLQ7bFyo+cjJnaqP0Vvlv8V4Sohw57u91c+SONzH6rjxJZ9bxeCU09DupLVvEe+03JES7U6PfRlJhju18b1Ayn/p/NLu9In5dXdycqfdKY/zdXdGHovvOtHupH5mgdc12WnuzL5mmX2ePe9a0+7ktxG13sNaYJcO0OhxCOvBjvw2wrvvksc97nqrcaDEtLqrdZfznLik+4G6bzXxn9tmlS+j8bp/t/pMyzMfNflFHVvpY87acabkKjf/pZ/vyiC7893esx2LY3aXO2julOaO136x2g+rzyq5893+7DMJHu6klIu+YER0p3XujOJ5z7vPmBHulMb3dNXdBrUvz3oe/ZHOL19xp0V8TsyT3XljlTuUc8p25zjOV9wp/XxXBt2dks8kWK33jGK94+6o5E6qQyj1Rp9Jx+LE9Z5nTAR3fn2954a1wG4pLmsJRoI+Dpo7rJETFxIe2zaKef9u9Zl3H3Z4nEBGb5vmBAh9+6OOQ/Tj3cudaBdfX9wPXD9WdyfStqG5kwt321ByJ5J2JwYI7rSIr6XSNYvX9iP0o925x6t/aPMu+n6Iot1px9fcWWG9xwOEuYmC9zWL6StiEC6u0MgYE8tvNHydzJzmtG3Zzy/nTtaxUyXPIslyZ2b9k6iS0+1OGzy+SWld1hpUd2ZQ0Z2o++9rOf31axbLbW93fosvXrOg0u6MJ8udmVTpJzrex454gV36CMBJiUF595H0MZmTsXpPknV7ozqZExenbcojXzMyx/lEduOA5k5Njkv71O6UUcGdN17u1D5mOiub6c7mPyTz7tfcKY3z9VyVzjFWcxwHNHdKzmm+SLVrFgR3Vrhm+Xp+V3KnF+1OX6q5M5NK7vwy4gX2dyJSB/v9vqBqrN7dZPX+H0lfOGVn/x+FxThx+s0db8mjzl7fGuHuH8mj1pxtXcWuMjl5n/TtxgHZnVG58zV3Wu2vCu704I7LcXMld1bB052e8+5z31Z3pzROu1M2TtJ59yR3cs9pqHGpVHHnidcsXHdWmnfbnTTQ3enF6pqFUodSz8OdSOs9VE50p2fZ66rhzgxQ3Gn2ihjJYCPvoBkofeZK4Vn2eVB6icX7LpnmnUu733vnslfZUR3K9kpjW8STxpGe6Esm0xuvYx/JnRm5E43UnXc96TfF2p1y7rjV3TlzkgW/v3bvE8xwZ9S8e5I7M+bdL7hz1RcJX3CnZ9zq7rwuvNzxcifivGsZTxpH687376kxUBZVUc7/OaBem3HK9nrP+ve93sPvD+J6zxMUd7IW2FFEfRJZi1Xei4xWk4z1ZDWLV3HypxC1vdH7W3qin7WfK7oT/ViJdqd2gU6a2yg3NNDzwRqP7fV00s+P3fsEkdxZEfR5l/stO2171dxpzUnuzBrTyu70iFcxdyi0O23qaeujuPMkkNxpWa/diQG6O6VxqrnzhrXATmmspfwHyligHeiZ+69zp7ECLZfanTwy3Yn2GHvnzvkg7T+kvjR82p1/aHfy6GuW5stUdKcX7U4e7c6myQE1j81eEXODJpBMOGOBkiDWk2rF967NQNlHN2j90WCdU+i5NCKyz+i5k+lOz7vl7U4M0PoTdTFb0Z1o+wqtP2+83OmZO+3OOZH5hj4WI3ohcA7adqP1501Fd1pzkjsjqTgWmfMuOmjbgtYfDae6843ZAvtJOz8D6wTx3B+rvj7b1byzNRrtQXzXt3431Sqe14VFxrH8ft+VdrulZGx71njvqOL0Ko65Lv/38WVgdQLU7lzDmXclMbRkfAvZa96V9ocTB4FKPqrUVyqo+XbaeZCFOz3xmmu+cs2SAYqPUL5g0vzHl92ZkTvtzn/joPOV9Z7UP3J6XXUS4rpq9TVrkny2690Hy/1h9a6mWZxZX2e/176z9B3Hu44FkblD6QN6m94+shyLSu78Akju3MVpd/LaRehDdJvWuWPVLwvanVic7M5dPE8QvIXQB886o/onzLszvuDOSgvilfaHtK/tzrw+eNYZ1T/Znd5UzR0O5q+I4VI1IRo/qBNbpf0x62ulbaCSeRJV6QROS6XcqdTXyrQ7a9PutOWE3KH29cT9h8gJuVNpG6i0O205IXfanVicmDuNjhOPvRPcSeXE/WeF+QL7e7Cpgy+tRy2riRexDVqs2kbYhltCVtsQScXceaMdd+3+y2wbIf+5bbc7dbQ7/41jVfb03HnT7pz/7Em7Mzd3Zj9Hx4ls+6TckbSpxTp3TnJnJBVz503l3Jn9HB0nsu12p46TcqfdibUN3Hrtzjnm72B/36Gh3rGh1hsNAKWNUZnZYHpvgwdWbSNswyyXuHHeeArAK/8zxKsdd+3+y2w7I/+j3EmpSy3T7vSLo2k70p3S3KGWbXfG0O7k16OWjXRnZu7Mfo6OE9l2xjWLti/UehHutM6dk9wZQcVrFqo7qSDkzuzn6DiRbZ+83lPRndo4mrbbne1Oaduzn6PjzEh/B7ukHYsk/P2t8wc4v4bXfrGKK530JVTM0cw+93itsZrAK47zF0D3UOSid8UcbXfyaHf6x210tDtjaHfyiPRQu7ORUHG9p3OJB/p4Za/3tDvrsl1gt5KbJRY71DopJNuI9JjICOv+oG2flBaKDafn16nutKbdua+Ptn1SEOZddDK2qcdxD6I7JVi7BW0et9g+xOMB0Z1o44TWn6q0O8egu1MLojtPWe/xiGEJ2jyOQra7stu34ov5xf4Gu+TdVpIEqfZOIskjHp4HDnWiXPXX+ps3nHia/Z/xmI1121HtRMd+Yv1YlEW+ooxru5NWJ4J2Zwya3OHEQTnGZ1jt6+jjCW3e/YI7JXUj591Md0ri9bxr305k7Ax3WvAFd6LmTnV3eo5ru3MMujurXbNYtJcRu9d76HzNnddFWGB/bsTsMZtZJzUHmJWkrS7atXUpO9xSSNR3C0VIUHIgROfOCMmJ5/t4kbbHacea6DumSHdovY7Biu6MantXN9qdbzLcecdePSKLkjsjtPNuu1PfHlJfvGNH5c6uvoZodz4d40WmO6+Lv9DwRXdycuc9H3les3jT7vz38+h5t93J7xf12l7bJro7q633RLoTdb0Hub1q6z0I7kRtL8OdI9TfYJ8tHO3qeTDqR8TOpGzbaqEEieg+ouTOCK3UuLnHuRFRIZcyQRyfau6MANGd0rbanX/Q5tPX3InUJ6S+3HD2oWXuRNSXQnVnJBXciTTvjkDJx9G8W8ENzd+guNPqm/IWnOTOSJDcWW29B3H/IvYpC6T1Hko/kL6YmA1qHrMW2H9+fsR3AE5OBs0YZI4L96C1umPMRSsiLhGPUXHhHHfeYyBpQ9snbu5JJkvvRy0ruBNxkXhWzmtcpHG59ahjHfXNoAhvZFDBnZ7zbhV3cvqEeD6J6k5NXatvQ1vX07gTcd69rjru9ByX6u6kxtDE48ZAcWf2vEvti1VdC3dmOqGSOyORjoG3O3cgrPdUd2cW2e48cb0nAvY32L1AGIwZGX2LuPtpIQ/NIzIeWFyoodyhl7b77v/v76/5hXL0NwWsc1Xy+ZvRuCK4QlPOer8ijAeCl0a83Zndz4hj7Lps85XDSe7czbsV3Zkx77Y7/23TOnc8QHCndZvI7pS02e6kxWh32iyQZLvKw50e24Rwfpx9rrsCYTw85rSK6z0e7pTEqLTeQ623i8H53IIT3Rm6wL7qnOVgWMtpJZWoA896fDz6oOkj9w6a1biPFlQk9ShYbIOkXcs759xtuD+X9pvyefTd1Wf7USf1Fu70+uYNtc0vu3NHRXdy+3EjydcT3UktL5l3T3Wn9bx7qjs1/fCa0xDduSPymoVCljslfN2dntcsX3Bn9jWLhArurHTNQm0HZd617EOWO5990Hy+qtPrPTSqutNzvYdLpjtFC+xWB6zlosJTsk85jeRrOYFm7nQvcUrxPDGxzJ0nq28deN9Bjswdrwkq86aLtA2rcY/cD57ufP/O6wTzy+7MvKjzcud10fNm9jsqJ7rTqrxmrNud+j4gujPyhh2VTHci5s4X3RmVOwju9LxmiahzXb7nDte1PgZOvGZBc6dnjF7v0fUhy51UKl6zVFzvmfE1d0rJdKdogf3n58f8ruGO3R2a2R2/0e+ld7Gs+P3d/6GQe4ylMWZ1n7+XfCtkd5FAic9pV/rNFerno/yg3oW7x8M7f+743H494eT8bJtW+9b6Ivid36sTLGqbu9zcxaR4hHpcI7nz+TuKZ6N9+STCnR6M+vR2J6VPSO68rrlXVjn2LNvulI37bt7VLthluHPXp7ejInIH3Z3WPnh7aJQHFd2JOG+hu/PdlmSMORfo7U5avEru5Pbr+Xvudbr1+Q63rIc7tdfTFufC2e68+/D+ucJ6T6Q7d7+XHB+7eZcbH8GdFdZ7ENxpPe/u4mvXe3bxuf20cOf/Nn1cNq6FM+F4ntRzYltcYFhss/RO0p001AWdXczVJLDqg5bZfrC4w4aQk6N2LPslyS3r3FmV2Z1cSMb+OSFJcoca36rcCi93Wh4fb6q4c4VmGyxzWAOCO7llpeN+qjs5866F61Dc+Szr7SHvOY5DpDtXddqdOg9py0jKanPHq1/Uul93p8W8/C57ojtnn1d25y53KHEtzoUz3WlNu1MeU7LeQ+nnrjzCNUv0eo9leck4nb7eQ4mrdWfqHznlDhr1Lsrqbgf3TsybjAnC+o6Z5TZ4382THhg7JHe0Gt+bJisk+yWqrxk5o3WnpGy7096dnrmD7M4KuWON5f7wHOvranc+QXan53Fh7adK7pzhec0ijbnj5Hn3y+7MwNs3nN9TaXf+y2nu7GuWOb3eM+eU9Z6T3Hldtvsl053uC+zUzlDKUQbqecfFMmYmoz56nyRSY51w0nhd9MeE3uVRy0nLW9e34r1fPHOaS/bkT/Uc9bGuuyz1ETVt3zzRutOSkTtP8OedK89vDOzKW+bOF90Z9c2oHdHuzMiJL7vTc97lgOhOi+uLKu7k4nUT14JT3Ok1n2WVox4DFfB2p/b6/wR33nGy1nu+4E7U9R4rd56y3nOSO68LZ71H6073BXbPxy92j4xIHimhtpNN1kkiygWXFu0jKNZ57XWcaPc9Su57TvQo2/gmOnfe7rR8TAuJrD6d7M7dvLuqj1ZOWt66/oqsG+xvoo8l1Nw51Z0oF9iI7rTYp1XcGU27074+qjt35VFzdAfKNQvi+J2w3oM4rtf1jfWedietPGqO7jjFnawFdpSD6yRWY1phvKsewKdTIXc4VMiz6sdyNaqPt/W3JxobKox39QvsNxXG/CS434xCo92Zx1fmXW7ZLCqM+UlUd+eT6sdyNaqPd4WcbupTIc8oxytrgd16oz2+SfAs5/GoT+T7sXaPfUj6Qnl82QLvNjImo1GbmZPiqm0PQUXkTlRuSo+d1e8px7IVWmftfuexH6q706MeZd9k5o4kpiTfOPEtONWdqG1o3Bm57yllT3Qn5/hsd9pSzZ0nXrNUbqO6O705xZ1fumaRxOz1nnGdU9Z7ot2R6S8Ed/Z6j9ydNyaviJFeXHBODCSPVEgfw5BefL/rWV10zdqUnFiNHl+m1KOWeSYfN9b9/+9/R0i3YbWPJHg9QuV9YswZoxtq7mhOIncnGdTPdrkz2w5OznFEO6OiO6X7pro7LepR9o3k25kZ7pzFjJ5335zqzt1ndxtfcSe17V1fVnEquXO0/6jjQvEQZZ9VdienL7uYnM9Q3Wkx73KOJ3R3rvB0p/U1C6ftNx7upMb7ujst5t3d7ylEu3NFu3PdHtp6zypGBXdGrfcguLPCeg+lTKY7TRbYV5KbbcBu0DIfEdi1vUrI2c/e22N1gFLqUe96c2Pd///+lwvnrjynDcnYzES3E6B2P+zKcsaI24bFfpP2zzp3rE6+KDEi3anpu2fujH6u6s7RfrLclmx3WsWllqEeD6e602LerehOi3mX0idpWxwicsfLnaNYJ7lT285p7rQ+v+G0LWnD253aPlDqUz77ijsz5t1Id0rKUOpZ34BZtRfhTm/anfIy7c5e78lc7+GUodSzdmfaHznNFKoWxL5HT7INjZnorE5MPPhy7ljfSdZwojsR8RrP3k+2VDge2p1/aHeeT7uzBojHQ1+z/AFp2xFzpZmDNO+eCOLx0O78A9K2V1zv+TLe7nRfYI/G6jEdSzL64XEAe23H72/8+7VGfbAoE4lXny1z5963HmOnfURpxFcmvtGYvd2JkO8IfUAGYT+9+7DLLQSquFPaj4zYXu6skDto7kToAzoI41PRnV5Ucqf18SXZ9r5m8aGaOzOuWVDGB3G9J4Mq6z3tTjkV+hyx3mOJtzvd38GueT8PNcbz89ljB5wYFn1+/vzzI38/FbVPHuP+/tlL4j8/4/cfRuTOjdVjKNJtkNSj9pk7jpa5c+9bj9x53h2OyH9KeU17kfl/j93KnaMy0vZ2nO5OD7Ld+dxHu3lX4k4qJ7vz7oc1ldzpOe9K+lTBnTMvWFPdnZZtn+bOyvOutzs9511q+exrll0caX+o+6zdKYtjcVwjuPO6MNd7NDGp9auu9yC48+6HRRnE9R5u+9XWe2ZtS/B2p/s72KUnh7v383BFsJMvpz1KG5SYHHb138KSthf9KIs2d7gxKWgWVUbbQDloV9uuXeThHoNVcsej7fe4U447DWju3MWTtEeh3alrL8OdlHlylzu7cdfOu+3OuLa17rSedzn1R/W4+XqiOzVtZ8SRtBflTq7rst3Z825c29w5q6I7ufF2tDv/rVfhmoUb87p6vWf0ebvz33oSj5y23kPB253eVHGn+TfYJexOFnaC3PWBctG8a1ObPNZ3w1ff2vDEezus2e3X399/H2fhjqP2oLQ+qDPyOxrvvLmu/WRp0QcEd3LijcbE6m7yjKrurDYuu/i7RZ7r4uXW6HfWXmp35iBxp/W8y61P2a/tTt/+RrUTPS6jOQbdnd7nN1HXLNWwPqejtMHFOvdG8aJd+QbdOSMoc4yWjDnli+s9CO6MyFlLKO48fb0HwZ3WVHWn+TfYV1A7f8e7P5/Fp97Bo9wdkrS5irfqgwWUOzijA+3+/QjJXVst0njUbZiN03M/W20T5c7l6N8IuHf8NDkSgWafzY4J7WK0hCx3Uhm5c5dLVHdK+mCBtTupuXOaOzV9mLW9y512Zy5I7qRCzZ1Rfmtod/77e2rZau6k7tdK7vQ+v7G4ZqmEdF7I/KZgpjt357LURVdUd47gbMNX3Dmq/4X1Hm93Zqz3SLF0J+p6z0nutKaqO7cL7LOOSIi+UxQR01JA2nEeLZBw+rcqq+lbZF3q9nLGxWpSvT9//6vF4245t2+I+cGJOzpWLI5HVHdG9CGSTHfuykr6ZrGQjOBOr7a/4k7NSbln3Sce7rSKcV38nKnkTgs/aPw0AsWdiJzmzuxrFgT/aet+0Z3c+cWynKQPs7oe7qTidc2CyknrPQjuPHG9J9Kd1us9X3Ondd1sd47YLrBb3vEZxZbCubMovQs5wuMgmMW2XiDZ3a3VxLaqS12s8LyxoV0wiWaUN9GLa5rc9cqtXV+5d793aE94OFi6c/VZu/PveNTc4cRe1anuTupnO6KOIw937uZdicOy6nq6M2rePcmdFvOu9/lgljs5RHwzUZMfqO7MvmbJcucT63n3VHeu4li6ctcWtQ+UmFF+irxm4YLgTmnZHSe7UxPbqm6GO09c71n9bNkWtQ+SmF51o9w5AuId7JJ478HgnCxJDkIuFndHtAc6VxwZd68R5CbJD2Q8F3Y5fUDHuo+eJ6eW8TSubHeOf2537vsQkTtaPNxZwYVcPMYoe5zanTZ80Z0W1yxo7syYd73jeZDdxyruXIFwDYbopTfWfbS4ZkFwp7Qspw8cEK9ZduUzQDjOs92pBWF7EMYQ0Z1m72CndJZ6p4W6s6wfz6L0YbUNVo8v7GJRy1C3YVdei3RcvL7RY1FfEu8u8/5XGzcSj30SvY2e/ZR8CyHDnRKs3Un97AR3Svv0BXc+2Y37qe70iBndnkc9i7jU3PmaO7m+oZZBcecKa3d6XrNQsXZnxrxrXU8a06OfFecjiTsRFiup9dudZ7vTc71n1eYJ1yxS2p1/l8m8ZslypzSmBkR3miywXxftYLW+0xL96Muo/vNnS2FJxtMy9hvNQeHRT+v9GHE36y7z/lcbd4W1zDz2iWYbI78p4LW/MtxJwfqbKp7HMzdWtDulffqCOzltnupOaR80+xnNnV5xJbmjpYI7pb6h3hSWxH6jcaeUdmfceYxl7Dee7oyMOeMr1yztTrxrFkms09Z7LNus5k7r9nb12p00qrqTSjV3mi2wZ1Pp7hcV7R1bbv1V3YiL0iwkd5zRufffadv1Bmn7qnpmhvRiAIF2ZwztTl88+4CwfTdVPTOjsjsprBaK2500TnYnAqeN7Zt7+5DG3IJ2Jx1NXWkdBHq9R1+/593zaHfSiXDnMQvskXe/opCcQD23lbvdmrqVOXFbT9wmVJAW5Kypuk1V3Fn9ZKhqfqw4cZtQaXfi0u70pXp+jDh10ReZk/OoKlXcWXWce71nXl9SvuqYSTh9W0/fvifo7iy9wD56P9b7roTkHVpoZB4wVu+Nshx3i1gZ78M6Beq4ROZO5r46YUIbuXNUxqKdSND3TXT/vPbhbt5teLQ769DuzKHdictXrlksz4W/SNT8hOjOL827Hvuw13vs+bI7q+UKmjsRc8cDzvEZusBuLT/K+7Ek79BakSFwqzbuOJxtsHpvlOW4WzwGI+mz9SNc0Y+EWfB+vAold1ZlZuNufVx54p07UccwNXessBqn3b/a+J60O/fxPEGfd9HdiZ477c51/XbnH05wp6RNbZzq7sx6dB8td9qdf0Cfdy3b8HBnr/fQ4rQ7aaDPu+juRMyd588Z7hQtsEs7ai2/XRve8TVtcMbQ6rHLO07GNmiJyB1uH7T1teOOMAaRfbD2jlXfIx5rtx733clpu/Pv+rt/uUQewwjz7pfdaT3vRoLgTqtjmNMmJ977d8julLR5gjtRcmfXRgV3StqUcqI7M/qAkv9fuWbxGnerBeOvuNMyvqaNE9zZ6z26+u1OHie4U7TAXuHkBh3JGHLu2kRwUh5kPnLCJeLkCJGMfLMev0rHDGruVHGn9m5/FVDzZMRX3ZlBu/MMTnAnau5UyhPrMay07YhkLNpFcFJeoF2znOTOSpzgzpPy4CTHPDlpu05wZ+l3sH8F7eNm0picchF49oUyfkhjcV0+eWHZfsZrGHafc/tUMS8oZOdOFJbbyc0hpNxpd/5NVv5Tc6jdySsTSbY7kZzxBXd6cup2el6zVHSnBxVzp93JK8Mpd1MxL5o/fGW9p93JA92dSLnDLXdjnRfLBXbrix/pO4k83oPl0ReLBIxI0vd7lVZkLrKt3jmVOWFIY74vEDTHjmW/ODzbeOfGan95s2vr+ZhQ9DhpyszKZrpz14akXAV33rlT4cZDu5PXZvS8S/VVBF9xp/RGgaRN65iceRcRJHdq5pp2p12/OFRxp8W8q6HduS6H5E7ONXiV3PFwJ+p6D7eutIwW1PUeNHfOyJh3Ldq3iFl1vedZLjN31N9g5wyq9H0+Hu8B8uiL14nfrJ40cRAuciisDo6MCUP7Lqd3fc2xo+mHhlUuouWVNne842W60+O40JQ73Z3WubOj3flv3d3vvLHORU+Q3Kndf5rcWcWxQHsses67p7pTM9e0O3X1rHMHzZ0W864l7c52p2W8DHeirvdw62rr9XpPLJnzrmU/VjGl5RDXe2ZkunO5wI48gJoTK7STsidSgSNvkwbkHEQjc6wQ95NFn1Z3cL3b9qLdGeNOae5YgZaDp+TOSW3PaHeOQXNntldOdacHaLljRea8i5gbPZfUINudXvS8a1fXG7RrlmyQc7D5w6n7yfq8s+w72Ed3u0bSGZU5MTne20R5lKuapC0fFaNsf7Xx2fHcHu2jfNXy5+4vd59/wRXXRffF6lsGVaHuY+6jfki5g+BOpPHgYuVOqoeQaHf+AcGdSLnzBXda8Nz/X3PnDOk1C1L+76D2lzqnVNp2Cpzr1NPcSaHn3f/o9Z6/+cp6T6Q7K47P7nMrd56aP97uZC2wWw2yNMZuMCiPw3HFRO2LFEkcSp1VotyTDuWOKNJB9Zxkpf16Pm6z237JxTSFyBjvO3L3z9JH+Z7173IIubPrw93f3SIHRbDSYxbJnW92uf7c37v9ru2LZxxN23fuRJ+wW4wXgjufZOZOpjufHlr1pd1pUy+i7Wh3Wj9dEEFld2rivJ1xgjuzr1kqunPXL8qc8j5+JPOYFK8YEndK20VzJyV/s69ZLOj1nj8xst25i/lld1Zb74l0Z7V597py3XlDWmAfDbIGaQyPk/PsvkjicOpQD8xZTLTHkrTC2MUd8T5oPduSxOAIRNs2Uu5Q9gu1v5z+SY6/iu7c1Wl3zstZYj1eke60rKOpN4vR7sR3J7eeRdvaeie6k8Op7rSuV9GdkptEJ12zWLqTA2c+QnHnLIZ20UPSbrYLqHGy511NHcs4p633tDvbnRYxMtxJaaNS7ni782a7wD676/vs4Luz1oPrEd+7z6v2Zt8kHP286hfnzqbkrtO7faqUdmVWuWPNKD63zfd27+prt0nbv12MqBslu9zh5MyIt9A120W9k8kZ11PduWvDMz6iOym5ze3LrH41d1rXt46P6E7OvCsF3Z3UuLsYlnzFnbt+Ueq3O2vnyvvn2T7m7ivOvCshw50RnOZOi/5Vd6f0HHXlTm5fKOfR799neggpJjV+hjuR1ntG/aPGsKD6ek8VdyKt92S4c8R2gX12QDx//5aF110UzuBkxKS09/z/3bg973g+kfR5Jo1VjFn7lPirMqOx8CLibuh17eUijS8V4awPv79xj2lbfHuLk39aaVLbmMVY3SU/yZ03X3Pn+2Jj587Vz7vP3rlzmju9Tq7R3Um9SIpyNKe9SHdS8V5Me7bxFXfu+iVx21fdaem5aHfOxms3746442kXz3dkuDOqnSrutFjwp9Sv7s5drs6Od8256/uz3TULgjsrrPeguRNpvUcTw4KVO6n130Su91RxJ9J6T4Y7R7Dewc490eVAuZu3a+N9V4Fyl2F38Mz6woF6V4aK5YSDgPfd5Ig41hc/EScCUUTdqaaQdexUdOeOr7lzdLERwao9FHcizbvI7kSad7kgbr923m13xnCiOy1y510v252rNiifWdbxiGFFxjltFXdy+plxraaNh+Qwzxjo7kRd72l3rkF1Z7X1norujCb72PmfunUjqHfzvGNYxrGqfzqICyHSOJlto3LStiDS7vwuKO60+IaTVw4255OZc+3OmliP7wnubGicNL5I7pSCch70FRDciTrvNmtQx7eav9qd+LD+yCkVSnmkuxxcZn2vvE1P3o/zUsre/8/9JtcutkXurWJ45CqlPW2fuOW57Un6INlfszYlufQeW49x5JZHiOkJ6l30bCTutG7f253RIB5vVu4c/Uvt0+6bXO1OeUxPLPqinXNnMUf/Tylv3T6lDOo3sKrlGsLxVtGd77qnXbNIY3q2Z3nNYol23q1OuzMvphSE9Z5T3dnrPesY73ic/lRwJ2mBnXtHglJe8sgGdSA5O17SxqzvVnduIhJm1Qbncd73t29+fv6878riLrdF7nEe2RqNC7UPlO2mjG308cYds1kMyf4atfn7++875zjbuBpjSt5z+6st4xFT6k5O2Xs/WVDBnZwJXeJOamwKEe58o8kxjzGlEO3O1fyinXdXP1PjtTtruVM75z4Znb9Yj2kVd1rFkV6HcMpWdSe3PKo733Wjr1mkZTxiUvOYMr9EXbNYoHUnN++tqe7OjPUebhnvmCvQ13uy3Im03lPRnUjrPSOy3cl6B3sEz86+B466Mzk7ftVG1p0RrVitJg1p29YXPBFYTRLveNJ+WFF1H3j12/IiAQ0Ldz7L7sbByyEaPN0ZMaEjjukOiTsR5903Hv3wnHdH/29Ju3NPu3Md+2vu3PXpVHdyyb5m4fQDBZS+rtzJoeJ1442neyLm3YrufJarvN6jJdudFY9bxPWer7nzlPWeVRtwC+xIokfqCwfKHa7d7y3ath4/T3lY91UaD6UfFE7NnZOOe6R4FdC6E3XMvuBOa9qd/9HuzI/3BPUi6lR3elIp7zzh9PtUd3JB2deVchjVnSPanbFUHcNe7+GD4iyUflA4IXei3Qm3wN74MLvbVmlSOeFuc3U6d5qv0e5sLOjcaSw4IXcqbUOTT9XcaXfmc1ruVNqGJocTcqfdmc9puRO9DWEL7CcfINW2Leoul9W49B17HKrlzohquVPNLxyqbdsJuVNtG06h3dlY0u70pdr4nky7s5HC+YYvKtVyp9r4cqi2be3ORkrnjg7xAjvnj0U839OkxXJHWMW63yEk/UMbHiC8P362z7njRH1v5y7uLkc13PWtbypY9UvbNiWO5bussqSYcWxGuZOL5nih5g6iO5E50Z2aOKM6aO6M4CvunLW5ygNJPC1R7qTEo7Z7Opxx2s27p7pz9tnXr1mocM+TNfOF5zVL5tzX7uSBdGxGupOL9XoPtR2k/WNNu7POeo+VOzllVnzhmkW8wM75YxGWgye9Gz0qY90v6R/aoMBNBOQ7fpbjxDlIV59rx+uuv5t8KTxPUCz6Jc0drgCRc44Kdxsk+zrLnSOkFyGcY41SFsmdyCC5k7uIz+mPtA6iO79ChDtnbY72lUU+rUBwJ7UMp9ypcMbJen57g+rO2Wdfv2ahIp13Je70vGbR5JNVv7ifvT//ijszrllW5aLcuSJivWfWXrtTRpQ7pXVG9RHXeySfvT/v9Z4x3H1t9g32TKolg+cE592Pyu1+lZNyR4NHn6sv+LY7a9G5E0u78z9Q3IlEtfyvmHeWIG3/F3Kn3fkf7c76nHSt3NcssbQ75VTs84lU3A8I7jT7BvuNxZ31O04W3o9tUseH86jR+xETyjas7g5T4kgfhVrljgWWjwxxY1l9s0TS7uhnr8enKNto/biTNhclfeZsg0X+Z7jTKveldThxUNypjdPutGn7TVV3WntHUx/BnRnzrrRdizjSOpw47c5254qVO6W5wyl7gjtHeJ0rz46VdqccNHdycudr7rSi3embO9R4I7zdqY2Fst6z+70k1hPU9R50d5r/kVOrO2/SOM+Nlu7w7G2Y1ac8/qF91OT96Mszzns8LR6FsqxvHecdiysijTS5QrPcFx77QXIs7rbpvf3c8ZO0yamrbdszzmis2p1+7tT0j9O2Fst5t90pq7ebdylt7vqB7s5qufNldz7ztd3JJ9udszje9a32Y7Y7LbCcc6q50wKE/TCqH+1OSpvU/nHa1tLrPbnujFrvQXPnKla7Ux4nar0H3Z3mC+xSrO5gPjfaKnEy766OiOqP1YV3BSjbxt1+hPIrSXnjnTurCytPTvDB7sLy54f+XvQVJ4yVhJNd+eadK5JFi1F5zr46yZ0R824ld3rOo5Jt/6o7Z1jnThV3Wn3zMdudEZzszih2C14jKrhT04fKoM+7nvR6D512px40d3qVz3RnX7P8C2mBPeJA4E7sWjwv3r2J7I91WxUnL+tHo6zhbANK7liPYcZ2SXLHg2h3cviyO61B844EtHn3K+5c1Wl30jjBnSc4BIXouaTdSaPdie9OlG3gcMI1CwpfX++p6E4JJ7gz20NV3Hn6eo8E0gL7roHRYzrvf3f1KDGp/aGw+naats+jbdc8PiLBYtwt+rFjNUYeufNGcleQI5JR+5TfWZ8QWJ3QIOWOtG3pZ29GY8p1VUV3ctqblZt9dpI7Ldvdlc9wpwTpSdjqmOAcN5z+cMq8+6L97IvulJR5t83Jk1UcbX+oWLqTG5PbjrY+ujs9510Lot3JPXcelW936j97c4I7LbahujszcseifsVrFrT1nnZnHXdmr/dUdKc3Vdxp8oqY0WM673939Sgxr4s/WNTy1EeNKJ+Ntt3ycRBKUliM+66shFV8TtvWuTOCWn6WR6Ofqb/zepQKJXci84rTNqVfloswke5coTk22p37zyjs6n/NnaN+rdw5KtPujGtb604uo9zh5MkISe6guJMSk+sYLqjuXJXPvGYZgeLOqvNuu3NPu3P8WaQ7UXKnqjt7vYdW38pfEiq5s9p6D7I7OXEkoOTOrj5rgT3yDsUMymD9/v7+f18jd7oEyZgi9Lsiu3F75g2lPDrau+LNmNEY7o5jBHeu4E7wCHn0FXci5A73RKTiOD9pd/ogcSc6X3FnRRC28zR3Ss510LepAl9wJ/ebvxlUHHPJuCFs59fWe9qdPszcWWnefVPRnRXRjBtrgT36LruUnx+bPzKlBelia9aXzMc83mTnjjRvvPehND7CMZBFdC5ZfLslO/9vEPIm051UJ0a5s0rutDvlIOy/GzR3WtfzjN/ubHdKQMk1i9z5sjujOc2d2f6s6E4vKrkzO2+uC8ud2fNuBU5a70E5BrJAc+eNyStinnx5J79BGotZX7wfEeKANF5NbSrmUsU+e4HkoXZn4w3S/kPqS8MHyUPtzlpUHIuKfW4wQfIQ1Z2ZIPUlG6SxqDDvNo0VqO40eUUMyl0w7ju5IkEfuyyQtx+5b00t0I9/lH6MQB87bypuP3LfmsYS5Fyv6A5LKm4/St8qjl1TC+Rc+nr+o29/r/c0llTbN9X6+0VMXhGDchds1A/kvq1+/+TkA8lz/2jHDSV3ojg5z7hwxkLzaCBKjqH0Y8TJ7kTNnXYnD/Q8i8Tanegg53q7E3veHYHSt6jcQc+zSL42Ftpc9xyvdie2O3u9Bz/PIvnaNQuyO0+GM27mr4jxwjMZ0BNt9gcaRv+/+h2l3qrO6A8prH62GFdujN0fraDE405w3vmjGQNOmdEfjZnV4b7LjdsnSp5x26QcM/fvOZMX+sRsfcKG7ssnEndasstppEXydmcdd3LcJvmmV4Y7PfaVBm5+ZrjTe4HJ+/xuRaY7ranoTg0SB7Q7Y+ZdSXkuUe5EnT+s3Empwz1m7v59FWRvXpfcnSPQ1nuquzPqmgXBnajrPZXcuV1g1ySTZSJ6TgiWsTUHBqfus8+cO7m7eqs6oz+ksPr5/ZlkbLj7xuKPiMzKzA62rD96MRtPzTben+1yivsuN8rkx4lPGXdOblJjckB2p1fuaEBxpyXSY0azfe1OWX9W5allotzJcZvkm14Z7vTYVxpm24Tkzt0F9giOW7h5YEmmO61BdifSeUq7cx+PU+ZL7pSe7yC7k1KHe8ysQHSnNV+5xnv/HmG9p7o7o65ZENyJut6D6s4R2wV27sRNqXsylG2eLYBQBEX5jPK5FE67swMqM3c046I5FjRIxKrJj6gTpVW+o+Y3py6yOzXxMxchItzphfQmwPPndiePU925ahehD5Z1K+WONKYWS3dSYkTT7rSNSYlPaYsz70rb8AbdnZr5qN25p7o7Lc9XEN2JzOnutFjv8cbrfLzdaUvF9R5tbom/wa4Ry8lwJqLd3WLNnTkvuXHanR1QmbnjIamsxVDJiTXl86yJkXPnMCu/OXWR3elx4qEFxZ1eWHyTqN3J41R3rtpF6INl3Uq5I42pxdKdlBjRtDttY1LiU9qyXFRByC2EPnA+233e7txT3Z2W5yuI7kTmdHdarPd443U+3u60peJ6jza3xN9gp2A5KJ4Hq2VszjZb3Q0f9X+2TZJva9zlfn917+SSjA1331jcCeOIdTQm1nBFr9nG+7NdTnFPNEefc+5ojn7mtkk5ZrJcYFn3DWXsV2UlZSSguNMS6TGj2b52p6w/q/LUMlHu5LiNc34giX//TrsvPfaVhtk2IbmTswByw3ELNw8syXSnNcjuRDpPaXfu43HKfMmd0vMdZHdS6nCPmRWI7rTmK9d4798jrPdUd2fUNQuCO1HXe1DdOaLMHzn1lDf6xLBL1lH/Z9sk+bbGXe7nR/dOLgncGM/yHElJ2xyNiTWaMeCUeZ5w7nKKeyLG7RMlz7htUo4ZyeKk94SrxfokGd2XTyTutGSX05650+48150ct3HODyTx799p3emxrzRw8zPDnZ5j8Pv77x8S/Yo7ranoTg0SB7Q7Y+ZdSXkuUe5EnT+s3Empwz1m7v59FWRvXpfcnSPQ1nuquzPqmgXBnajrPZXcyVpg59wFyEDybYMoNGOHPiFoQFpgeoOSO1GcnGdcrCe4iu5E4WR3ouZOu5MHep5FYu1OdJBzvd2JPe+OQOlbVO6g51kkXxsLba57L5Jzfv8EfT+e4M5e78HPs0i+ds2C7M6T4Ywba4F9dqfIY0dp7uI9HyfQfqvKivcYSR5Po8AVe/Ykytl+yaMhXDTfiIgCZSJA6QeF7L5Wceeofrsz152cu+8zPNy5yhPJvBsBSn9Q+kEBpa8o/bjJcicnpoU7KKC7E3XeHf2M4s6oeZdL9rhwQOkrSj/erNzJod35PXf2es+c7NyxIKJPFdZ7ZiC7MwpUd978T1IpIhE1d/Ekj/VJy0rxaoN7UhB1EmGB5NEQrzYyQekjSj8ooPQVzS0UV7Y7252Ustp5NwKU/qD0gwJKX1H6cZPlTrRxuC58d6LNS+1OOSj9oIDSV5R+rIi4bkMch3Znr/dEtXfiNQt6GxG0O31+r+WoV8Qg02M3BnH7EfvEoXr/T6TK8Y/Wn+uqM3ZeVNp+xD5xqN7/ylQfe8T+V3KHB5W2H61Plcbu61TfJ4j9/3r+f337NfTY1aH6Pqne/5MRvSKG+vs3msdnqTxjesfntIF05xDpgETKnZtZn7STpnQbuO165lTkfvAio89o7hzV//3984eZ2p1jOnfWbfz8jP/4YLuzpivf9Lzb7pTS7ly3gebOr+eOlr5mof8e8Zrl6/lfyZ0R8TltfD13tLQ76b9HdCcSmX1mLbDPGEnu/e918RJBKs5nG9Htrbadu5M9FtAk9ayTcxV/9VnEvtxBmTQp2/C8oOLUo/ZHizb/KXEi80oTx7t+ljt3j1W2O+PqUesju3OXOxJ3rvpHzR1kd67qWbuTEy8r/2dwcmeVA5z8orY7+l2WOykxuY7hgurOVXn0a5Ysd1add1HcKc0dSyq4U0p1d6LMuye707O9r7nTIj617WruHP2MvN7j7c5VexZUcSdpgX0XZCS597+7epSY1P5QeMegipry2WjbuQeH9mCyGHeLfuxYjZFH7rzhnjju6lC2gfI76wlGKlHNZ5F36z1yZ8doTLmuquhOTnuzcrPPTnKnZbu78hnulMBx5+izlTspxw2nP5wy775oP/uiOyVl3m1z8mQVR9sfKpbu5MbktqOtj+5Oz3nXgmh3cs+dR+XbnfrP3pzgTottqO7OjNyxqF/xmgVtvafdWced2es9Fd3pTRV3khbYIwbOSiYeMbzukEmJ7I91W5EHIQVKfzJFQoGzDSi5Yz2GGdslyR0Pot3J4cvutAbNOxLQ5t2vuDN64XjXVrtzj/eCfqMjei5pd9Jod+K7E2UbOJxwzYLC19d7KrpTwgnuzPZQFXeevt4jweQVMRZY7QDNYzozsg+wN1H9+f39/asttEnaEutvhaCUp9wp98I7d96PVUXl5wk+GI3V250W43nCWEk42ZVv3rmyyy1qTM+LImR3Rsy7ldzpOY9Ktv2r7pwR+Vg5Ehb7B8GdEZzszigk3+yt4E5NHyqDPu960us9dNqdetDc6VU+0519zfIv5gvsVhstjaN5xEvbtnUczqMh92fvf6Vt3rJdCVHy6AqlbS2WBx53QtBMIFxBrcprxsBjP1gsrKwWvKxODjTjWCn/R2PV7vRzp6Z/nLa1WM677U5Zvd28S2lz1w90d1bLnS+785mv7U4+2e6cxfGub7Ufs91pgeWcU82dFiDsh1H9aHdS2qT2j9O2ll7vyXVn1HoPmjtXsdqd8jhR6z3o7hQvsM+CW91ZyLyLaNX2LA51fDiPhtyfvf9dMeoHJ470UZpV7lhg+RioRJoWE61G1s+fLcfiCXdCkCDJ/1VZSZ8522CR/xnutMp9aR1OHBR3auO0O23aflPVndbe0dRHcGfGvCtt1yKOtA4nTruz3bli5U5p7nDKnuDOEV7nyrNjpd0pB82dnNz5mjutaHf65g413ghvd2pjoaz37H4vifUEdb0H3Z3iBfZMIb6hDKTlHR8tkrHz6H/WPkTKnS9wUu5o8Oiz9i5+Nu3OWnTuxNLu/A8UdyJRLf8r5p0lSNv/hdxpd/5Hu7M+J10r9zVLLO1OORX7fCIV9wOCO82+wc55tEQD5RsMI6R3OKj8/tLfuxkxwSFNMG8sx+lZZlee8/gHl/ejVhqed0Uz7o6O2q52UiOFuw0WJ09R7hyx6v/dtsSd3OMSyZ3IILnzHtdVXc7YS/bt6DFCNHd+hQh3ztoc7SuLfFqB4E5qGU65U+GMk/X89gbVnbPPvn7NQkU670rc6XnNosknq35xP3t//hV3ZlyzrMpFuXNFxHrPrL12p4wod0rrjOojrvdIPnt/3us9Y7j72uwb7Luv1lvtDMvFEqtY90FGjRex4JO1qEQ5SLnjRL0I2cXlPP7BhfKISUY8zSNDnLGVtvWEK3gPMo7NKHdy0Rwv1NxBdCcyJ7pTE2dUB82dEXzFnbM2V3kgiaclyp2UeNR2T4czTrt591R3zj77+jULFe55sma+8LxmyZz72p08kI7NSHdysV7vobaDtH+saXfWWe+xcienzIovXLOY/5HTGcgTlJaTt40L9yDlxmzOxSN3TuDksTh52xDo8f0G7c7v0fvZlx7fb9Du/B69n305eXxP3jYu7c5Gyhdyx2SB/YRHBbzxfPQjOpZHX7gxrXMuMoej28rMHY9t1T6Cd+qxZA1K35DGGLEv2cdku5MeT1Peoi8nuRO5vWx3Wj1yPIpZtb5XLA3IOVl5f3m4c9eGdfldLOTcQWpLup+QfHNqX04FaYyR+iKNme2Er7aVvZ8sYmYeSyYL7BZ3HyzeozSKYXGiZbGDMh5XX8XSbNO7L5R3O+1eF8PdPsvHU2bxvMQYebeO8/oNajzL8pQLhd2+4vTJ4p1p0rY96l+Xz8LbdfGPc2pcLghjfF1/csfSnZQ2d7FOdSclJz3xcCdnXHae27lz9PlJ7rRo71R3UtzAja/ZXxnu9I5lsU+R3Yky796xst25a4PbJw3WcxOlvTfI7lzFo5ZHc6cVCMd1r/f41n/HQlnvmcXk9kHLCes9yO6cYbHewyHbne6viKHeDbZ4j9IohsX7sChJ44Hnna/IpNOeDHqMQ8Q7pq6L3vfsO49ZUHID6eRyhdc+9HCnJMbX3Sld0LZoU0pld1JjVHJn5CJL9s1VDln78MvuzPBYpUeBI65ZrOJKY7Q7ZZ8j0e7kU9GdHmSdf5+w3vNld+4+r+LOJ9XWe7LcqeFr6z2kBXbNYwKzE+sqC4AjZndYrLcp6+DgTCDPMu+7UxaPdnDvQFHualM/45SZlZ/l/eouncVjOZzjjTtmsxiUu5O7GO94nP68x9ZiHDnlLe6KV3On1YXKKe68kbjTun1vd0Yz68sJ7hz9S6lz/3+7cz3vSmN6YuFO7Zw7Qpo7Hu1Tyni1j3hzUwqiO6kxKeUz3fmua7HNnDmnoju1XtHOu2juzHKF9XnyF9w5ui5DcKcUhPWeLHdKy8zKZ633cMqcut4TDcVx/7MKxC2fvQCiYdb3ytv0hPNto2cZi7vH2vqUu9rcNizznzK20ccbd8ykfeDEkOTSe2y9tyE6d6QxPbHqC9I2WaB1Z0T7lDpI+wXxeLN0p9e82+6Ux/Qk8pvK0pgZY1rRnZ5xLKhyvFV0p6Tsrh7SNYs0pmd7KNcsq5iSebc67c68mFIQ1ntOdWev99BiWFyzIOL+ihgq77sQkrsSu28jRPbFsv7pZHwDyitOZtuonLQtiHi5M6svlvVPB8WdSPNu58z3yPRVu7Mm1uN7gjsbGieNb7Y7LUA5D/oKCO5EXe9p1qCObzV/tTvxYS2wax+9WEG5g7Fr4+fn55+7Iru7G5RHdbR3SFb1sw4spIPghG8gWT0a9277BPkh3WHMOnYqunPH19yZ9Ujaqj0UdyLNu8juRJp3uSBuv3bebXfGcKI7LXLnXS/bnas2KJ9Z1vGIYUXGOW0Vd3L6ifqNZ0R3RsdAdyfqek+7cw2qO6ut91R0ZzTZx852gf0Z/Dlw7zuHs88seCez5aB5XYzP2nv+/27cZndkJX1+l6XE4NwRppQbXWRFjrtVm7tJ2jK+VFazPvz86P6iuEUfqJ9Ty9zlrCfRWRuzGKtvQpzkzpuvufP9SBrnRJp70v3OndPc6XUShu5O6uJr5AURojupeLrz3cZX3Lnrl8RtX3Wnpeei3Tkbr928O+KOpxl3VHdGtVPFndIbTSs8zkU4bXq4c5ers+Ndc+76/mx3zYLgzgrrPWjuRFrv0cSwYOVOav03kes9VdyJtN6T4c4R2wX22UXl6hs73ndELOJ793nV3vtiY9WnVb8435Sald3Fp4qE0pfRdkeOu7TN2WTFaZODtn+7GFF3GHe5w8mZEe+JyXoSHfWFM66nunPXhmd8RHdScpvbl1n9au60rm8dH9GdnHlXCro7qXF3MSz5ijt3/aLUb3fWzpX3z7N9zN1XnHlXQoY7IzjNnRb9q+5O6TkqZbGU2hfKefT795keQopJjZ/hTqT1nlH/qDEsqL7eU8WdSOs9Ge4cQXpFzLNTlncTo+p5xLTqiyQOpw715GsWM0OIK0Z31C3jjrA+YbeOQZWSRdtIuUPZL9T+cvonOf4qutPrGwinudPbi9bjFelOyzqaerMY7U58d3LrWbStrXeiOzmc6k7rehXduVvE0baNfs1i6U4OnPkIxZ2zGJx516rdbBdQ42TPu5o6lnFOW+9pd7Y7LWJkuJPSRqXc8XbnDesd7NpvOj3jWNSjHDS7MlZ9kSKJo/0WyM/Pv48cSe54RvP7++eur7Rfz5Ot3fZ7XRBGxnhLiXIHjzL5UE4WInOHcsfy/k87QUmPWSR3vtnl+nN/S04SOX3xjKNp+84d78WpNxbjheDOJ5m5k+nOp4dWfWl32tSLaDvanRb+iT6vq+xOTZy3M05wZ/Y1S0V37vpFmVPex49kHpPiFUPiTmm7aO6k5G/2NYsFvd7zJ0a2O3cxv+zOaus9ke6sNu9eV647b/4nrpnMaOBGAzEqsxv0iry3abd9FbffUlSUWBXHaAU1P04cG+nFyBdccV10X3xlPEZwF6iRxgrBnUjjwcXKnRW3v935BwR3Io3pF9xpAee64zR3zuhrFlo5zhhVg7PPT3MnBYkvvuCK6+r1nnbnvtzJYxTpzmpjc10Y7vyJ/kZJ0zRN0zRN0zRN0zRN0zRN05wA6xUxTdM0TdM0TdM0TdM0TdM0TdP8Ry+wN03TNE3TNE3TNE3TNE3TNI2AXmBvmqZpmqZpmqZpmqZpmqZpGgG9wN40TdM0TdM0TdM0TdM0TdM0AnqBvWmapmmapmmapmmapmmapmkE9AJ70zRN0zRN0zRN0zRN0zRN0wj4P8mH99ovo4L5AAAAAElFTkSuQmCC\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":"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gdmzZwMAduzYAbVaTevwyy+/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/Tipsomaly/model/big_vision/configs/proj/clippo/train_clippo.py b/Tipsomaly/model/big_vision/configs/proj/clippo/train_clippo.py new file mode 100644 index 0000000000000000000000000000000000000000..8fc683b985d096a010744bb565c1d3b47d03c96e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/distill/README.md b/Tipsomaly/model/big_vision/configs/proj/distill/README.md new file mode 100644 index 0000000000000000000000000000000000000000..44e10c3670d40307b74e367a9e6725ca1f848c61 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/distill/bigsweep_flowers_pet.py b/Tipsomaly/model/big_vision/configs/proj/distill/bigsweep_flowers_pet.py new file mode 100644 index 0000000000000000000000000000000000000000..977f7d2e5cf672bae6032b108148d123ecca4265 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/distill/bigsweep_food_sun.py b/Tipsomaly/model/big_vision/configs/proj/distill/bigsweep_food_sun.py new file mode 100644 index 0000000000000000000000000000000000000000..36362e658cc0b0cec13f3b33d2f31cd7cd45ff37 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/distill/bit_i1k.py b/Tipsomaly/model/big_vision/configs/proj/distill/bit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..24c2a0388109de1e3de6504422c54f0317cbed8a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/distill/common.py b/Tipsomaly/model/big_vision/configs/proj/distill/common.py new file mode 100644 index 0000000000000000000000000000000000000000..f4bb89ab540878672424e95856194ff98f3616f5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/flexivit/README.md b/Tipsomaly/model/big_vision/configs/proj/flexivit/README.md new file mode 100644 index 0000000000000000000000000000000000000000..2d41d8a954cb199a5b481e30eb32a1095a8523dc --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/flexivit/i1k_deit3_distill.py b/Tipsomaly/model/big_vision/configs/proj/flexivit/i1k_deit3_distill.py new file mode 100644 index 0000000000000000000000000000000000000000..fd7bf3c14b7ef48279fe75ff653fb4f25434762a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/flexivit/i21k_distill.py b/Tipsomaly/model/big_vision/configs/proj/flexivit/i21k_distill.py new file mode 100644 index 0000000000000000000000000000000000000000..30eddec5785e9a551fa5caf825820a43dfdf76ae --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/flexivit/i21k_sup.py b/Tipsomaly/model/big_vision/configs/proj/flexivit/i21k_sup.py new file mode 100644 index 0000000000000000000000000000000000000000..cca2443024bc4a4fe9c9474a2e34f9d53d4e52a9 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/flexivit/timing.py b/Tipsomaly/model/big_vision/configs/proj/flexivit/timing.py new file mode 100644 index 0000000000000000000000000000000000000000..07514087765f6959309bf473a90932590346613f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/README.md b/Tipsomaly/model/big_vision/configs/proj/givt/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a6818a55aeb9846e3c8fe89ebcc44fcf08d51def --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/givt_coco_panoptic.py b/Tipsomaly/model/big_vision/configs/proj/givt/givt_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..e89d43609d7fb317391c1e380cd9921abb2c10a8 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/givt_demo_colab.ipynb b/Tipsomaly/model/big_vision/configs/proj/givt/givt_demo_colab.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..3a4a24fbc641b95b99cd867410c494ea0c33b183 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/givt/givt_demo_colab.ipynb @@ -0,0 +1,318 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "botgo-GZiWI_" + }, + "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._" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jQxc9UZ-mVrQ" + }, + "source": [ + "### `big_vision` setup" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "ZAXiVta3n2jL" + }, + "outputs": [], + "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 \u0026\u0026 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\" \u003e big_vision/big_vision/constraints.txt\n", + "!pip install -r big_vision/big_vision/requirements.txt -c big_vision/big_vision/constraints.txt\n", + "\n", + "# To fix/accelerate gsutil cp\n", + "!pip3 -q install --no-cache-dir -U crcmod\n", + "\n", + "%cd big_vision" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "qYS7JNups4MU" + }, + "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", + "metadata": { + "id": "MaCM_PIcd2Rb" + }, + "source": [ + "### Select and download model\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "7l6QIjdyN3dg" + }, + "outputs": [], + "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", + "# Manually download VAE checkpoint as the built-in loading logic may hit\n", + "# permission issues.\n", + "vae_ckpt_local = f\"/tmp/{config.vae.model_init.split('/')[-1]}\"\n", + "!test -f {vae_ckpt_local} || gsutil cp {config.vae.model_init} /tmp/\n", + "config.vae.model_init = vae_ckpt_local\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])" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SUj5k1bxd6wr" + }, + "source": [ + "### VAE encode/decode and sampling loop" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "vSn7Si2FS1zi" + }, + "outputs": [], + "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" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "tOnWaJZVeOIX" + }, + "source": [ + "### Generate images for class label" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "_CiyXD_6nQbu" + }, + "outputs": [], + "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]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "sCcGB0m1oQY1" + }, + "outputs": [], + "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)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "4FWgfAghuh8P" + }, + "outputs": [], + "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()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "zGPPeXONy0Am" + }, + "outputs": [], + "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": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Tipsomaly/model/big_vision/configs/proj/givt/givt_imagenet2012.py b/Tipsomaly/model/big_vision/configs/proj/givt/givt_imagenet2012.py new file mode 100644 index 0000000000000000000000000000000000000000..84b0fd352487edbc66475ebde5883b087cf0f6ba --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/givt_nyu_depth.py b/Tipsomaly/model/big_vision/configs/proj/givt/givt_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..31c03499498c34f8b1a947d9e491ee3c0e8164b8 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/vae_coco_panoptic.py b/Tipsomaly/model/big_vision/configs/proj/givt/vae_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..db29d6e657d955f92e38313a201f34ed682eee5d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/givt/vae_nyu_depth.py b/Tipsomaly/model/big_vision/configs/proj/givt/vae_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..1f28cf5feaab113f91fd002d03157d953b377384 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py b/Tipsomaly/model/big_vision/configs/proj/gsam/vit_i1k_gsam_no_aug.py new file mode 100644 index 0000000000000000000000000000000000000000..90ac3ee1e0c9786af4e708aa5b68a31a869c22a9 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/image_text/README.md b/Tipsomaly/model/big_vision/configs/proj/image_text/README.md new file mode 100644 index 0000000000000000000000000000000000000000..7ce2d7b417350e6d44c3fc689bc3c0ba6f9c734c --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/image_text/README.md @@ -0,0 +1,7 @@ +# Image/text models + +This directory provides configs and Colabs for different projects on image/text multimodal learning. Please refer to the separate readmes for information on specific projects. + +**LiT: Zero-Shot Transfer with Locked-image text Tuning: [README_lit.md](README_lit.md)** + +**SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features: [README_siglip2.md](README_siglip2.md)** \ No newline at end of file diff --git a/Tipsomaly/model/big_vision/configs/proj/image_text/README_lit.md b/Tipsomaly/model/big_vision/configs/proj/image_text/README_lit.md new file mode 100644 index 0000000000000000000000000000000000000000..68538bbe38a1600ec7a5beaa349d0a5d724276ac --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/image_text/README_lit.md @@ -0,0 +1,63 @@ +# 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/Tipsomaly/model/big_vision/configs/proj/image_text/README_siglip2.md b/Tipsomaly/model/big_vision/configs/proj/image_text/README_siglip2.md new file mode 100644 index 0000000000000000000000000000000000000000..1c1af0a38209b7cbeb87c34c420c3c4455bdaa48 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/image_text/README_siglip2.md @@ -0,0 +1,57 @@ +# SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features + +*by Michael Tschannen\*, Alexey Gritsenko\*, Xiao Wang\*, Muhammad Ferjad Naeem\*, Ibrahim Alabdulmohsin\*,Nikhil Parthasarathy\*, Talfan Evans\*, Lucas Beyer\*, Ye Xia, Basil Mustafa, Olivier Hรฉnaff, Jeremiah Harmsen, Andreas Steiner, Xiaohua Zhai\* (\*core contributor)* + +[[arxiv]](https://arxiv.org/abs/2502.14786) [[colab]](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb) [[Hugging Face]](https://huggingface.co/collections/google/siglip2-67b5dcef38c175486e240107) + +### Summary + +We introduce SigLIP 2, a family of new multilingual vision-language encoders that build on the success of the original [SigLIP](https://arxiv.org/abs/2303.15343). In this second iteration, we extend the original image-text training objective with several prior, independently developed techniques into a unified recipe---this includes captioning-based pretraining, self-supervised losses (self-distillation, masked prediction) and online data curation. With these changes, SigLIP 2 models outperform their SigLIP counterparts at all model scales in core capabilities, including zero-shot classification, image-text retrieval, and transfer performance when extracting visual representations for Vision-Language Models (VLMs). Furthermore, the new training recipe leads to significant improvements on localization and dense prediction tasks. We also train variants which support multiple resolutions and preserve the input's native aspect ratio. Finally, we train on a more diverse data-mixture that includes de-biasing techniques, leading to much better multilingual understanding and improved fairness. To allow users to trade off inference cost with performance, we release model checkpoints at four sizes: ViT-B (86M), L (303M), So400m (400M), and g (1B). + +### Checkpoints + +Below we provide links to all available checkpoints. The standard (non-NaFlex) checkpoints are compatible with [vit.py](https://github.com/google-research/big_vision/blob/main/big_vision/models/vit.py) and [two_towers.py](https://github.com/google-research/big_vision/blob/main/big_vision/models/proj/image_text/two_towers.py) from [SigLIP](https://arxiv.org/abs/2303.15343). The only difference is the vocab size (256k) and the tokenizer (Gemma tokenizer). The NaFlex variant requires a different ViT implementation, [naflex_vit.py](https://github.com/google-research/big_vision/blob/main/big_vision/models/proj/image_text/naflex_vit.py), and an adapted image preprocessing, see [ops_naflex.py](https://github.com/google-research/big_vision/blob/main/big_vision/pp/proj/image_text/ops_naflex.py). The [demo colab](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb) is a good entry point to see how to use the models. + +| Model | ViT | Res | Download | INet 0-shot | COCO Tโ†’I | COCO Iโ†’T | +|:------------------|:----------|:------:|:--------------------------------------------------------------------------------------:|:--------------:|:------------:|:------------:| +| SigLIP 2 | B/32 | 256 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b32_256.npz) | 74.0 | 47.2 | 63.7 | +| SigLIP 2 | B/16 | 224 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b16_224.npz) | 78.2 | 52.1 | 68.9 | +| SigLIP 2 | B/16 | 256 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b16_256.npz) | 79.1 | 53.2 | 69.7 | +| SigLIP 2 | B/16 | 384 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b16_384.npz) | 80.6 | 54.6 | 71.4 | +| SigLIP 2 | B/16 | 512 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b16_512.npz) | 81.2 | 55.2 | 71.2 | +| SigLIP 2 | L/16 | 256 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_l16_256.npz) | 82.5 | 54.7 | 71.5 | +| SigLIP 2 | L/16 | 384 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_l16_384.npz) | 83.1 | 55.3 | 71.4 | +| SigLIP 2 | L/16 | 512 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_l16_512.npz) | 83.5 | 55.2 | 72.1 | +| SigLIP 2 | So400m/14 | 224 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m14_224.npz) | 83.2 | 55.1 | 71.5 | +| SigLIP 2 | So400m/14 | 384 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m14_384.npz) | 84.1 | 55.8 | 71.7 | +| SigLIP 2 | So400m/16 | 256 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m16_256.npz) | 83.4 | 55.4 | 71.5 | +| SigLIP 2 | So400m/16 | 384 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m16_384.npz) | 84.1 | 56.0 | 71.2 | +| SigLIP 2 | So400m/16 | 512 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m16_512.npz) | 84.3 | 56.0 | 71.3 | +| SigLIP 2 | g-opt/16 | 256 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_g-opt16_256.npz) | 84.5 | 55.7 | 72.5 | +| SigLIP 2 | g-opt/16 | 384 | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_g-opt16_384.npz) | 85.0 | 56.1 | 72.8 | +| SigLIP 2 (NaFlex) | B/16 | var. | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_b16_naflex.npz) | 78.5 | 51.1 | 67.3 | +| SigLIP 2 (NaFlex) | So400m/16 | var. | [link](https://storage.googleapis.com/big_vision/siglip2/siglip2_so400m16_naflex.npz) | 83.5 | 55.1 | 71.2 | + +*The NaFlex results are for sequence length 256.* + +### Citation +``` +@article{tschannen2025siglip, + title={SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features}, + author={Tschannen, Michael and Gritsenko, Alexey and Wang, Xiao and Naeem, Muhammad Ferjad and Alabdulmohsin, Ibrahim and Parthasarathy, Nikhil and Evans, Talfan and Beyer, Lucas and Xia, Ye and Mustafa, Basil and H\'enaff, Olivier and Harmsen, Jeremiah and Steiner, Andreas and Zhai, Xiaohua}, + year={2025}, + journal={arXiv preprint arXiv:2502.14786} +} +``` + +\ +\ +Copyright 2025 Google LLC + +All software is licensed under the Apache License, Version 2.0 (Apache 2.0); you may not use this file except in compliance with the Apache 2.0 license. You may obtain a copy of the Apache 2.0 license at: https://www.apache.org/licenses/LICENSE-2.0 + +All other materials are licensed under the Creative Commons Attribution 4.0 International License (CC-BY). You may obtain a copy of the CC-BY license at: https://creativecommons.org/licenses/by/4.0/legalcode + +Unless required by applicable law or agreed to in writing, all software and materials distributed here under the Apache 2.0 or CC-BY licenses are distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the licenses for the specific language governing permissions and limitations under those licenses. + +This is not an official Google product. diff --git a/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb b/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..37c063b18b6ca96ec06b37ad0b59920410ac7ddd --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb @@ -0,0 +1,1161 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "XVNFRF1WpKfX" + }, + "source": [ + "\u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/SigLIP2_demo.ipynb\"\u003e\n", + " \u003cimg src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/\u003e\n", + "\u003c/a\u003e" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wR53lePHuiP-" + }, + "source": [ + "# General information\n", + "\n", + "Example colab for SigLIP 2 models described in the [SigLIP 2 paper](https://arxiv.org/abs/2502.14786).\n", + "\n", + "**These models are not official Google products and were trained and released for research purposes.**\n", + "\n", + "If you find these model(s) useful for your research, consider citing\n", + "\n", + "```\n", + "@article{tschannen2025siglip,\n", + " title={SigLIP 2: Multilingual Vision-Language Encoders with Improved Semantic Understanding, Localization, and Dense Features},\n", + " author={Tschannen, Michael and Gritsenko, Alexey and Wang, Xiao and Naeem, Muhammad Ferjad and Alabdulmohsin, Ibrahim and Parthasarathy, Nikhil and Evans, Talfan and Beyer, Lucas and Xia, Ye and Mustafa, Basil and H\\'enaff, Olivier and Harmsen, Jeremiah and Steiner, Andreas and Zhai, Xiaohua},\n", + " year={2025},\n", + " journal={arXiv preprint arXiv:2502.14786}\n", + "}\n", + "```\n", + "\n", + "If you use our released models in your products, we appreciate any direct feedback. Please contact tschannen@google.com to get in touch.\n", + "\n", + "This Colab was forked from the previous version\n", + "[SigLIP_demo.ipynb](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/image_text/SigLIP_demo.ipynb)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kXSdSXVg2PAI", + "outputId": "bf11ea75-e6b2-447d-c511-7a5df2c445a8" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Tue Mar 4 18:37:57 2025 \n", + "+-----------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 550.54.15 Driver Version: 550.54.15 CUDA Version: 12.4 |\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 36C P8 10W / 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", + " 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[32m319.7/319.7 kB\u001b[0m \u001b[31m6.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m230.2/230.2 MB\u001b[0m \u001b[31m5.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m5.3/5.3 MB\u001b[0m \u001b[31m92.3 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[31m25.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m104.3/104.3 MB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m894.1/894.1 kB\u001b[0m \u001b[31m46.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m76.5/76.5 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Building wheel for clu (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Building wheel for flaxformer (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Building wheel for panopticapi (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m89.7/89.7 kB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25h Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Building wheel for crcmod (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Collecting flax==0.8.5\n", + " Downloading flax-0.8.5-py3-none-any.whl.metadata (10 kB)\n", + "Requirement already satisfied: numpy\u003e=1.22 in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (1.26.4)\n", + "Requirement already satisfied: jax\u003e=0.4.27 in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (0.4.33)\n", + "Requirement already satisfied: msgpack in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (1.1.0)\n", + "Requirement already satisfied: optax in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (0.2.4)\n", + "Requirement already satisfied: orbax-checkpoint in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (0.6.4)\n", + "Requirement already satisfied: tensorstore in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (0.1.72)\n", + "Requirement already satisfied: rich\u003e=11.1 in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (13.9.4)\n", + "Requirement already satisfied: typing-extensions\u003e=4.2 in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (4.12.2)\n", + "Requirement already satisfied: PyYAML\u003e=5.4.1 in /usr/local/lib/python3.11/dist-packages (from flax==0.8.5) (6.0.2)\n", + "Requirement already satisfied: jaxlib\u003c=0.4.33,\u003e=0.4.33 in /usr/local/lib/python3.11/dist-packages (from jax\u003e=0.4.27-\u003eflax==0.8.5) (0.4.33)\n", + "Requirement already satisfied: ml-dtypes\u003e=0.2.0 in /usr/local/lib/python3.11/dist-packages (from jax\u003e=0.4.27-\u003eflax==0.8.5) (0.4.1)\n", + "Requirement already satisfied: opt-einsum in /usr/local/lib/python3.11/dist-packages (from jax\u003e=0.4.27-\u003eflax==0.8.5) (3.4.0)\n", + "Requirement already satisfied: scipy\u003e=1.10 in /usr/local/lib/python3.11/dist-packages (from jax\u003e=0.4.27-\u003eflax==0.8.5) (1.13.1)\n", + "Requirement already satisfied: markdown-it-py\u003e=2.2.0 in /usr/local/lib/python3.11/dist-packages (from rich\u003e=11.1-\u003eflax==0.8.5) (3.0.0)\n", + "Requirement already satisfied: pygments\u003c3.0.0,\u003e=2.13.0 in /usr/local/lib/python3.11/dist-packages (from rich\u003e=11.1-\u003eflax==0.8.5) (2.18.0)\n", + "Requirement already satisfied: absl-py\u003e=0.7.1 in /usr/local/lib/python3.11/dist-packages (from optax-\u003eflax==0.8.5) (1.4.0)\n", + "Requirement already satisfied: chex\u003e=0.1.87 in /usr/local/lib/python3.11/dist-packages (from optax-\u003eflax==0.8.5) (0.1.89)\n", + "Requirement already satisfied: etils[epy] in /usr/local/lib/python3.11/dist-packages (from optax-\u003eflax==0.8.5) (1.12.0)\n", + "Requirement already satisfied: nest_asyncio in /usr/local/lib/python3.11/dist-packages (from orbax-checkpoint-\u003eflax==0.8.5) (1.6.0)\n", + "Requirement already satisfied: protobuf in /usr/local/lib/python3.11/dist-packages (from orbax-checkpoint-\u003eflax==0.8.5) (4.25.6)\n", + "Requirement already satisfied: humanize in /usr/local/lib/python3.11/dist-packages (from orbax-checkpoint-\u003eflax==0.8.5) (4.11.0)\n", + "Requirement already satisfied: toolz\u003e=0.9.0 in /usr/local/lib/python3.11/dist-packages (from chex\u003e=0.1.87-\u003eoptax-\u003eflax==0.8.5) (0.12.1)\n", + "Requirement already satisfied: mdurl~=0.1 in /usr/local/lib/python3.11/dist-packages (from markdown-it-py\u003e=2.2.0-\u003erich\u003e=11.1-\u003eflax==0.8.5) (0.1.2)\n", + "Requirement already satisfied: fsspec in /usr/local/lib/python3.11/dist-packages (from etils[epath,epy]-\u003eorbax-checkpoint-\u003eflax==0.8.5) (2024.10.0)\n", + "Requirement already satisfied: importlib_resources in /usr/local/lib/python3.11/dist-packages (from etils[epath,epy]-\u003eorbax-checkpoint-\u003eflax==0.8.5) (6.5.2)\n", + "Requirement already satisfied: zipp in /usr/local/lib/python3.11/dist-packages (from etils[epath,epy]-\u003eorbax-checkpoint-\u003eflax==0.8.5) (3.21.0)\n", + "Downloading flax-0.8.5-py3-none-any.whl (731 kB)\n", + "\u001b[2K \u001b[90mโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”\u001b[0m \u001b[32m731.3/731.3 kB\u001b[0m \u001b[31m8.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n", + "\u001b[?25hInstalling collected packages: flax\n", + " Attempting uninstall: flax\n", + " Found existing installation: flax 0.10.4\n", + " Uninstalling flax-0.10.4:\n", + " Successfully uninstalled flax-0.10.4\n", + "Successfully installed flax-0.8.5\n", + "/content/big_vision\n" + ] + } + ], + "source": [ + "#@markdown # Environment setup\n", + "#@markdown Tested on GPU, TPU, and CPU with ViT-B/16 @256px.\n", + "import os\n", + "if '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 \u0026\u0026 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" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "ezmzqkFspGXS" + }, + "outputs": [], + "source": [ + "#@markdown Image credit:\n", + "\n", + "#@markdown - The apple and apple + iPod images are from OpenAI.\n", + "#@markdown - Cows on beach were created by Chitwan Saharia using the Imagen model and shared with permission.\n", + "#@markdown - [Cold drink on hot day](https://unsplash.com/fr/photos/hQHm2D1fH70).\n", + "#@markdown - [Hot drink on cold day](https://www.rawpixel.com/image/3282934).\n", + "#@markdown - The remaining pictures are personal photos taken by the SigLIP1 authors.\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\u0026q=85\u0026fm=jpg\u0026crop=entropy\u0026cs=srgb\u0026dl=svetlana-gumerova-hQHm2D1fH70-unsplash.jpg\u0026w=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\u0026name=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\u0026name=medium' -O mountain_view.jpg\n", + "!wget -q https://storage.googleapis.com/big_vision/siglip2/siglip2_screenshot.jpg\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "byHpmgAO6inM" + }, + "source": [ + "# Choose and load model, perform inference" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0DsOabGD7MRG", + "outputId": "a0d54aa3-6543-48e8-ccb6-4c2048a0b7c7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Copying gs://big_vision/siglip2/siglip2_so400m16_256.npz...\n", + "\\ [1 files][ 4.2 GiB/ 4.2 GiB] 86.7 MiB/s \n", + "Operation completed over 1 objects/4.2 GiB. \n" + ] + } + ], + "source": [ + "# Pick your hero: (WHEN CHANGING THIS, RERUN IMAGE/TEXT EMBEDDING CELLS)\n", + "# Give this cell 1-3mins.\n", + "\n", + "# VARIANT, RES = 'B/32', 256\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, N_PATCHES = 'B/16', 'naflex', 256\n", + "# VARIANT, RES = 'L/16', 256\n", + "# VARIANT, RES = 'L/16', 384\n", + "# VARIANT, RES = 'L/16', 512\n", + "VARIANT, RES = 'So400m/16', 256\n", + "# VARIANT, RES = 'So400m/16', 384\n", + "# VARIANT, RES = 'So400m/16', 512\n", + "# VARIANT, RES, N_PATCHES = 'So400m/16', 'naflex', 256\n", + "# VARIANT, RES = 'So400m/14', 224\n", + "# VARIANT, RES = 'So400m/14', 384\n", + "# VARIANT, RES = 'g-opt/16', 256\n", + "# VARIANT, RES = 'g-opt/16', 384\n", + "\n", + "CKPT = f'siglip2_{VARIANT.lower().replace(\"/\", \"\")}_{RES}.npz'\n", + "TXTVARIANT, PATCH_SIZE = VARIANT.split('/')\n", + "EMBDIM = {'B': 768, 'L': 1024, 'So400m': 1152, 'g-opt': 1536}[TXTVARIANT]\n", + "# Note: The g-opt vision encoder is paired with a So400m text encoder\n", + "TXTVARIANT = 'So400m' if TXTVARIANT == 'g-opt' else TXTVARIANT\n", + "PATCH_SIZE = int(PATCH_SIZE)\n", + "VOCAB = 256_000\n", + "SEQLEN = 64\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/siglip2/{CKPT} /tmp/\n", + "\n", + "import big_vision.models.proj.image_text.two_towers as model_mod\n", + "\n", + "model_cfg = ml_collections.ConfigDict(dict(\n", + " image_model='vit',\n", + " image=dict(\n", + " pool_type='map',\n", + " scan=True,\n", + " variant=VARIANT,\n", + " ),\n", + " text_model='proj.image_text.text_transformer',\n", + " text=dict(\n", + " scan=True,\n", + " variant=TXTVARIANT,\n", + " vocab_size=256_000,\n", + " ),\n", + " out_dim=[None, EMBDIM],\n", + " bias_init=-10, # without this arg, no \"b\" param is added\n", + "))\n", + "if RES == 'naflex':\n", + " model_cfg.image_model = 'proj.image_text.naflex_vit'\n", + " model_cfg.image = dict(\n", + " pool_type='map',\n", + " nposemb=16,\n", + " posemb='learn_2d',\n", + " variant='B'\n", + " )\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)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "FOsMPR6Gg3Sn" + }, + "outputs": [], + "source": [ + "# # inspect params:\n", + "# from clu import parameter_overview\n", + "# print(parameter_overview.get_parameter_overview(params))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "xmuXfCfBjgeF", + "outputId": "f8c2323f-25fe-4fe9-9d4b-bfedc62b60f3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "imgs (11, 256, 256, 3)\n", + "zimg (11, 1152)\n" + ] + } + ], + "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 big_vision.pp.proj.image_text.ops_naflex\n", + "import big_vision.pp.proj.paligemma.ops\n", + "import PIL\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", + "get_pp_naflex = lambda n_patches, patch_size: pp_builder.get_preprocess_fn('|'.join((\n", + " 'value_range(-1, 1)',\n", + " f'resize_to_sequence({patch_size}, {n_patches}, outkey=\"image\")',\n", + " f'patchify({patch_size}, key=\"image\")',\n", + " 'flatten([\"image\"])',\n", + " f'pad_to_shape(key=\"image/patches\", shape=({n_patches}, None))',\n", + " f'pad_to_shape(key=\"image/type\", shape=({n_patches},))',\n", + " f'pad_to_shape(key=\"image/yidx\", shape=({n_patches},))',\n", + " f'pad_to_shape(key=\"image/xidx\", shape=({n_patches},))',\n", + " 'tuplify([\"image/patches\", \"image/type\", \"image/yidx\", \"image/xidx\"], \"image\")',\n", + ")))\n", + "\n", + "if RES == 'naflex':\n", + " # See section \"NaFlex\" below for a comparison of this preprocessing with\n", + " # resize to square.\n", + " pp_img = get_pp_naflex(N_PATCHES, PATCH_SIZE)\n", + " imgs = [pp_img(dict(image=np.array(image)))['image'] for image in images]\n", + " imgs = tuple(np.stack(_) for _ in zip(*imgs))\n", + " print('imgs', tuple(x.shape for x in imgs))\n", + "else:\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", + " print('imgs', imgs.shape)\n", + "\n", + "zimg, _, out = model.apply({'params': params}, imgs, None)\n", + "\n", + "print('zimg', zimg.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "KGrpkRTtjU-L", + "outputId": "f36af86f-d844-4faf-ad9b-fd2dae91c0a1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(18, 64) (18, 1152)\n" + ] + } + ], + "source": [ + "#@title Tokenize and embed texts\n", + "\n", + "# texts with translations into random languages\n", + "texts_dict = {\n", + " 'an apple': 'tufaha', # Swahili\n", + " 'a picture of an apple': 'ฮญฮฝฮฑ ฮผฮฎฮปฮฟ', # Greek (Modern)\n", + " 'an ipod': 'ะฐะนะฟะพะด', # Russian\n", + " 'an apple with a note saying \"ipod\"': 'apple na nรณta ag rรก \"ipod\"', # Irish\n", + " 'a cold drink on a hot day': 'en kold drink pรฅ en varm dag', # Danish\n", + " 'a hot drink on a cold day': 'een hete drank op een koude dag', # Dutch\n", + " 'a photo of a cold drink on a hot day': '็‚Ž็ƒญๅคฉๆฐ”้‡Œไธ€ๆฏๅ†ท้ฅฎ็š„็…ง็‰‡', # Chinese\n", + " 'a photo of a hot drink on a cold day': 'foto cangkir panas dina dinten tiis', # Sundanese\n", + " #\n", + " 'a photo of two guys in need of caffeine': 'เคเค• เคคเคธเฅเคตเฅ€เคฐ เคœเคฟเคธเคฎเฅ‡เค‚ เคฆเฅ‹ เคฒเฅ‹เค—เฅ‹เค‚ เค•เฅ‹ เค•เฅˆเคซเฅ€เคจ เค•เฅ€ เคœเคผเคฐเฅ‚เคฐเคค เคนเฅˆ', # Hindi\n", + " 'a photo of two guys in need of water': 'o imagine a doi tipi care au nevoie de apฤƒ', # Romanian\n", + " 'a photo of the SigLIP authors': 'foto para penulis SigLIP', # Indonesian\n", + " 'a photo of a rock band': 'ใƒญใƒƒใ‚ฏใƒใƒณใƒ‰ใฎๅ†™็œŸ', # Japanese\n", + " 'a photo of researchers at Google Brain': 'foto av forskare pรฅ Google Brain', # Swedish\n", + " 'a photo of researchers at OpenAI': 'OpenAI:n tutkijoiden kuva', # Finnish\n", + " #\n", + " 'cow': 'sapi', # Indonesian\n", + " 'a cow in a tuxedo': 'une vache en smoking', # French\n", + " 'a cow on the beach': 'ืคืจื” ืขืœ ื”ื—ื•ืฃ', # Hebrew\n", + " #\n", + " # Croatian:\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.': \"slika prijenosnog raฤunala sa zakljuฤanim zaslonom, ลกalica cappuccina, mlinci za sol i papar. Pogled kroz prozor otkriva jezero Zรผrich i Alpe u pozadini grada.\",\n", + "}\n", + "\n", + "# โ€ผ๏ธ NOTE: SigLIP 2 models work best with lowercase texts\n", + "pp_txt = pp_builder.get_preprocess_fn(f'lower(key=\"text\")|tok(length={SEQLEN}, model=\"gemma\", bos=\"no\", eos=\"sticky\", key=\"text\")')\n", + "\n", + "\n", + "def embed_texts(texts):\n", + " txts = np.array([pp_txt({'text': text})['text'] for text in texts])\n", + " _, ztxt, out = model.apply({'params': params}, None, txts)\n", + " return txts, ztxt, dict(t=out['t'], b=out['b'])\n", + "\n", + "\n", + "txts, ztxt, out = embed_texts(texts_dict)\n", + "print(txts.shape, ztxt.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TIdAVw9VGEAw", + "outputId": "15496356-b38e-4646-88d2-b4e62edcf782" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Learned temperature 109.9, learned bias: -15.9\n", + "21.8% that image 0 is 'an apple'\n", + "33.6% that image 0 is 'a picture of an apple'\n" + ] + } + ], + "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 '{list(texts_dict)[0]}'\")\n", + "print(f\"{probs[0][1]:.1%} that image 0 is '{list(texts_dict)[1]}'\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "cellView": "form", + "id": "f_kXZhQ2ovIi" + }, + "outputs": [], + "source": [ + "# @title Pretty demo (code)\n", + "from IPython.display import Javascript, HTML\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 img2html(img):\n", + " with io.BytesIO() as buf:\n", + " img.save(buf, format=\"JPEG\")\n", + " enc_img = buf.getvalue()\n", + " img_data = base64.b64encode(np.ascontiguousarray(enc_img)).decode('ascii')\n", + " return f'\u003cimg src=\"data:image/jpeg;base64,{img_data}\"/\u003e'\n", + "\n", + "\n", + "def make_table(zimg, ztxt, images, texts, 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 \u003e= 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\"\u003ctd id=td_{iimg}_{itxt} style=background-color:{color(x)} class=pct\u003e\u003cpre id=p_{iimg}_{itxt}\u003e{x * 100:\u003e4.0f}%\u003c/pre\u003e\"\n", + "\n", + " html = f'''\n", + " \u003cp\u003e\n", + " \u003clabel for=b\u003eBias value:\u003c/label\u003e\n", + " \u003cinput id=b type=range min=-20 max=0 step=0.1 name=b value={b} style=vertical-align:middle\u003e\n", + " \u003coutput id=value\u003e\u003c/output\u003e\n", + " \u003c/p\u003e\n", + " '''\n", + "\n", + " html += \"\u003ctable\u003e\\n\"\n", + " html += \"\u003ctr\u003e\"\n", + " html += \"\".join([f\"\u003ctd style='width:{DEMO_IMG_SIZE}px;line-height:0'\u003e\" + img2html(img.resize([DEMO_IMG_SIZE, DEMO_IMG_SIZE])) for img in images])\n", + " html += \"\u003ctd\u003e\"\n", + " for itxt, txt in enumerate(texts):\n", + " html += f\"\u003ctr\u003e\" + \"\".join([cell(probs[iimg, itxt], iimg, itxt) for iimg in range(len(images))]) + f\"\u003ctd class=txt\u003e{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 \u003c logits.length; iimg++) {\n", + " for(var itxt = 0; itxt \u003c 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 \u003c 10.0 ? ' ' : p \u003c 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) =\u003e {\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()'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 515 + }, + "id": "mt5BIywzzA6c", + "outputId": "7e92d9f8-eaf8-44cb-9092-7e06f4fac390" + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "var logits = [[14.656767845153809, 15.250247955322266, 15.409202575683594, 19.771230697631836, 2.2841110229492188, 3.2212440967559814, 2.7505900859832764, 2.945586681365967, 1.249023675918579, -0.022008903324604034, 4.742644786834717, 0.2296551764011383, -1.9878309965133667, -1.3340283632278442, 0.16120955348014832, -7.615262031555176, -5.1106181144714355, -8.441411972045898], [16.266162872314453, 17.33416748046875, 4.963876247406006, 9.065200805664062, 4.221234321594238, 3.199340343475342, 3.3866782188415527, 1.9877198934555054, -1.8332914113998413, -0.5539354085922241, 2.5022826194763184, -3.1470394134521484, -2.840461492538452, -4.132590293884277, 2.5552444458007812, -6.645838260650635, -3.538269281387329, -8.712821006774902], [1.8294894695281982, 2.154853343963623, -0.7122278213500977, -3.929692506790161, 15.371567726135254, 5.8843841552734375, 15.19970417022705, 5.412536144256592, 2.618687868118286, 2.752964973449707, 4.505246639251709, -1.6640056371688843, -5.128721237182617, -4.339777946472168, -1.4124987125396729, -9.368490219116211, -3.787917375564575, -7.841649055480957], [2.9122354984283447, 2.4692718982696533, 2.459583282470703, -4.101287364959717, 4.548985958099365, 15.888904571533203, 5.545042514801025, 18.007904052734375, 2.996645450592041, -3.7164523601531982, -0.09089484810829163, -3.3227603435516357, -5.767429351806641, -6.667675495147705, 0.5110976696014404, -7.719865798950195, -4.184369087219238, -5.109359264373779], [-1.0981005430221558, -0.11852116882801056, -1.1732755899429321, -7.526024341583252, 4.226718425750732, 3.3711671829223633, 4.0914788246154785, 2.414170026779175, 20.417871475219727, 10.825191497802734, 11.764508247375488, 1.3084189891815186, 8.395318984985352, 9.139975547790527, -0.11757967621088028, -11.61832332611084, -5.792989253997803, 2.866032361984253], [0.23675498366355896, 1.2888647317886353, 0.12354141473770142, 3.171550750732422, -0.03542157635092735, -2.6963112354278564, -0.2601011395454407, -3.704585075378418, 4.161721706390381, 4.016524314880371, 26.05571746826172, 4.814850330352783, 16.42120361328125, 14.271191596984863, -2.4880902767181396, -9.752284049987793, -9.190740585327148, -3.5817220211029053], [1.078748106956482, 1.369918704032898, -0.3256940245628357, -2.161975383758545, 1.5595654249191284, -0.008188785053789616, 0.1384943276643753, -1.9556262493133545, 5.385950088500977, 2.41086745262146, 16.263961791992188, 4.138530254364014, 22.473264694213867, 18.332529067993164, -2.502660036087036, -9.021139144897461, -7.785128116607666, -1.456052541732788], [0.830745279788971, 2.6711432933807373, 1.1853156089782715, -0.051070623099803925, -4.069701194763184, 0.5147737860679626, -3.8544862270355225, 1.2381788492202759, -0.8294660449028015, 2.6137824058532715, 4.02204704284668, 0.727441132068634, -2.1278531551361084, -0.1010865867137909, 1.333038330078125, -5.051750183105469, -6.807021141052246, -2.0184223651885986], [-0.8872398138046265, 0.4818684756755829, -1.6223139762878418, -7.974726676940918, 3.2184135913848877, -1.5331275463104248, 1.2972863912582397, -3.3138039112091064, -2.8257241249084473, -0.2787041664123535, -1.9446015357971191, -5.636784553527832, -5.324060440063477, -5.12062406539917, 13.344278335571289, 6.2044501304626465, 21.382110595703125, -6.001667499542236], [1.0758713483810425, 1.9333736896514893, -0.2656210660934448, -6.8738508224487305, 2.1863884925842285, -1.0240341424942017, 0.3531090319156647, -2.908724546432495, -1.3156943321228027, -0.13916334509849548, -0.8485345244407654, -3.262017011642456, -3.924159049987793, -4.24848747253418, 13.137842178344727, 22.693538665771484, 17.857364654541016, -3.6404786109924316], [0.6692630648612976, 2.067216634750366, 1.699018120765686, -1.8247333765029907, 5.062845706939697, 9.979086875915527, 5.2289862632751465, 10.712607383728027, 6.570303440093994, -0.30220529437065125, 6.702491283416748, -3.121782064437866, 0.9961336255073547, 2.7932493686676025, 2.873249053955078, -2.8172128200531006, 1.5813153982162476, 32.34162139892578]];\n", + "//# sourceURL=js_d53fa15802" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "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" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\u003cstyle\u003e\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", + " \u003c/style\u003e" + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + " \u003cp\u003e\n", + " \u003clabel for=b\u003eBias value:\u003c/label\u003e\n", + " \u003cinput id=b type=range min=-20 max=0 step=0.1 name=b value=-15.932409286499023 style=vertical-align:middle\u003e\n", + " \u003coutput id=value\u003e\u003c/output\u003e\n", + " \u003c/p\u003e\n", + " \u003ctable\u003e\n", + "\u003ctr\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd\u003e\u003ctr\u003e\u003ctd id=td_0_0 style=background-color:#e8f6e3 class=pct\u003e\u003cpre id=p_0_0\u003e 22%\u003c/pre\u003e\u003ctd id=td_1_0 style=background-color:#bbe4b4 class=pct\u003e\u003cpre id=p_1_0\u003e 58%\u003c/pre\u003e\u003ctd id=td_2_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_0\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ean apple\u003ctr\u003e\u003ctd id=td_0_1 style=background-color:#dbf1d6 class=pct\u003e\u003cpre id=p_0_1\u003e 34%\u003c/pre\u003e\u003ctd id=td_1_1 style=background-color:#98d594 class=pct\u003e\u003cpre id=p_1_1\u003e 80%\u003c/pre\u003e\u003ctd id=td_2_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_1\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea picture of an apple\u003ctr\u003e\u003ctd id=td_0_2 style=background-color:#d7efd1 class=pct\u003e\u003cpre id=p_0_2\u003e 37%\u003c/pre\u003e\u003ctd id=td_1_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_2\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ean ipod\u003ctr\u003e\u003ctd id=td_0_3 style=background-color:#78c679 class=pct\u003e\u003cpre id=p_0_3\u003e 98%\u003c/pre\u003e\u003ctd id=td_1_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_3\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ean apple with a note saying \"ipod\"\u003ctr\u003e\u003ctd id=td_0_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_4 style=background-color:#d8f0d2 class=pct\u003e\u003cpre id=p_2_4\u003e 36%\u003c/pre\u003e\u003ctd id=td_3_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_4\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea cold drink on a hot day\u003ctr\u003e\u003ctd id=td_0_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_5 style=background-color:#c9eac2 class=pct\u003e\u003cpre id=p_3_5\u003e 49%\u003c/pre\u003e\u003ctd id=td_4_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_5\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea hot drink on a cold day\u003ctr\u003e\u003ctd id=td_0_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_6 style=background-color:#dcf2d7 class=pct\u003e\u003cpre id=p_2_6\u003e 32%\u003c/pre\u003e\u003ctd id=td_3_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_6\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of a cold drink on a hot day\u003ctr\u003e\u003ctd id=td_0_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_7 style=background-color:#88ce87 class=pct\u003e\u003cpre id=p_3_7\u003e 89%\u003c/pre\u003e\u003ctd id=td_4_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_7\u003e 1%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of a hot drink on a cold day\u003ctr\u003e\u003ctd id=td_0_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_8 style=background-color:#76c578 class=pct\u003e\u003cpre id=p_4_8\u003e 99%\u003c/pre\u003e\u003ctd id=td_5_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_8\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of two guys in need of caffeine\u003ctr\u003e\u003ctd id=td_0_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_9\u003e 1%\u003c/pre\u003e\u003ctd id=td_5_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_9\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of two guys in need of water\u003ctr\u003e\u003ctd id=td_0_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_4_10\u003e 2%\u003c/pre\u003e\u003ctd id=td_5_10 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_5_10\u003e 100%\u003c/pre\u003e\u003ctd id=td_6_10 style=background-color:#bbe4b4 class=pct\u003e\u003cpre id=p_6_10\u003e 58%\u003c/pre\u003e\u003ctd id=td_7_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_10\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of the SigLIP authors\u003ctr\u003e\u003ctd id=td_0_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_11\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of a rock band\u003ctr\u003e\u003ctd id=td_0_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_12 style=background-color:#b5e1ae class=pct\u003e\u003cpre id=p_5_12\u003e 62%\u003c/pre\u003e\u003ctd id=td_6_12 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_6_12\u003e 100%\u003c/pre\u003e\u003ctd id=td_7_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_12\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of researchers at Google Brain\u003ctr\u003e\u003ctd id=td_0_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_13 style=background-color:#ecf8e8 class=pct\u003e\u003cpre id=p_5_13\u003e 16%\u003c/pre\u003e\u003ctd id=td_6_13 style=background-color:#83cb82 class=pct\u003e\u003cpre id=p_6_13\u003e 92%\u003c/pre\u003e\u003ctd id=td_7_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_13\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea photo of researchers at OpenAI\u003ctr\u003e\u003ctd id=td_0_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_14 style=background-color:#f2faf0 class=pct\u003e\u003cpre id=p_8_14\u003e 7%\u003c/pre\u003e\u003ctd id=td_9_14 style=background-color:#f3faf0 class=pct\u003e\u003cpre id=p_9_14\u003e 6%\u003c/pre\u003e\u003ctd id=td_10_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_14\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ecow\u003ctr\u003e\u003ctd id=td_0_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_15 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_9_15\u003e 100%\u003c/pre\u003e\u003ctd id=td_10_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_15\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea cow in a tuxedo\u003ctr\u003e\u003ctd id=td_0_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_16 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_8_16\u003e 100%\u003c/pre\u003e\u003ctd id=td_9_16 style=background-color:#8bcf89 class=pct\u003e\u003cpre id=p_9_16\u003e 87%\u003c/pre\u003e\u003ctd id=td_10_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_16\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003ea cow on the beach\u003ctr\u003e\u003ctd id=td_0_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_17 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_10_17\u003e 100%\u003c/pre\u003e\u003ctd class=txt\u003ea 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." + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + " function update(b) {\n", + " for(var iimg = 0; iimg \u003c logits.length; iimg++) {\n", + " for(var itxt = 0; itxt \u003c 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 \u003c 10.0 ? ' ' : p \u003c 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", + " " + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + " const value = document.querySelector(\"#value\");\n", + " const input = document.querySelector(\"#b\");\n", + " value.textContent = input.value;\n", + " input.addEventListener(\"input\", (event) =\u003e {\n", + " value.textContent = event.target.value;\n", + " update(event.target.value);\n", + " });\n", + " " + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "update(-15.932409286499023)" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "google.colab.output.resizeIframeToContent()" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "make_table(zimg, ztxt, images, texts_dict, out)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 515 + }, + "id": "30FA-NO7KNOP", + "outputId": "00bf42e2-b5d4-4641-b989-672e61af37d9" + }, + "outputs": [ + { + "data": { + "application/javascript": [ + "var logits = [[4.708954811096191, 10.252580642700195, 11.7265043258667, 11.544998168945312, 0.1305990368127823, 1.5841662883758545, 3.630753755569458, -0.7714423537254333, 0.3038620054721832, 0.9330309629440308, 5.620166301727295, 4.994389057159424, -1.0472909212112427, 4.543239116668701, -2.824063777923584, -5.081585884094238, -6.987623691558838, -2.3818702697753906], [8.171940803527832, 11.17417049407959, 4.432953357696533, 6.457207679748535, 1.3871290683746338, 2.219010353088379, 3.5326123237609863, -0.7986044883728027, -2.825303316116333, 1.1154505014419556, 4.556244373321533, 2.2998785972595215, -2.406578540802002, 4.929878234863281, -1.9565794467926025, -4.75186014175415, -6.194672584533691, -4.585219383239746], [-2.6471810340881348, 3.9581215381622314, -2.9246411323547363, -0.9786444902420044, 14.128297805786133, 4.567025184631348, 11.410842895507812, 1.76983642578125, 4.689402103424072, 1.7969943284988403, 5.961252212524414, 1.7688648700714111, -3.651942491531372, 7.71082878112793, -3.0781638622283936, -2.641646385192871, -2.5421512126922607, 1.7708303928375244], [0.2949942648410797, 1.932803988456726, 3.306114435195923, 2.584017276763916, 2.1844401359558105, 12.037250518798828, 7.571876049041748, 12.411622047424316, 7.1210479736328125, 1.8668016195297241, 5.749546051025391, 3.610408067703247, -3.6449713706970215, 7.799605846405029, -0.9833672642707825, -4.073538780212402, -6.789474010467529, 6.41080379486084], [-2.0145375728607178, -1.0846933126449585, -2.940375804901123, 2.6589603424072266, 5.769614219665527, 4.240757942199707, 7.1254754066467285, 4.012314319610596, 15.3019380569458, 3.6545205116271973, 5.716222763061523, 2.5195000171661377, 9.611126899719238, 9.60205078125, -1.9188376665115356, -5.494052886962891, -5.42561674118042, 8.74514102935791], [-2.7537841796875, -3.5822393894195557, 0.32357507944107056, 7.592442989349365, 0.9119635224342346, -3.0199146270751953, 3.205625057220459, -0.31654486060142517, 4.254082679748535, 0.168704554438591, 13.821099281311035, 2.7867889404296875, 13.22304630279541, 11.5840482711792, -0.5913334488868713, -6.033755302429199, -9.892203330993652, 5.269253730773926], [-3.990476131439209, -1.9461418390274048, -2.113290309906006, 4.550549030303955, 1.036789059638977, -1.5451210737228394, 4.352191925048828, 0.6461118459701538, 5.015008926391602, -1.0808383226394653, 7.48602294921875, 2.4091312885284424, 18.148042678833008, 10.651418685913086, -2.0211689472198486, -6.457880020141602, -9.205273628234863, 3.46785306930542], [-3.516423225402832, -2.8655121326446533, -0.6669525504112244, 1.7112245559692383, -4.795217990875244, -1.1479816436767578, 0.5346255898475647, -2.38808274269104, 1.3803900480270386, 0.5720602869987488, 5.026162624359131, 2.283620595932007, -2.3419137001037598, 9.335556030273438, -1.9925683736801147, -0.6448726654052734, -8.274177551269531, 1.9019465446472168], [-1.2616840600967407, -0.16455139219760895, -1.1095176935195923, 2.245575428009033, 0.3639048933982849, -1.5086716413497925, 5.667880058288574, -0.43802565336227417, -1.7136955261230469, 4.012487411499023, 6.23145866394043, 1.973950982093811, -3.3627028465270996, 7.671400547027588, 11.997048377990723, 8.537683486938477, 18.037771224975586, 1.0273253917694092], [-3.207190990447998, -1.040488362312317, 0.22546111047267914, 3.421861410140991, -0.2788379490375519, -2.991720199584961, 3.0546505451202393, -0.015481832437217236, -0.0560772567987442, 0.8988143801689148, 4.313138484954834, 1.859076976776123, -0.37786176800727844, 8.216859817504883, 11.015033721923828, 19.41145133972168, 15.026349067687988, 1.2226654291152954], [-0.6364625692367554, -3.1751327514648438, -0.228841632604599, 4.9642558097839355, 4.6297149658203125, 7.745206356048584, 6.453392028808594, 4.766838073730469, 8.835647583007812, 3.2101669311523438, 6.487405300140381, 1.499509334564209, 4.8225250244140625, 10.128129005432129, 0.3955494165420532, -0.6124244332313538, -0.9442399144172668, 19.979965209960938]];\n", + "//# sourceURL=js_8a6424db91" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "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" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\u003cstyle\u003e\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", + " \u003c/style\u003e" + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "\n", + " \u003cp\u003e\n", + " \u003clabel for=b\u003eBias value:\u003c/label\u003e\n", + " \u003cinput id=b type=range min=-20 max=0 step=0.1 name=b value=-10.0 style=vertical-align:middle\u003e\n", + " \u003coutput id=value\u003e\u003c/output\u003e\n", + " \u003c/p\u003e\n", + " \u003ctable\u003e\n", + "\u003ctr\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABgAGADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDn5J7qKCNcmNt5Z2PWQNxyKi09xFex+cN0ALKWY8IPf2qGTztzK7E4yCCSeQalvAotpcKSScZHbIrgktbM1jG8BbyKF5z5ZjcJPlSo4+YdvypJ9PiGoW5KKQylelVIG2QsOTlFfP04rVuG81bRgcYfj8QaJPVGsFo0V5dOtv7PYCJThU7e/NF9oOniQh4VJZkxx6irMRBtJ1OctGcD6EVpXWnX1zbB47S4fDxtlYmORj6URdkTI4ltBsmIHkjIjYnHqKx49KjC7wXUnGMMetdncQS20L+dE8JWKT76lT+tYO3CxLnstbQZlIhjhurS2uxFdTjMaquXJwSw5H4Vbi03WLfT/Nj1CZWydyt7e1XrYqLlcuyksoG0ZyeeK6W9gMelIZHLNMu70xlh2qpzskKOsmjhhHqkjSO0kErKFBLx88/SoGS9Eu8WNu5UEAqWHNdg9qu6d1XCNPt/BRU9raJ5cZKAgRM5B9c5zUOdtS1G5S1PFvqs64Byd4GR3FQSXC+WVZWA2rjjqRVjW9BtNeWC/ZSJXhA3qPSudbwfcqrCJWz7OelNxTGnYuozGRk8p8BWH3T9a7Pwh4TuvEttDcTGS2so3B3lfmkI6hQf51wvh3wtq03iW1gYzpbySbXPmcBccmvo6y22EEUEShIkUKqjsBXLipxppJPUznVkrpFXTvDmnaIv+iW4D95ZMO5/Ht+FT6nrf2LTbhojidYnKEjPzAHH61q+bFjOOtRT21neRlJoUdSMYZa4W5XutTlu3K7MLTtai1LR7P7csU7ywIZC6DDEgZ46VR1DwH4b1n5lsPskg+7LbHZ/470P5VuR6BptqF8iMRqvAUMcAVo27QxrtB6e1LnmndOwWlzXR4p4l8GXfhYx3Sy/aLUvt8wLgj6+h96ddtxZQM5OdgYNyeOa9l1KO1vrKW0uYw8MqlWU189eI7tdH8TXNhe3siy2zgI32c7SpHB4PcEV2UKrqvlb1R1U007yNeUZs7Yk5MhllP54FSxbyskanlwkC/ief0rA/wCEi02RYwuoQgRRCMBwy9+c8VJb6zGJ45Y7yykCvuC+eB7DrXS4SN00afh+U3Gjoh+9GxXn0rbtriSxlaaBV3lCnTOB3rmvDGsyXUstldNI1zEx++Ow4I/OuskhYp8yjgYxmteuhzVJNPyHeG7tDrLuygME4b3zXZi/Hm7Gxjsa4e3gFlm5UFcnGfb/APXWrDdk/eORXkY9P2lyYxctzqxfJtx3oGpDgE4rnkut3INNkuCeOlcMqsoo6aNBN6m7LfYODJuFMj1MZ4NctJe+W3GSO5JqIXxDA59zzWUaje56DoRaOrv9WURqAckmvNPHcVpda99okQF2t0BO0H1/wrYW7e4uCzMcKcCq2tww29z9svQ2wqhKBeSmK9LAQftb+Rw1oJLlOJfQbO6VfI8xtzYYqgDL79elWrfwTp5edLmaRCI/3LLFkFvf0FdDDL4YuVzDeGJjyC42/pVm3tUmYr9oViD8rA9R9K9p3RikmYseg6ifFsl7Z22baV85LgdRz+td1Gkcpa3Vs+UAjsDjLd6gur1dOsWkUYmf5Ige3vUfhvYY5383c2/DfXGT/Opb96wNXjqag0w6irWaEqGUjJ7en61zEEskDvBMCJIyVYHsR1r0fTohbWzSkYdx37CuT8VWcb3DX8IAk4EgA+9jv9azxWFc4cy3QlLUpx3XygDr0zUxmzGSeuKx4bhQB0qdrrIxwK8KdM66TsyO5kJYKpwO+agJyxCn5elSS4b584qJOXGOnvUwpNs7vaJI1tEsGvb+OLHyZ3SH0AqT4t2c66FbX9o2xY28qXA/hP3f5EfjWpoTCBOPvN1Nb+o2UOt6HdWE6h0lTofUcj9RX0OFoezp+bPJqT5p3PnezU315Eka4/iYD8q7WCa6iyxtovKQc7c7jVy38Nafp90XghlSYgAFiSPwrUjtWjAjwrf73GPTNUNtnKeJdfSGGW9Y/u4/khQ9zXSeACt4jxjDDImZvUEf41wfiyzS48KyvGN0kDiVvYdP5GvUvhnosuleDrN7tCt1cRiRweqr/CPy5/Grp07u5EpaWOrmyBj2rntXhaSJgPSuglBIJrPmi3Kc10NmZ4/qct9plwf3JnhJzgcMv09agj8T2uMSGVCP4XQ8fjXoWp6UsoPy1zNxoMbFkKLnGelcVXCwm7mqm4oyE16KY7YFkcn0U4/WtnSori5kDyDaOy0+x0NUZFCDpnpXVafpflKOOlKnhoQd0W6jkizp8JVRXRWrFcVQhg2gVowrgV1pGLFdYcOj2iMOm7AphtrCTbvthnpyvSi8dYVEjybE7k9K5y+8TJFuWyDSyf32GFH+NLlbehpzJK7OK8MWTa1qzW0wWW2Yl5gV6KOMH6mvX1wEAHSuW8E6GulaW07KfPuSHfcckD+Eflz+Ndam0ffGa0ijEhdc8VWkj4xVwDcSfU1DPDvGRncvIwabAzprYMvSsa6sSS64xuwv511KRMYgXUBj1HWqUkCvdgLjcFJP06VDQ5OyM61sAJ84657VqRwgZAHIqeGAdx75/Gpyo7UJDWisQBMVYjHFN25NTrDvjJVgGA9elUlcUnZXMrxLpkmr+HL6yhkaOd4iYXU4KuOV/UCvl6PxBqunapcrNdyuJjtlDMfzx2xX1rHuKDcK+cfivoenaT4juQgmSec/aEBVdhDZzg9fvZoehLvdM//Z\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABgAGADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDUvdfs9Hv2hjZcyRnCjqWry+9cMW9TzVHTxc3N6ks0rPOSSS5q5dJcEH9zn1KmnCLijOVRTenQr6aYDDdRyqPMldY4z6ZByf0H5123gaZv7PmR2+6cbc9T1P8AOvOAXhu95UqfvAH1BzXe+Fp4obu8BYbjsCL7Nk5/E/yrOojopy0SLPiQh54VPQuB1rmvHUpfS9Kyfny8Z9sY/pit3xDJieM/3XB/Wuf8aFA1pASMiXzQB/dI/wDrCph0CZoQuI/DSPuCEDy0ZuiSKMrk9gy8fVRSDxE3/COQG5bzbiC45YHG5QpOM1nafM17pMsEZH34yQzY4BIP49KyLm2Iu0RSSGXO0epOK1lHUzpu6Xc0Yrt5luNSnILs5fB/PH8h+Nen6MhXRY5WA8ycCRvx5ry94g11Fp8ZyqD5yO+D/j/IV6TYXYTw/BK5AWOM7v8AgP8A+qsqr7G1OLS16XOBu5Tc6/qMgIO6dgM+xx/StvTR90EjoBzXJ2UzPO0rxk72L9O5Oa6nSpU3KWxjoM9q6Fojle5i3Vr9m1m8t7Y7zG5XJPqabHpLYGZSjHn5Qav3ulahBqOp3vkOIYsF2Ix1x/jT7aUMhkfOT/L0qabutR1YKD066nO3+mSxFZ03uFPO49R7Vb0jUHg1iyRiqpIVSQt9Dj+YrYlSNgEIVyf5msabSZEuG+zkBiMtjnB6j6VUo3Qoysza8T3kcEi+Yx3ddves288PazrVtbXtnpt5dlhhmjiZvpz0rrfA3h5PE/iZb/W7fd9kgDNC5BWWTOASPTvj1r2SQhVCrgADAA6AVnGFkauprc+ZLbSdQ0u+8q8s7iCVEJxLGVU5x0z1q01pun8xVIjiDIZOyttJB/Q4r3++igu4GguI1libqjjIrxvxfbGz1eHRoIjHaRzrMzdTICByT+OKKl0Xh+Vt6anOWAfzBdMCpl5A9FHAFb13qIi8GXEedrSOYV59ev6ZrLukEM3ljjyyUAx71e0nTZLyR475ECKSYo2Ofm9SP0qIx5rMus/ZuUX3/NHP6ZcG1QeXucY6MvH511FlqluP+Pq1MIJAZimMen0qaKyhDrbuFVGyNrrx+H+FMuljtrdQchIm2Sbhnap/muePofauixx3PS7N7XXtHkZlVoLtCHA9CK8m1G1k0fVLixcnEL4Df3geh/KtPT/FK+HdIhhhbzfMQ/ITyjjr+FczquuS6xqJuJdoKqMn8ayhJ3NJpWNGJkj2bvvsCQajtZNiuSRk/Mf8/lWfb3StebmOVVdopXmwLhhkAA4/KtrmNjrvDusroJg1UXCmeeUQGEjiRWOFXPb1zXcz+LmvVBsJbWE45S7Dgg/VQQa8mtPD13q0BuLK7VMMAYnbAIAHQ44NdDpEUtvixuGT7ZCoLRCQO23+EnH4VyupJLmR3KlF6Ha23iSGC9gtdWurZppyBGtqr45/vFgMD6Vh+N9BMhS9sZjNcOzeZ5hyQCcgDHYdhVlvDRu5zeBGM6IFRuBt69Cen4Uo0htJRUlumnZ4cvuP8QPUfnUSqt25loXGnyXcXqcW1nJLZi8SGOZw2+RcfMD6g9RWhDLHcRW2oQOweNgsmRzj3q7dlbS5LRgKx5Kjoff9DWarR6ZqLkc2l1gkDtn/AOvXbZI4G3J3ZqX9tHcRSFThh/461ZVy8cllcb8EiHZJn3HBq9cXCxTZEm6ORARXH6vfiSeaCA/KwUSEHjjtUtgkYMsUl1K5VsEZOCap3O9IVcnBfjFbHh+GPUdQjjnkESM3zuTgAdzVDX2t5dXlg0+QyWqyFImP8QqEina5HZztFAxYs3PXGcCtD7UskZ2n7w6/hUNzbNBY+R5nzq/liNRjnuT60+/t47N08tmKso2kj7x6HHt/OmnYpx1Oi8MILmzO3UTbTHjyyMq2BjpUbRajD4qC2s2brahe4VWHmKCDtGDg9B+Vc5Y6qLNLiOSBZA6MPoSOo9K6eG1l0xLLVrGKaW5gC5TOeG6nHesbWZ0Kd0j02G/16eECOyiiVv8AlpLIcD3xjNY+p3E9tcTC7kF0yRcPGMLGcHoP8anm1qBNP8691lO2Fgjxz2GTmuCuddjvisUdxKZseWz7hgjJLFvfk1hOLnaKNuZRuzWur5V1+OKfIilttp56HJ5qF3F2L2AtvaF8ofbv/KsnUdRjXUFmd8hV2jjnHWo/D9013qk742hgXbPfJrucrHBCDk7I079rhfDyyn5GRgi+u01zK4U9e1ddr7Y0WSIHLZRmHtmuMd1jUszYNAhp069WBfLtpXWQ4wgOSKJtM/s66tzPIpkRDJKq/wADdl+tdxrWow6RELW0fMu0jPoDXEakk0IiEuTLIvmEdSM9M1nCTkrmsoxg7dRdVuRLq1wyA+WTlQPUjkVaubUnTLfUZ23FyOPTtUTJGYreQBWccs2Dkn3q1JcK+kCAgtsnyqeueg/OntYH717hbaNG6ysybicNn6jNdDb+JW0GyhguNNhvowNse87WUAdM4PHFWNNtCsUqMDkFev0rF8XRLGLeLeEBDsc9wB0rLmbnYvlShcv3es3Hi+2WIWkNnaRMHEEI6n1J7nrWXY6dDb+IY4Z1BhdTwegOODWh4RRVG09JIyPxHNN1seVqEAj/ANaXwAPSpTblYuUVGJU1vRDa6dBfRncjqS6nqCT1qppDLaNBIek84X/gKjn9SPyrtNYQJa21sRuURgEdj/niuS1CzbS9ftLTJMcWwIfc/Mw/M1snsiYpOMm/kbXiAMuhyysuGeROPQDOBXD3aGVRgV3XiNzJohYjAMqjH51xhOE9hWhyjpppJLgyyNuZzkk1qSaesT7mbzHfkuf0rLt41lQFsH610MLB7OInrtHH6VnJWRrF3ZlzRhBt246Gs65Ywj92T8xGDnoa278iS2hkHBy0Z+oOR+hrEI3XCqwypOfpRF6G1Sn7yXdfij0LwzNLfWMkk0R3cHeB8rdelcj4/l3a3awZ+4mcfU//AFq7PRdTs7axtZriVY2lQxIuc52gcAduledeI7z+0PFDzlcAEKB7Cs0vfuJ/Dys3tIlaDT0nU7WhO4H1FWbNW1fWY9QXiNmxFk8Ke7H2Uc/lWCl60Vv9jj5eYhfwrT0y8GnYtWYbZlIjP91M8/8AfRGfwq+TX1JlN8qb6aHVu8V3q6dfs8Y3Ef7KjP8AID86ow2X9r6ixnGW3GTcP4W65/KktX/4ll1cN1kIiX6Hk/oB+daeixmCymmcYZ/lUk+v+QPxoXxNjlpTjDuZWv4/sFuASsi5ri/LbJBwAea7bVR/xJbuI5yjAjP1rjJy5Cknj1FanMf/2Q==\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABgAGADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDp503W8o9UYfpT7b5raBvWNT+gqd48ow9Qah0/nTbQ+sKf+givbb99en+R5K+F/wBdyjq+oLBE8CttOB5jeme341kWtr5sz3E5DeWdsSjovHX3NJ4htLm61G4gjH7uQJk5wAO/44rQs4dlnI8rCOPd99uB07etc7knUfN0NuV8i5epVKmWNVVSSQQAPoKqzWdpYRf6bJvunTEcCckH1Pt79K0jNJgR2aGCLoZpB+8b6Dt+P5VkSwLAJCASzAlnY5Zj7k8mrvOe2iIShDfVnpVsZo9NtmMeV8lOV57CtGynDWa9CCT/ADqpYyldOtf+uKfyFIzLvZl+VieorxD1S1cXDJbSqDuUqwweorjpdOa2Rr60cwXa4AcDIcZ6MP4h+vpW/JI6xsZGHljJZwM7R3OO/wCFLIkVzp5kt5IriDcD51u29fx7qfrTGZ+m6y0lwqTKLTUNvCk5SYD0Pce3UV1theRXqHaNsq/fjPUf4j3rjr7TorlhvUPGI8j69iD/AFqnaand6YYhdyO6Ku5bpR88Y6YcfxD3/Md61hUa0M5076o1zHxWfpckTaXCFdWaGFTIAeVGO9RNrzP59tNZOkuCqGJtytkcHdwPxzXNyX8+j+FprqE/vbi3jiAx06f1FdksUrpxOOGHk00zUu7u2mumdJ4wzhcKzAEcenrT7SISQSSSNvMbERsxyFGB07etcBpXhm61WNrtrplHKryfxrUsfD+qaLNHO1z59r5gWSMnJw3GfzIrlhiZOd2jtnhYqnZM6m4uYI+A3mNnOE5/XpWRcTNKjBYwoIxknJqywIbhKyNT1qx0xhFcSEyMOVjXcVHvW8q0n1OWNGK6HplsNmnWxWRlxCucnI6D1rzjV/iPqjalLHodgLm0t2KyzeWWD+4x0rb/AOElnv8ATCmlpb3EDW/lrL5hV0fb3XHavP8ATm1DR9Ne2S/iik2Kyrs3BWY5O449PWuFux304c256j4Y8V2niTShKrLFOmFmjJ4U+ufQ1rz6VGoF1bl7e4LDE9u2xvzHX8c15x4CjGkXmuPfN5ccsw8tghKEcknIGB1712hvtNjjM0d9BEufvJMFGfzotfYl6Oxfaa8hdxcRLcrt+aWICOT8V+43/jpqNkt75WSBwz7ADGQVcDOfunn8Rke9KlzcbCwkSZGHVgOR9RUd0IZQplhKkgEY+bH9RQNNHKWVzBJYXDghzHFEgKjgDd2z/nil1INdaFdWHyqFkRYyo92IH/jtUtR1ICLUbe8PkMyxoAyYAyc8AZ7Vee5hjS3iTMr3ageaFIBKmQlqV29Xvr+pMLc3zRWlnfQ9KtYYEy+BvyhYbj1yR0qw2vLPJFp8lvsnnQOcN0Ge3r0qvc6zA0QUqC7cDd7UWmqW12FHkMsojZFZuD1/+vU09zsqL3WX2bL15+1lp0t7Pd35naeSSRsKuQAGIAH5fSuy/dkk8/gxrF1e/W0vIoreOLf5Z3GV8Dk4/PmuipfQ4qCV2L4RENvrd3BayP8AZWt45h5gwQ2SKqa+9yuoMNNkiaxucAnB645+oz0zW/4dEV1GL5IQGdAg2jPQ5P6morvRobrUVxcGKANuMYA4PfB7Vxzq2laxvFxTd2dbp2nmw0wQR7jjBLN1YkjJNed32k6n4q1CW/muxaQCVooUA3EIrEZ/HBNdvDqUFncRWPnS7BtAQNnHoM+tYl1C+n6pcQJOAoZxsLdOSc/XmrU9LoKceaXvEXhWzudM1a40K5uZZo/J8+2lEjKfvAEYB969Dg0RpLSNzd3QbkcsG9fUVxmjRWlnqS319qEamO3aNXeUgncwPBzk9P1rau9csbaOJjc3DpKpaMJvO4A84q+bqyZRSdrHKnWdHv8AVHMXlPFb7TJLLOoDADBbDHnGcU2DxXpk0g0+zMcjoZHVih4BckBT9GrzSULKTu+8vR8YIqBJHtry3uIW2Orjp65wauOGUXdu5kqjPfTp9lJnNrAec/6sUo0qxzu+yQg+oQCpIMsoNWEZSxUMCy9RnkV9DyR7Hkc0u5z+oW8dvrViEQrDIcOixblPpk9q2BYWrMH+zQ7sYzsGa5rxbK1vrehPuIVrjaR68j/E108l6ttaLNt3guiYBx94gZ/WuRxUJSZ1U7zUUty1EgjAVRgegrL0by7nSopJFSQOWblfVj61r5ANYujj7PoNv/sQ7v0zTcVe7H5DI/DMB1c3hc+TnesYJHzf4VS1/wAPXd1rjXlk0bJMoMnmHGG6H+Wa6O2nMllFM4AZow7AdBkZpqXMbWkdw7qiMiuWY4AyK53h6TW3mdHt6ie/kZmkaDJZ7pLuK3klUEQlWJ2gjBByPpzWZLqlqzWN5DKkwt7cI5hP3JMtuB9MD2712EcqSRB43V0PRlOQa8BnubjTPEN61tI0UsdxKAV9Nx4Nc1fCwUfc0uaQrSk/eKVwvl3DYPHQiqzf6xR/tA/rV/Wp47jXLx4ABEZnK49M8VnuP30Pu4H6iquQevXj202tSwajfz2YjVPspUkL0yW+ua07m8+yahqH2aJPtMgt4llYk7mbIBI6ce1VNRk1G8Nxpn9lrLHIAsFwfuoO7E+tT3+meTa3btHNcrIIERLc4kXYMbsmvdUlZX/rY8Zxd3b+tynqFk1/diK/me6l0+4WSNo9sSk7QcN6DvWo6yXmlOtuokk3o6qGABwwPX8Ky5bC2sdMSW/RlkuLrzEikYyEfLgA9ieMntn6Vt6c8VhYRy3Mix7hkAgA/kK8upUvWnGW1j1aMOWlCUVqmX7WS4kj3XMKwyEn5FfdgduaqwQZ04W+duYvLz6cYpl1r+l2cQluL6GJWGQGbBP4daxLPxvp1xbtMLa6VFbGdqnv161o6kIx957kqEpS91bHSSgxadIiHJSEhfwWsO3vEufCPkjBlt4I1cHkEjH5jg0+TxPplzYXISSRX8p8K8ZGeD+FSaPPbyaNaSW6jy5Ylfp1yM5NZq1V8sXpYqUeWL5lqaOnQyWkEok8oNJIZNkQwi8AYH5V4r4nQReLdUUf8/Bb8wD/AFr2gS5rx/xzC0fi26YDiRI5P/HcH+VOvBRppIVJ6mTNF5eWONzcmqpG65t19ZUH6ir94Q0r46A4FU4ADqFoGIC+emSTwBuFciNme9xS4TGad56qCWYKo6knAFcnqvjbS9Mj2wyC7mIyojOV/OvO9Y13UNduma4nb7MGOyFflQDtkdz9a7pVox21OWNJs9I8ZXi39law2Eiz4lbzGjJwvy46j61k6RFaWumcsqm3JeRS2fl9eetanjhILTSdNs7S3jhR1bbsGMsNvX16mvO47dVJcjH19a8ud675tj0KclRXLa5YvZjqF/PdMCA7fKD2XsK0tJfb4X1KRFXzLcsRnkZABFZi42Vs2VsLfQL4Ak/a7Uy49CCyn9AKKsVypBSk+ZsoWkl9qfhDU5SYmKqwdsENjg4GOOxqrp3jLXLC0htoXt3hiQIivF2HTkEV0Hw9slvdP1aF5VSNVTfuzjaQwNcPGFHCnK54J9K1jLldoaGTTkry1Oug+I2qJxLYWr/Qsv8AU1kazrJ13UY7x7VYWWMRsobcDyef1rNApypkFR1PTFaOpOSs2SopO6P/2Q==\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABgAGADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwB2KTFS7aUJXuXPAsQ4NJg1YKU0rRcLEGzJ6VIbTEZdqcMqcjrQ7u/DE4ouwsupTKCmMlWStMK1VyLFRkqIpV1kqJo/ancloqFaryLzV8pTRAXYKByTimpEuNzoNlOSIswAGSe1TrFk9K0LREi+YqM+tczlY7IwuNtNHMwDSZUelXJtEthGwDHIHWpRckLkVzXiLx9pnhy5S3uxLLK67tka9B6k1zzq8urZ106Kl7sUMeAqTnpmoigzVW38SW3iGznudKkj8+P5jDcfLge+Og966/wxptj4g8Ow6r9ivI/OB2q8q8+4IHIq3ioK3mZfVKjb8jlylNKVpa0tpYa9Lo9tFcG6SNZCZ2VUwe693x7AfWucu/FWiWdwYZrvDA7WZULKD9RVLFU7XuQ8JVvZIvFKaImZgqgknoBWvaaY95GkyEeS4DK/Yg9xWkiWOl/MGDS+3JrR1V01M1Rf2tDDt9CuZ+XXy19+tXodMtbACSZh5qnIyabc6pcysdp2L2ArOkDyMS7FifU0vfluP3I7K5rXMsdrA0sroir3ZsD86bFfWU7eXBewzSYzsifcR+VcTbeKL7UNVt55wsNqmdkf97tk10J1u2gmdIVihTALbECj8cV8xVzatzNR0Pp6WVUVFc2poTXF6WKw7VUfxMM5rn/EXh6LW7dGvWBuFU7ZkUZX2OOoqzceJ7eG2mY87VJ/oPzNUrXUTbaZFFJ87Fck57nr+tcssVWm/ek2dsMLRh8MUjidO8LXlldsp1KNGbICxq/I9+AMe1e1fDfxbDd6hJ4dW2mt0t7cPHHIoUpjHYHowORj0NeaSXa/a3m5VeeT3q74E322rPfKjeekbbJiP4T/AA5/StVXakpSM5UE48sSXX7h/HXi6eGGVra4tFlK3KrnaCVAU/gT+dTaT4C0+302WDVYI725aYOsysyhVHOMepPB7Yp/gXS5I73V72UfMZRAD645J/Va7Tyq9fC0ISpqctzxsXiJxqShDZFaSWZ0Ee7bGBgInAAqsYa0DFSGL2r0lJLY8xxb1ZnGH2phhrSMQHammP2o5xch49eSm216+tQd0FtuWMjuN3H6VXbV1GVds9/qewrak0iSbxPd2LLtS6VnUgcD+IEfSuO1Wwn0u+lt7lcOjYODkV83LCrmba62PpaeMukk+l/kxbvUpWDRq5Kltxb1IH/666aDUodPtIpLotJEsSDaOTkjP9a5TTore4vYRcsTAHBkRThmXuBW3rOmGKeJoXAguCD5YbJXj09KzqxhzRgzelOVnInTXrO8SSdoWiih+b5jncewHvVnQtfuLWzh+zRvcyXDyI9vhhh8qVKnac8cYrn59EuUi24ITOQCO9dr4Wsr610aynhMWI3lKrgFg7YG78gR+da0oU51FFdX+hjXrSp0pTl0R1Xg2bVbiK/k1K0e0TzVEELJjjHLZwCcnuRXUYriJJtakJJupB7A4/lVd4NSkzullb6sTXuwpckVFM+aqYuM5OTO+xSFa8+FnqC9JJAfx5pyw6tyBeS4z0yavl8yPrMex3pAppArjIk1oMAt3N9NxNXBb+ImXPnzYx2Wp07lKsnsmQXFoyeObZnKrgAIMklwRgnHQYzWPd6A+s6/4gs/L3Swq86AnG92AEYJ9MD9a7bULGI+JNMvC5DkNGQDgHAJH9aq2csMfxG1OEEiSWwhfk9cEg/pivLSd2n3PYaSs12sLpPhXTdP0+CFLGASiNRI+z5mbHPP1rzjULHHxDXTnLRo92IlI6qpORj8DXtfJ6FvwNeT+J7ad/i3pywo5lknhlB7EDGT+AB/KirHmtoVQ9zmd9yXxf4c1LRrZ7uzY3dgibpGcgPGPfpn8K1Ph6Gv9AkmZdoWXyhn0Az/ADY11niS1N34a1OBVZi9u+B64Gf6Vzfwwif/AIRSWV+Flu3KD2AA/mDUxpqE7pDnJ1KfLJnSiy7llH4U/wCyW46v+Qqyy81GR9K6OeXc5lQp9hnkWw7OaaI4VPEZI9yKfjPepUtJpOiHHvxRz9ylRj0RAViP/LID8aFxGMKKvDTZP4mAqRdMUfeb9al1Ilqml0OP8W6l9gmsJlZfMifdGpBO5jxyAM4xnNclqPg7xTqWsf2lcXNqbhl5VZSPl6ADjGK9EuArsuRubpwM4FH7+WUNHHjjA3n/AAqeflbLcOZI85Twr4rtnMkMUisO8VyAf51Xmvte07XLOaYSyarFFIB5qiRlj4479s816vHZzy4MkxRQfuoMfrXD6ib218S3NzpkDnUVcxQyMhYJk4zkjABHenOu5NadSYUVFPXoQ2/xIvQNt7awSoQVbYSh/qKoeGfHcOh2B064s3eOOVmDRMNwDHPT65710l34D1mWzN1cXdjqOoffaCe2AQn0DDB/PArzbUvDesaJerLfWhtvMzgj5lYegI4/DNaXjKSsZe9GLuewaf4q0TU1UpqMUJP8M/yEfnxW9bR2k43RXCTr3KMCP0r57Cn7Mze/3icA/wCfatTw14juPDuppPbN5kLECeJTgOv9D6GidJ20YRrd0fQCRJGMBVH4U7GejVzemeNtC1LylW+WKWTAEUylSCe2emfxro1IHAz75rmaa3OhNPYcB/tHFKeCOGJNSwojoWLZYNjYpAI9+TQxjEM0mHQRqSS7J1Hbg/rTUHa4uZXsf//Z\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd style='width:96px;line-height:0'\u003e\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e\u003ctd\u003e\u003ctr\u003e\u003ctd id=td_0_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_0\u003e 1%\u003c/pre\u003e\u003ctd id=td_1_0 style=background-color:#edf8ea class=pct\u003e\u003cpre id=p_1_0\u003e 14%\u003c/pre\u003e\u003ctd id=td_2_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_0\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_0 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_0\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003etufaha\u003ctr\u003e\u003ctd id=td_0_1 style=background-color:#bde5b6 class=pct\u003e\u003cpre id=p_0_1\u003e 56%\u003c/pre\u003e\u003ctd id=td_1_1 style=background-color:#9fd899 class=pct\u003e\u003cpre id=p_1_1\u003e 76%\u003c/pre\u003e\u003ctd id=td_2_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_1\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_1 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_1\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eฮญฮฝฮฑ ฮผฮฎฮปฮฟ\u003ctr\u003e\u003ctd id=td_0_2 style=background-color:#90d18d class=pct\u003e\u003cpre id=p_0_2\u003e 85%\u003c/pre\u003e\u003ctd id=td_1_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_2\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_2 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_2\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eะฐะนะฟะพะด\u003ctr\u003e\u003ctd id=td_0_3 style=background-color:#94d390 class=pct\u003e\u003cpre id=p_0_3\u003e 82%\u003c/pre\u003e\u003ctd id=td_1_3 style=background-color:#f5fbf3 class=pct\u003e\u003cpre id=p_1_3\u003e 3%\u003c/pre\u003e\u003ctd id=td_2_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_3 style=background-color:#f1faee class=pct\u003e\u003cpre id=p_5_3\u003e 8%\u003c/pre\u003e\u003ctd id=td_6_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_3\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_3 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_3\u003e 1%\u003c/pre\u003e\u003ctd class=txt\u003eapple na nรณta ag rรก \"ipod\"\u003ctr\u003e\u003ctd id=td_0_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_4 style=background-color:#78c679 class=pct\u003e\u003cpre id=p_2_4\u003e 98%\u003c/pre\u003e\u003ctd id=td_3_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_4 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_4_4\u003e 1%\u003c/pre\u003e\u003ctd id=td_5_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_4\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_4 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_4\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003een kold drink pรฅ en varm dag\u003ctr\u003e\u003ctd id=td_0_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_5 style=background-color:#88ce87 class=pct\u003e\u003cpre id=p_3_5\u003e 88%\u003c/pre\u003e\u003ctd id=td_4_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_5 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_5\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_5 style=background-color:#f0f9ed class=pct\u003e\u003cpre id=p_10_5\u003e 9%\u003c/pre\u003e\u003ctd class=txt\u003eeen hete drank op een koude dag\u003ctr\u003e\u003ctd id=td_0_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_6 style=background-color:#98d594 class=pct\u003e\u003cpre id=p_2_6\u003e 80%\u003c/pre\u003e\u003ctd id=td_3_6 style=background-color:#f1faee class=pct\u003e\u003cpre id=p_3_6\u003e 8%\u003c/pre\u003e\u003ctd id=td_4_6 style=background-color:#f4fbf1 class=pct\u003e\u003cpre id=p_4_6\u003e 5%\u003c/pre\u003e\u003ctd id=td_5_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_6 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_8_6\u003e 1%\u003c/pre\u003e\u003ctd id=td_9_6 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_6\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_6 style=background-color:#f5fbf3 class=pct\u003e\u003cpre id=p_10_6\u003e 3%\u003c/pre\u003e\u003ctd class=txt\u003e็‚Ž็ƒญๅคฉๆฐ”้‡Œไธ€ๆฏๅ†ท้ฅฎ็š„็…ง็‰‡\u003ctr\u003e\u003ctd id=td_0_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_7 style=background-color:#83cb82 class=pct\u003e\u003cpre id=p_3_7\u003e 92%\u003c/pre\u003e\u003ctd id=td_4_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_7\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_7 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_7\u003e 1%\u003c/pre\u003e\u003ctd class=txt\u003efoto cangkir panas dina dinten tiis\u003ctr\u003e\u003ctd id=td_0_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_8 style=background-color:#f4fbf1 class=pct\u003e\u003cpre id=p_3_8\u003e 5%\u003c/pre\u003e\u003ctd id=td_4_8 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_4_8\u003e 100%\u003c/pre\u003e\u003ctd id=td_5_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_8\u003e 1%\u003c/pre\u003e\u003ctd id=td_7_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_8 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_8\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_8 style=background-color:#e6f5e1 class=pct\u003e\u003cpre id=p_10_8\u003e 24%\u003c/pre\u003e\u003ctd class=txt\u003eเคเค• เคคเคธเฅเคตเฅ€เคฐ เคœเคฟเคธเคฎเฅ‡เค‚ เคฆเฅ‹ เคฒเฅ‹เค—เฅ‹เค‚ เค•เฅ‹ เค•เฅˆเคซเฅ€เคจ เค•เฅ€ เคœเคผเคฐเฅ‚เคฐเคค เคนเฅˆ\u003ctr\u003e\u003ctd id=td_0_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_9\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_9 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_9\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eo imagine a doi tipi care au nevoie de apฤƒ\u003ctr\u003e\u003ctd id=td_0_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_0_10\u003e 1%\u003c/pre\u003e\u003ctd id=td_1_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_2_10\u003e 2%\u003c/pre\u003e\u003ctd id=td_3_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_3_10\u003e 1%\u003c/pre\u003e\u003ctd id=td_4_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_4_10\u003e 1%\u003c/pre\u003e\u003ctd id=td_5_10 style=background-color:#78c679 class=pct\u003e\u003cpre id=p_5_10\u003e 98%\u003c/pre\u003e\u003ctd id=td_6_10 style=background-color:#f2faef class=pct\u003e\u003cpre id=p_6_10\u003e 7%\u003c/pre\u003e\u003ctd id=td_7_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_10\u003e 1%\u003c/pre\u003e\u003ctd id=td_8_10 style=background-color:#f6fcf4 class=pct\u003e\u003cpre id=p_8_10\u003e 2%\u003c/pre\u003e\u003ctd id=td_9_10 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_10\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_10 style=background-color:#f5fbf3 class=pct\u003e\u003cpre id=p_10_10\u003e 3%\u003c/pre\u003e\u003ctd class=txt\u003efoto para penulis SigLIP\u003ctr\u003e\u003ctd id=td_0_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_11\u003e 1%\u003c/pre\u003e\u003ctd id=td_1_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_11\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_11 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_11\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eใƒญใƒƒใ‚ฏใƒใƒณใƒ‰ใฎๅ†™็œŸ\u003ctr\u003e\u003ctd id=td_0_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_12 style=background-color:#d3eecd class=pct\u003e\u003cpre id=p_4_12\u003e 40%\u003c/pre\u003e\u003ctd id=td_5_12 style=background-color:#7ac77b class=pct\u003e\u003cpre id=p_5_12\u003e 96%\u003c/pre\u003e\u003ctd id=td_6_12 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_6_12\u003e 100%\u003c/pre\u003e\u003ctd id=td_7_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_12\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_12 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_12\u003e 1%\u003c/pre\u003e\u003ctd class=txt\u003efoto av forskare pรฅ Google Brain\u003ctr\u003e\u003ctd id=td_0_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_13\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_13 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_13\u003e 1%\u003c/pre\u003e\u003ctd id=td_2_13 style=background-color:#f1faee class=pct\u003e\u003cpre id=p_2_13\u003e 9%\u003c/pre\u003e\u003ctd id=td_3_13 style=background-color:#f0f9ed class=pct\u003e\u003cpre id=p_3_13\u003e 10%\u003c/pre\u003e\u003ctd id=td_4_13 style=background-color:#d3eecd class=pct\u003e\u003cpre id=p_4_13\u003e 40%\u003c/pre\u003e\u003ctd id=td_5_13 style=background-color:#92d28f class=pct\u003e\u003cpre id=p_5_13\u003e 83%\u003c/pre\u003e\u003ctd id=td_6_13 style=background-color:#afdfa8 class=pct\u003e\u003cpre id=p_6_13\u003e 66%\u003c/pre\u003e\u003ctd id=td_7_13 style=background-color:#dbf1d5 class=pct\u003e\u003cpre id=p_7_13\u003e 34%\u003c/pre\u003e\u003ctd id=td_8_13 style=background-color:#f1faee class=pct\u003e\u003cpre id=p_8_13\u003e 9%\u003c/pre\u003e\u003ctd id=td_9_13 style=background-color:#edf8e9 class=pct\u003e\u003cpre id=p_9_13\u003e 14%\u003c/pre\u003e\u003ctd id=td_10_13 style=background-color:#c2e7bb class=pct\u003e\u003cpre id=p_10_13\u003e 53%\u003c/pre\u003e\u003ctd class=txt\u003eOpenAI:n tutkijoiden kuva\u003ctr\u003e\u003ctd id=td_0_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_14\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_14 style=background-color:#8ace88 class=pct\u003e\u003cpre id=p_8_14\u003e 88%\u003c/pre\u003e\u003ctd id=td_9_14 style=background-color:#a4da9e class=pct\u003e\u003cpre id=p_9_14\u003e 73%\u003c/pre\u003e\u003ctd id=td_10_14 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_14\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003esapi\u003ctr\u003e\u003ctd id=td_0_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_15\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_15 style=background-color:#e9f7e5 class=pct\u003e\u003cpre id=p_8_15\u003e 19%\u003c/pre\u003e\u003ctd id=td_9_15 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_9_15\u003e 100%\u003c/pre\u003e\u003ctd id=td_10_15 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_15\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eune vache en smoking\u003ctr\u003e\u003ctd id=td_0_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_3_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_4_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_4_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_5_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_6_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_16\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_16 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_8_16\u003e 100%\u003c/pre\u003e\u003ctd id=td_9_16 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_9_16\u003e 99%\u003c/pre\u003e\u003ctd id=td_10_16 style=background-color:transparent class=pct\u003e\u003cpre id=p_10_16\u003e 0%\u003c/pre\u003e\u003ctd class=txt\u003eืคืจื” ืขืœ ื”ื—ื•ืฃ\u003ctr\u003e\u003ctd id=td_0_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_0_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_1_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_1_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_2_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_2_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_3_17 style=background-color:#f5fbf3 class=pct\u003e\u003cpre id=p_3_17\u003e 3%\u003c/pre\u003e\u003ctd id=td_4_17 style=background-color:#e7f6e3 class=pct\u003e\u003cpre id=p_4_17\u003e 22%\u003c/pre\u003e\u003ctd id=td_5_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_5_17\u003e 1%\u003c/pre\u003e\u003ctd id=td_6_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_6_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_7_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_7_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_8_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_8_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_9_17 style=background-color:transparent class=pct\u003e\u003cpre id=p_9_17\u003e 0%\u003c/pre\u003e\u003ctd id=td_10_17 style=background-color:#75c477 class=pct\u003e\u003cpre id=p_10_17\u003e 100%\u003c/pre\u003e\u003ctd class=txt\u003eslika prijenosnog raฤunala sa zakljuฤanim zaslonom, ลกalica cappuccina, mlinci za sol i papar. Pogled kroz prozor otkriva jezero Zรผrich i Alpe u pozadini grada." + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + " function update(b) {\n", + " for(var iimg = 0; iimg \u003c logits.length; iimg++) {\n", + " for(var itxt = 0; itxt \u003c 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 \u003c 10.0 ? ' ' : p \u003c 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", + " " + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "\n", + " const value = document.querySelector(\"#value\");\n", + " const input = document.querySelector(\"#b\");\n", + " value.textContent = input.value;\n", + " input.addEventListener(\"input\", (event) =\u003e {\n", + " value.textContent = event.target.value;\n", + " update(event.target.value);\n", + " });\n", + " " + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "update(-10.0)" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": [ + "google.colab.output.resizeIframeToContent()" + ], + "text/plain": [ + "\u003cIPython.core.display.Javascript object\u003e" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# The same checkpoint also works well across a wide array of langauges.\n", + "# Note that we adapt the bias a bit to correct for preponderance of English.\n", + "make_table(zimg, embed_texts(texts_dict.values())[1], images, texts_dict.values(), {**out, 'b': np.array(-10.0)})" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "vd9_TZRxjtFK" + }, + "source": [ + "# Naflex\n", + "\n", + "Example to compare the aspect-preserving NaFlex preprocessing with resizing to fixed-resolution square." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "obR29Tzjoy6Q", + "outputId": "b7a62c0c-de7f-4418-ef33-b5f70e16f5bb" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1000, 2853)\n" + ] + } + ], + "source": [ + "# Example document image with height \u003e\u003e width.\n", + "img = PIL.Image.open('siglip2_screenshot.jpg')\n", + "print(img.size)\n", + "# img # show image in original resolution" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4ccyCKIiwImu", + "outputId": "eb24e2ee-4cea-444a-b850-6d9c13f849c2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1024, 768) (1024,) (1024,) (1024,)\n" + ] + } + ], + "source": [ + "# Split the image into 1024 naflex patches of size 16.\n", + "pp_naflex = get_pp_naflex(1024, 16)\n", + "\n", + "# These are encoded as the actual patches, input mask, y and x indices.\n", + "patches, input_mask, yidx, xidx = map(np.array, pp_naflex(dict(image=np.array(img)))['image'])\n", + "print(patches.shape, input_mask.shape, yidx.shape, xidx.shape)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LJGkqWEexmaq", + "outputId": "c1a35f23-7ecc-46e2-9d20-d1c626969091" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "19" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "patches, yidx, xidx = map(lambda x: x[:input_mask.sum()], (patches, yidx, xidx)) # remove padding\n", + "cols = xidx.max() + 1\n", + "cols" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 411 + }, + "id": "OwKOYB2xyBdj", + "outputId": "252ca20c-e03a-45b6-a70d-9d8ea19904f5" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "Text(0.5, 1.0, 'yidx')" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAo0AAALzCAYAAAB0hT38AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjAsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvlHJYcgAAAAlwSFlzAAAewgAAHsIBbtB1PgAARy1JREFUeJzt3Xt4FPXd///X5rA5Q0RADEGChBCoXpZCEAQbqKefFAmBamtrBQGx1abS+sVzabXVW0AFm7tVkCBiW7VSb7gRaau9MSiKmBYvbQERBEsOKkHklONm5/dHypCYw2cn7DnPx3XtdU12PvPZz7Du29fOzM7HZVmWJQAAAKATMaEeAAAAAMIfoREAAABGhEYAAAAYERoBAABgRGgEAACAEaERAAAARoRGAAAAGBEaAQAAYERoBAAAgBGhEQAAAEaERgAAABgRGgEAAGBEaAQAAIARoREAAABGhEYAAAAYERoBAABgRGgEAACAEaERAAAARoRGAAAAGBEaEVH2798vl8sll8ulrKwsv/U7YcIEu9/XXnvNb/0CQDiilqIrCI0AAAAwIjQCAADAiNAIAAAAo7hQDwBwIisrS5ZlhXoYABDRqKXoCo40AgAAwIjQCAAAACNCIwLqo48+Uo8ePexbMCxcuNC4zY9+9CO7/YABA3T48GF7ndPbRHi9Xj399NO67LLL1K9fPyUmJiorK0sFBQVau3atT/vQ0NCgr33ta/brTpo0ybjNn/70J7t9fHy8tm7d6tNrAUB7LrjgArumPPvssz5vN2PGDHu7n/70p/bz1FJ0iQUE2OrVqy1JliQrPj7eKisr67DtSy+9ZLeNiYmxNm3a1Gr9vn377PUDBw7s9HWrqqqsCy+80G7f3qOwsNA6evSolZ+fbz/35de0LMvauXOnlZycbLf59a9/3eHrHjhwwDrjjDPstvfdd1+n4wQAk+LiYrumXHrppT5tc+TIkVZ161//+pe9jlqKriA0IiiuvfZa+4Ofk5NjHT9+vE2bqqoqq0+fPna7O++8s00bXwvd4cOHrWHDhrUqaoMGDbK++93vWjfccIM1evRo+/np06cbC51lWdayZcvsNomJidb777/fpk1TU5M1YcIEu924ceMsj8fj878TALTn8OHDVlJSkiXJcrlc1r59+4zbPPHEE3YtGjt2bKt11FJ0BaERQfHFF19YAwcOtAvAnDlzWq33er3WFVdcYa8fNWqU1dDQ0KYfXwvdrFmz7HZut9sqKSlp0+btt9+2x+R2u42FzrIsa+rUqXa7888/36qrq2u1/oEHHrDX9+zZ06fCDgC+mDFjhl1fFixYYGyfl5dnt/9yDaSWoisIjQia119/3YqNjbULwYsvvmive/TRR+3nU1JSrA8++KDdPnwpdB988IHlcrnsdqtWrepwTB988EGrUyWmQlddXW1lZGTYbX/84x/b695++20rLi7OXvf73/++838QAHBgy5Ytdn0555xzrKampg7bvvfee3bbtLS0Nmd3qKXoCkIjgmrBggV2IejVq5dVXl5uvfvuu1ZCQoL9/JNPPtnh9r4Uuttvv91uM3r0aOOY7r77bp8LnWVZ1quvvmoXUpfLZb388svWsWPHrOzsbLuP733ve8bXBQCnvvKVr9h15s9//nOH7W699Va73Y033thmPbUUXUFoRFB5PB7roosusgvCxIkTreHDh9t/T5s2rdPtfSl0LU/JFBcXG8f0wQcfOCp0ltW6mJ511lnWt771LfvvrKws68iRI8Y+AMCppUuX2rXm6quvbrdNfX29deaZZ9rt3n777TZtqKXoCkIjgu6jjz6yevTo0aq4SLL69+9vHTp0qNNtTYXO6/W2uqams19qt9SrVy9Hha6hocEaOXJkm32IjY21tmzZ4tNrAoBTn3/+uZWYmGhfP1hdXd2mzfPPP9/qesH2UEvRFdynEUE3aNAg/fa3v231nMvl0urVq9WrV6/T6vvIkSNqaGiw/z7nnHN82s7XdifFx8frD3/4g1JSUlo9f++99+qiiy5y1BcA+OqMM87Qt771LUnN9z185pln2rQpKSmxl+fMmdOl16GWoj2ERoRE3759W/199tln68ILLzztfo8fP97q7+TkZJ+2+3LB8kWPHj2UlJTU6rmpU6c67gcAnJg7d6693DIgStK///1vvfrqq5KkhIQEXXfddV16DWop2kNoRNBVV1drxowZrZ6rrKzUvHnzTrvv1NTUVn/X1NT4tN2JEyccvY5lWZo5c6aqq6tbPX/99dervr7eUV8A4MTFF1+s3NxcSdI///lPbdu2zV731FNPyev1SpKmTZvW5bM31FK0h9CIoJszZ46qqqokSdnZ2YqNjZUkrVixwufpqDrSs2dPud1u++9///vfPm134MABR6+zdOlS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\n", + "text/plain": [ + "\u003cFigure size 400x400 with 2 Axes\u003e" + ] + }, + "metadata": { + "image/png": { + "height": 377, + "width": 326 + } + }, + "output_type": "display_data" + } + ], + "source": [ + "# Visualizing the indices\n", + "_, axs = plt.subplots(1, 2, figsize=(4, 4))\n", + "axs[0].matshow(xidx.reshape([-1, cols]))\n", + "axs[0].set_title('xidx')\n", + "axs[1].matshow(yidx.reshape([-1, cols]))\n", + "axs[1].set_title('yidx')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 869 + }, + "id": "wPwegN9szoTR", + "outputId": "4524a662-d6e7-4555-d5e8-8814f2cbd698" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e" + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import einops\n", + "patches_rearranged = einops.rearrange(patches.reshape([-1, cols, PATCH_SIZE, PATCH_SIZE, 3]), 'r c y x z -\u003e (r y) (c x) z')\n", + "img_naflex = PIL.Image.fromarray((patches_rearranged * 127.5 + 127.5).astype('uint8'))\n", + "# (work around broken saved outputs)\n", + "HTML(img2html(img_naflex))\n", + "# Note how 1024 patches represent the text somewhat readable thanks to NaFlex..." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 533 + }, + "id": "qFxylZ_QTASu", + "outputId": "8ad5dea5-22a9-416d-e3ac-8d8a17922bb0" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\u003cimg src=\"data:image/jpeg;base64,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\"/\u003e" + ], + "text/plain": [ + "\u003cIPython.core.display.HTML object\u003e" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# ...compared to the resize to square with the same number of patches (and pixels).\n", + "img_resize_to_square = img.resize([32 * 16, 32 * 16])\n", + "HTML(img2html(img_resize_to_square))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "tcf7tnEAPLwo" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP_demo.ipynb b/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP_demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..6f0b15e2b6a16c988f3a38e9f914a7b33916243b --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/image_text/SigLIP_demo.ipynb @@ -0,0 +1,1023 @@ +{ + "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", + "# VARIANT, RES = 'So400m/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-i18n', 256): ('webli_i18n_so400m_16_256_78061115.npz', 'So400m', None, 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 # Seems like ideogram now forbits (403) direct downloads?\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/Tipsomaly/model/big_vision/configs/proj/image_text/common.py b/Tipsomaly/model/big_vision/configs/proj/image_text/common.py new file mode 100644 index 0000000000000000000000000000000000000000..96e5235c557a19aaf8500f03759c0655ea28956d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/image_text/lit.ipynb b/Tipsomaly/model/big_vision/configs/proj/image_text/lit.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..db3c9536c216102e8d0343d0283a79517eb0302c --- /dev/null +++ b/Tipsomaly/model/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)/AD4DTPduKxL1Gh5fV86+mhZuOX+aEs0iknAdkSillY3MUh9IRHzQ2hFh4746LRQhkuQCkdjZW9NMU3tE31aJBN9SoNQ5t9M51w48Aqzsds5K4AFv+/fA+ccagWNmY4Ac59yrzjkH/Br4UK9HDnRGovx8TSmnjMvhvJn5iXgKEZF32VnVRHskqhWxRMQXm8rraI9EVThZJMkFIrHzTtFAdWpEAm4csLfL/X3eviOe45zrBOqAkd6xyWa2zsyeN7Ozu5y/7zjXBMDMbjKzYjMrrqqq6nHwT2wMU3awmZvPU20dEekb6gOJiJ/WlsUKJxcpsSOS1AKR2NkSbiDFYGaBhiGLDGBhYIJzbiFwK/CQmfXok45z7m7nXJFzrigvL69HTx6JOn66ejszR2dz4ZzRPXqsiMiJCoXrGZSawpRRQ/0ORUQGoDd2VzNl1FAVbxdJcoFI7ITC9UzJy1LBLpHgKwfGd7lf6O074jlmlgYMAw4659qccwcBnHNvADuAGd75hce55kl7alOYHVVNfPH8aaSkaLSOiPSNLeF6po/OIi01EF02EelHolFH8e4aiiZptI5IsgtEL0GrQYj0G2uB6WY22cwGAVcCq7qdswq41tu+HFjtnHNmlucVX8bMphArkrzTORcG6s3sNK8WzzXAn3oz6GjU8bPVpUzNG8rFp4zpzUuLiBxTSUWD+kAi4oudBxqpbe6gaKIKJ4sku6RP7NS1dLCvpkUrYon0A17NnJuBp4EQ8JhzbrOZ3WFml3mn3QOMNLNSYlOuDi2Jfg6w0czWEyuq/DnnXLV37PPAr4BSYiN5nurNuJ8J7aekooGbl08jVaN1RKSPHGhso6qhTYkdEfHF4fo6GrEjkvTS/A7geEpUNFCkX3HOPQk82W3f7V22W4ErjvC4/wH+5yjXLAZO6d1ID1+bnzy7nYkjh/DB+WMT8RQiIkf0TuFkfbklIn2vuKyGkUMHMVk1vkSSXtKP2DnUqdEynyLihzVbK9n8dj1fOHeaalyIDEBmtsLMtppZqZnddoTjGWb2qHf8NTOb1FvPfTixU6A+kMhA5Gf7A7HCyYsnDtdKoCIBkPSfUkLhBkYMHUR+tiqxi0jfio3WKWVc7mA+vOiIK6iLSD/m1fW6E7gYmANcZWZzup12A1DjnJsG/BD4bm89f0m4gYKcTIYPHdRblxSRgPC7/alqaKPsYLOmYYkERPIndirqmT0mW5liEelzL5YeYP3eWj5/3lTSNVpHZCBaCpQ653Y659qBR4CV3c5ZCTzgbf8eON96qdOyJVyvaVgiA5ev7c8bu2NlDIsmqXCySBDE9UnFz2GA587I41LVtRARHwwbnM6l88dw+eLC458sIv3ROGBvl/v7vH1HPMcrEF8HjOx+ITO7ycyKzay4qqoqridfPiufi+dpJT6RAarX2h/oeRs0MiuDlQvGcsrYYScSu4j0seMWT+4yDPD9xBqUtWa2yjm3pctph4cBmtmVxIYBfrw3Arz1wpm9cRkRkR6bX5jLz65e5HcYItIPOOfuBu4GKCoqcvE85qsrZiU0JhEZOHraBi2ZNIIlGq0jEhjxjNjxdRigiIiIiE/KgfFd7hd6+454jpmlAcOAg30SnYj0Z2p/RCRu8SR2fB2GLCIiIuKTtcB0M5tsZoOAK4FV3c5ZBVzrbV8OrHbOxTUiR0TkGNT+iEjc+rQaqHPubudckXOuKC8vry+fWkRERKRHvC+rbgaeBkLAY865zWZ2h5ld5p12DzDSzEqBW4H31CIUEekptT8i0hPHrbFDz4YB7tMwQBEREekvnHNPAk9223d7l+1W4Iq+jktE+j+1PyISr3hG7GgYoIiIiIiIiIhIErJ48i9m9gHgR0AqcK9z7t/N7A6g2Dm3yswygQeBhUA1cKVzbudxrlkF7I4zzlHAgTjP9VuQYoVgxatYEyfeeCc65/rFPMoetEH99W+ZDBRr4gQp3p7E2i/aIPWBkkaQ4lWsiaM+0NH1179lMlCsiROkeHutDxRXYsdvZlbsnCvyO454BClWCFa8ijVxghZvXwra7yZI8SrWxAlSvEGK1Q9B+v0EKVYIVryKNXGCFm9fCtrvJkjxKtbECVK8vRlrnxZPFhERERERERGR3qPEjoiIiIiIiIhIQAUlsXO33wH0QJBihWDFq1gTJ2jx9qWg/W6CFK9iTZwgxRukWP0QpN9PkGKFYMWrWBMnaPH2paD9boIUr2JNnCDF22uxBqLGjoiIiIiIiIiIvFdQRuyIiIiIiIiIiEg3SuyIiIiIiIiIiARUUid2zGyFmW01s1Izu83HOO41s0oz29Rl3wgze8bMtns/h3v7zcx+4sW80cwWdXnMtd75283s2gTFOt7M1pjZFjPbbGa3JGu8ZpZpZq+b2QYv1n/z9k82s9e8mB41s0He/gzvfql3fFKXa33d27/VzC7q7Vi7PE+qma0zsycCEGuZmb1lZuvNrNjbl3Tvg2SWDG2Q2p+Exqs2KEGxqv05ecnQ/nhxqA1SH+jQ8wSi/fGeR23QSUqGNkjtj/pA3WIORBvkW/vjnEvKG5AK7ACmAIOADcAcn2I5B1gEbOqy73vAbd72bcB3ve0PAE8BBpwGvObtHwHs9H4O97aHJyDWMcAibzsb2AbMScZ4vefM8rbTgde8GB4DrvT2/zfwD97254H/9ravBB71tud4748MYLL3vklN0HvhVuAh4AnvfjLHWgaM6rYv6d4HyXpLljZI7U9C41UblKBY1f6c9O8vKdofLxa1QeoDHYo5EO2P91xqg07u95cUbZDaH/WBusUciDbIr/anT/9x9vAXcjrwdJf7Xwe+7mM8k7o1KluBMd72GGCrt30XcFX384CrgLu67H/XeQmM+0/A+5M9XmAI8CawDDgApHV/HwBPA6d722needb9vdH1vF6OsRB4FlgOPOE9d1LG6l37SI1KUr8PkumWTG2Q2p/Ex6s2qNdjVftzcr+/pGl/vOdXG6Q+UGDaH+/aaoNO7veXNG2Q2h/1gbzrBqYN8qv9SeapWOOAvV3u7/P2JYvRzrmwt10BjPa2jxZ3n78eb9jZQmIZ2KSM1xtStx6oBJ4hljmtdc51HuF5D8fkHa8DRvZVrMCPgK8CUe/+yCSOFcAB/2tmb5jZTd6+pHwfJKlkfu1J/3cMQvvjxak2KDGxqv05Ocn+2pP+bxmENkjtj/pASSyZX3vS/x2D0P54caoN6kd9oLSTjVrAOefMzPkdR1dmlgX8D/CPzrl6Mzt8LJnidc5FgAVmlgs8DszyN6IjM7NLgUrn3Btmdq7P4cTrLOdcuZnlA8+YWUnXg8n0PpATl4x/x6C0P6A2KIHU/gwQyfi3DEobpPYnodQGDQDJ+HcMSvsDaoMSyJf2J5lH7JQD47vcL/T2JYv9ZjYGwPtZ6e0/Wtx99nrMLJ1Yg/Jb59wfkj1eAOdcLbCG2DC6XDM7lHTs+ryHY/KODwMO9lGsZwKXmVkZ8AixYYA/TtJYAXDOlXs/K4k11ktJ8vdBkknm1560f8cgtj+gNqiXY1X7c/KS/bUn7d8yiG2Q2p/epzbopCXza0/av2MQ2x9QG9TLsfrX/vT2nLLeuhEbTbSTWGGjQ0W75voYzyTePb/z+7y7ANL3vO1LeHcBpNe9/SOAXcSKHw33tkckIE4Dfg38qNv+pIsXyANyve3BwAvApcDveHchrM9721/g3YWwHvO25/LuQlg7SVDRLu/5zuWdol1JGSswFMjusv0ysCIZ3wfJekumNkjtT8LiVRuUgFjV/vTK7zBp2h8vHrVB6gMdijup2x/vedQGnfzvMGnaILU/6gN1izup2yA/258+/8fZw1/MB4hVFN8BfMPHOB4GwkAHsfltNxCbp/cssB3426FftPdHudOL+S2gqMt1Pg2UerfrExTrWcTm9W0E1nu3DyRjvMB8YJ0X6ybgdm//FOB173l/B2R4+zO9+6Xe8SldrvUN7zVsBS5O8Puha4OSlLF6cW3wbpsP/ftJxvdBMt+SoQ1S+5PQeNUGJSBWtT+99nv0vf3x4lAbpD5Q17iTuv3pEpfaoJP/PfreBqn9UR/oCHEndRvkZ/tj3oNERERERERERCRgkrnGjoiIiIiIiIiIHIMSOyIiIiIiIiIiAaXEjoiIiIiIiIhIQCmxIyIiIiIiIiISUErsiIiIiIiIiIgElBI7clLM7B/NbIjfcYjIwKQ2SET8ovZHRPykNki60nLnclLMrAwocs4d8DsWERl41AaJiF/U/oiIn9QGSVcasSNxM7OhZvYXM9tgZpvM7F+AscAaM1vjnXOhmb1iZm+a2e/MLMvbX2Zm3zOzt8zsdTOb5udrEZHgURskIn5R+yMiflIbJMejxI70xArgbefcqc65U4AfAW8D5znnzjOzUcA3gQucc4uAYuDWLo+vc87NA37mPVZEpCfUBomIX9T+iIif1AbJMSmxIz3xFvB+M/uumZ3tnKvrdvw0YA7wkpmtB64FJnY5/nCXn6cnOlgR6XfUBomIX9T+iIif1AbJMaX5HYAEh3Num5ktAj4AfNvMnu12igHPOOeuOtoljrItInJcaoNExC9qf0TET2qD5Hg0YkfiZmZjgWbn3G+A7wOLgAYg2zvlVeDMQ/M2vbmgM7pc4uNdfr7SN1GLSH+hNkhE/KL2R0T8pDZIjkcjdqQn5gHfN7Mo0AH8A7GhfH81s7e9+Z3XAQ+bWYb3mG8C27zt4Wa2EWgDjpZNFhE5GrVBIuIXtT8i4ie1QXJMWu5c+oRpOT4R8ZHaIBHxi9ofEfGT2qCBQVOxREREREREREQCSiN2REREREREREQCSiN2REREREREREQCSokdEREREREREZGAUmJHRERERERERCSglNgREREREREREQkoJXZERERERERERALq/wN2Tyd6lh5ojQAAAABJRU5ErkJggg==\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/Tipsomaly/model/big_vision/configs/proj/image_text/siglip_lit_coco.py b/Tipsomaly/model/big_vision/configs/proj/image_text/siglip_lit_coco.py new file mode 100644 index 0000000000000000000000000000000000000000..f1dbd472a1d440351b3e176696873fc566797ffd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/jet/imagenet64.py b/Tipsomaly/model/big_vision/configs/proj/jet/imagenet64.py new file mode 100644 index 0000000000000000000000000000000000000000..62931936a66c8bc9f4e1977904798343086fbf92 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/jet/imagenet64.py @@ -0,0 +1,107 @@ +# 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=attribute-error,line-too-long +r"""Jet config for imagenet64. + +Expected values in imagenet64 (200 epochs): + - 32 couplings and block depth 2: 3.72 bpd + - 64 couplings and block depth 5: 3.66 bpd +""" + +import big_vision.configs.common as bvcc + + +def get_config(arg=None): + """Config for training a Flow model.""" + config = bvcc.parse_arg(arg, mode='') + + config.seed = 0 + config.total_epochs = 200 + + config.input = dict() + config.input.data = dict( + name='downsampled_imagenet/64x64', + split='train', + ) + config.input.batch_size = 1024 + config.input.shuffle_buffer_size = 250_000 + + config.input.pp = 'decode|resize(64)|value_range(-1, 1)|keep("image")' + pp_eval = 'decode|resize(64)|value_range(-1, 1)|keep("image")' + + config.log_training_steps = 50 + config.ckpt_steps = 5000 + + # Model section + config.model_name = 'proj.jet.jet' + config.model = dict( + depth=32, block_depth=2, emb_dim=512, num_heads=8, + kinds=('channels', 'channels', 'channels', 'channels', 'spatial'), + channels_coupling_projs=('random',), + spatial_coupling_projs=('checkerboard', 'checkerboard-inv', + 'vstripes', 'vstripes-inv', + 'hstripes', 'hstripes-inv')) + + # Optimizer section + config.optax_name = 'scale_by_adam' + config.optax = dict(mu_dtype='bfloat16', b2=0.95) + config.grad_clip_norm = 1.0 + + # rsqrt schedule. + config.lr = 3e-4 + config.wd = 1e-5 + config.wd_mults = ( + ('.*', 1.0), + ) + config.schedule = [ + ('.*FREEZE_ME.*', None), # Permutation matrices should be always frozen. + ('.*', dict(decay_type='cosine', warmup_percent=0.1)), + ] + + # config.mesh = [('replica', 16), ('fsdp', -1)] + # config.sharding_strategy = [('.*', 'fsdp(axis="fsdp")')] + # config.sharding_rules = [('act_batch', ('replica', 'fsdp'))] + + # Eval section + config.evals = {} + + config.evals.minitrain_bits = dict( + type='mean', + pred='loss', + data=dict(name=config.input.data.name, split='train[:4096]'), + pp_fn=pp_eval, + log_percent=0.05, + ) + + config.evals.val_bits = dict( + type='mean', + pred='loss', + data=dict(name=config.input.data.name, split='validation'), + pp_fn=pp_eval, + log_percent=0.05, + ) + + if config.mode == 'runlocal': + del config.total_epochs + config.total_steps = 200 + config.input.shuffle_buffer_size = 10 + config.input.batch_size = 32 + config.model.depth = 1 + config.model.block_depth = 1 + + config.evals.val_bits.data.split = 'validation[:16]' + config.evals.minitrain_bits.data.split = 'train[:16]' + + return config diff --git a/Tipsomaly/model/big_vision/configs/proj/jetformer/README.md b/Tipsomaly/model/big_vision/configs/proj/jetformer/README.md new file mode 100644 index 0000000000000000000000000000000000000000..050256d3808dea052e0d32fa5f0d5e68adedab87 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/jetformer/README.md @@ -0,0 +1,33 @@ +# JetFormer: An Autoregressive Generative Model of Raw Images and Text + +*by Michael Tschannen\*, Andrรฉ Susano Pinto\*, Alexander Kolesnikov\** [[arxiv]](https://arxiv.org/abs//2411.19722) + +![JetFormer overview](jetformer_overview.png) + +### Summary + +Removing modeling constraints and unifying architectures across domains has +been a key driver of the recent progress in training large multimodal models. +However, most of these models still rely on many separately trained components +such as modality-specific encoders and decoders. In this work, we further +streamline joint generative modeling of images and text. We propose an +autoregressive decoder-only transformer - JetFormer - which is trained to +directly maximize the likelihood of raw data, without relying on any separately +pretrained components, and can understand and generate both text and images. +Specifically, we leverage a normalizing flow model to obtain a soft-token image +representation that is jointly trained with an autoregressive multimodal +transformer. The normalizing flow model serves as both an image encoder for +perception tasks and an image decoder for image generation tasks during +inference. JetFormer achieves text-to-image generation quality competitive with +recent VQ-VAE- and VAE-based baselines. These baselines rely on pretrained +image autoencoders, which are trained with a complex mixture of losses, +including perceptual ones. At the same time, JetFormer demonstrates robust image +understanding capabilities. To the best of our knowledge, JetFormer is the +first model that is capable of generating high-fidelity images and producing +strong log-likelihood bounds. + +### Training models + +Please see the [main README](https://github.com/google-research/big_vision) for +how to set up the codebase and training data sets in your preferred environment. +Use the commands in the config headers to train models. \ No newline at end of file diff --git a/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_image_text.py b/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_image_text.py new file mode 100644 index 0000000000000000000000000000000000000000..44a75648ac8a977d3053b3437bbdd92b29c1294b --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_image_text.py @@ -0,0 +1,279 @@ +# 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 JetFormer (https://arxiv.org/abs//2411.19722) on image-text data. + +Example launch command (local; see main README for launching on TPU servers): + + python -m big_vision.trainers.proj.jetformer.train \ + --config big_vision/configs/proj/jetformer/jetformer_image_text.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. +""" + +import itertools + +import big_vision.configs.common as bvcc +import ml_collections + + +GIVT_MODELS = { + '350M': { + 'width': 1024, + 'depth': 24, + 'mlp_dim': 1024 * 4, + 'num_heads': 16, + 'num_kv_heads': 1, + 'head_dim': 64, + }, + '750M': { + 'width': 1536, + 'depth': 24, + 'mlp_dim': 1536 * 4, + 'num_heads': 16, + 'num_kv_heads': 1, + 'head_dim': 96, + }, + '1p3B': { + 'width': 1536, + 'depth': 48, + 'mlp_dim': 1536 * 4, + 'num_heads': 16, + 'num_kv_heads': 1, + 'head_dim': 96, + }, +} + +NVP_BLOCK_DEPTH = { + '350M': 4, + '1p3B': 6, +} + +SAMPLING_PARAMS = { + '350M': (9, 0.96), + '750M': (8, 0.94), + '1p3B': (6, 0.96), +} + +TOKENIZER = 'sp(\'c4_en\')' +EOS_ID = 1 + + +def get_config(arg=None): + """A config for training GIVT + NVP on WebLI.""" + config = bvcc.parse_arg( + arg, res=256, patch_size=16, model_size='350M', text_len=64, + use_adaptor=True, depth_to_seq=1, use_boi=True, + runlocal=False, num_replicas=1, num_slices=1, ignore_pad=False) + + config.pp_modules = ( + 'ops_general', 'ops_image', 'ops_text', 'proj.paligemma.ops') + + # Data section + pp_image = ( + f'decode' + f'|resize_small({config.res}, inkey="image", outkey="image",' + f'method="bicubic", antialias=True)' + f'|central_crop({config.res})' + f'|value_range(-1, 1, key="image")') + + def tokenize(inkey): + """Tokenizes inkey into "text" and "text_mask" and "text_loss".""" + # `text_mask` is 1 if tokens are allowed to attend to it. + # `text_loss` is 1 if predicting it is part of the loss. + # This has two modes: + # 1. `ignore_pad=False`: + # - Model learns [BOS, A, B, C, EOS, pad..., BOI?, img1, img2, ...] + # - `text_mask` and `text_loss` are 1 for all tokens. + # 2. `ignore_pad=True`: + # - Model learns [BOS, A, B, C, EOS, img1, img2, ...] + # - `text_mask` = 1 for text tokens, 0 for EOS and padding. + # - `text_loss` = 1 for text tokens and EOS and 0 for padding. + ip = 0 if config.ignore_pad else 1 + return '|'.join([ + f'copy(inkey="{inkey}", outkey="text")', + 'lower(key="text")', + f'tok(model="{TOKENIZER}", eos="no", key="text")', + f'setdefault("eos", [{EOS_ID}])', + f'masked_concat(["text", "eos"], outkey="text", text_loss=[1, 1], text_mask=[1, {ip}])', + f'tolen(length={config.text_len}, pad_value={ip}, key="text")', + f'tolen(length={config.text_len}, pad_value={ip}, key="text_mask")', + f'tolen(length={config.text_len}, pad_value={ip}, key="text_loss")', + ]) + vocab_size = 32_000 + + config.input = {} + # Using COCO captions as a dummy dataset since it is available on TFDS + # without manual download. This won't produce good results - please use + # your preferred large image-text data set. + config.input.data = dict(name='coco_captions', split='train') + pp_text = '|'.join([ + 'choice(inkey="captions/text", outkey="text")', + tokenize('text'), + ]) + + config.input.pp = f'{pp_image}|flatten|{pp_text}|keep("image", "text", "text_mask", "text_loss")' + + # Steps, batch size and friends. + config.total_steps = 500_000 + config.input.batch_size = 4096 + config.input.shuffle_buffer_size = 25_000 + + config.log_training_steps = 50 + config.ckpt_steps = 500 + config.keep_ckpt_percent = 0.0 + + # Sample config. + cfg_w, temp = SAMPLING_PARAMS[config.model_size] + config.sample_images = {} + config.sample_images.cfg_inference_weight = cfg_w + config.sample_images.temperature = temp + config.sample_images.temperature_probs = 1.0 + + # Patch PCA section. + # Does not apply PCA, but only splits image into patches and adds + # dequantization noise. + config.patch_pca = {} + config.patch_pca.model_name = 'proj.jetformer.patch_pca' + config.patch_pca.model = ml_collections.ConfigDict() + config.patch_pca.model.depth_to_seq = config.depth_to_seq + config.patch_pca.model.input_size = (config.res, config.res) + config.patch_pca.model.patch_size = (config.patch_size, config.patch_size) + config.patch_pca.model.code_len = ((config.res // config.patch_size) ** 2) * config.depth_to_seq + config.patch_pca.model.codeword_dim = 128 // config.depth_to_seq + config.patch_pca.model.noise_std = 0.0 + config.patch_pca.model.add_dequant_noise = True + config.patch_pca.model.skip_pca = True + + # Transformer section. + config.model_name = 'proj.jetformer.jetformer' + config.model_init = '' + assert config.model_size in GIVT_MODELS, f'Unknown model size: {config.model_size}' + config.model = ml_collections.ConfigDict(GIVT_MODELS[config.model_size]) + # Alloc special tokens + token_id = itertools.count(vocab_size) + config.model.bos_id = next(token_id) + if config.ignore_pad: + assert config.use_boi, 'Ignore padding uses EOS as BOI' + config.model.boi_id = 1 + else: + if config.use_boi: + config.model.boi_id = next(token_id) + config.model.nolabel_id = next(token_id) + config.model.vocab_size = next(token_id) + config.model.out_dim = config.patch_pca.model.codeword_dim + config.model.num_mixtures = 1024 + config.model.scale_tol = 1e-6 + config.model.drop_labels_probability = 0.1 + config.model.dropout = 0.1 + config.model.head_dtype = 'bfloat16' # Seems stable and save memory. + # Required for model sharding + config.model.scan = True + config.model.remat_policy = 'nothing_saveable' + config.model.untie_output_vocab = True + config.model.causal_mask_on_prefix = True + + config.input_noise_std = 0.3 + config.noise_scale = 64.0 + config.noise_min = 3.0 + config.rgb_noise_on_image_prefix = False + config.latent_noise_dim = (16 * 16 * 3) - config.patch_pca.model.codeword_dim + + config.text_prefix_prob = 0.5 + config.loss_on_prefix = False + config.text_loss_weight = 0.0025 + config.stop_grad_nvp_prefix = True + + # NVP section. + config.adaptor_name = 'proj.jet.jet' if config.use_adaptor else '' + config.adaptor = {} + config.adaptor.model = {} + config.adaptor.model.depth = 32 + config.adaptor.model.block_depth = NVP_BLOCK_DEPTH[config.model_size] + config.adaptor.model.emb_dim = 512 + config.adaptor.model.num_heads = 8 + config.adaptor.model.ps = 1 + config.adaptor.model.kinds = ('channels',) + config.adaptor.model.channels_coupling_projs = ('random',) + config.adaptor.model.spatial_coupling_projs = ('checkerboard', 'checkerboard-inv',) + + 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 = [ + ('.*FREEZE_ME.*', 'replicate'), + ('.*', 'fsdp(axis="fsdp")'), + ] + config.mesh = [ + ('slice', config.num_slices), + ('replica', config.num_replicas), + ('fsdp', -1), + ] + config.sharding_rules = [('act_batch', ('slice', 'replica', 'fsdp',))] + + # Standard schedule. + config.lr = 0.001 + config.wd = 0.0001 + config.wd_mults = [ + # Explicitly handling the Gemma kernels, which have different names. + ('^decoder/layers/attn/.*', 1.0), + ('^decoder/layers/mlp/.*', 1.0), + ('.*/kernel$', 1.0), + ] + config.schedule = [ + ('.*FREEZE_ME.*', None), + ('.*', dict(decay_type='cosine', warmup_percent=0.1)), + ] + + ### Evaluation section. + config.evals = {} + config.evals.val = dict( + type='mean', + pred='validation', + data={**config.input.data, 'split': 'val[:4096]'}, + pp_fn=config.input.pp, + log_steps=5_000, + ) + + # config.evals.sample_images = dict( + # type='proj.givt.save_predictions', + # pp_fn=config.input.pp + '|keep("text","text_mask")', + # log_percent=0.1, + # pred='sample_images', + # data={**config.input.data, 'split': 'train[:128]'}, + # outfile='inference_sampled_images.npz', + # skip_first=True, + # ) + + config.seed = 0 + config.ckpt_timeout = 30 + + if config.runlocal: + config.input.batch_size = 8 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.depth = 2 + if config.use_adaptor: + config.adaptor.model.depth = 1 + for k in config.evals.keys(): + if not hasattr(config.evals[k], 'data'): + continue + config.evals[k].data.split = config.evals[k].data.split.split('[')[0] + '[:16]' + + return config diff --git a/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_imagenet2012.py b/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_imagenet2012.py new file mode 100644 index 0000000000000000000000000000000000000000..57792a315a261a8cac7180d0f32fe84aea3381cb --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/jetformer/jetformer_imagenet2012.py @@ -0,0 +1,229 @@ +# 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 JetFormer (https://arxiv.org/abs//2411.19722) on ImageNet256. + +Example launch command (local; see main README for launching on TPU servers): + + python -m big_vision.trainers.proj.jetformer.train \ + --config big_vision/configs/proj/jetformer/jetformer_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 .../jetformer_imagenet2012.py:model_size=1p3B,total_epochs=500` +""" + +import big_vision.configs.common as bvcc +import ml_collections + +GIVT_MODELS = { + '350M': { + 'width': 1024, + 'depth': 24, + 'mlp_dim': 1024 * 4, + 'num_heads': 16, + 'num_kv_heads': 1, + 'head_dim': 64, + }, + '1p3B': { + 'width': 1536, + 'depth': 48, + 'mlp_dim': 1536 * 4, + 'num_heads': 16, + 'num_kv_heads': 1, + 'head_dim': 96, + }, +} + +NVP_BLOCK_DEPTH = { + '350M': 4, + '1p3B': 6, +} + +SAMPLING_PARAMS = { + '350M': (3, 0.94), # Tested with 100 and 500 epoch training. + '1p3B': (2, 0.93), # Tested with 500 epoch training. +} + + +def get_config(arg=None): + """Train JetFormer on ImageNet.""" + config = bvcc.parse_arg( + arg, res=256, patch_size=16, model_size='350M', + total_epochs=100, use_adaptor=True, depth_to_seq=1, + runlocal=False, num_replicas=1, num_slices=1) + + config.input = {} + # Consider using Imagenette in case you don't want to download ImageNet. + # (This won't produce good results, but will enable running the config) + # config.input.data = dict(name='imagenette', split='train') + config.input.data = dict(name='imagenet2012', split='train[4096:]') + + config.input.batch_size = 2048 + config.input.shuffle_buffer_size = 25_000 + + config.input.pp = ( + f'decode' + f'|resize_small({config.res}, inkey="image", outkey="image",' + f'method="bicubic", antialias=True)' + f'|central_crop({config.res})' + f'|flip_lr' + f'|value_range(-1, 1, key="image")' + f'|reshape((1,), inkey="label", outkey="text")' + f'|setdefault("text_loss", [1])' + f'|copy("text_loss", "text_mask")' + f'|keep("image", "text", "text_mask", "text_loss")') + + pp_eval = config.input.pp.replace('|flip_lr', '') + + config.log_training_steps = 50 + config.ckpt_steps = 1000 + config.keep_ckpt_steps = None + + # Sample config + cfg_w, temp = SAMPLING_PARAMS[config.model_size] + config.sample_images = {} + config.sample_images.cfg_inference_weight = cfg_w + config.sample_images.temperature = temp + config.sample_images.temperature_probs = 1.0 + + sequence_length = ((config.res // config.patch_size) ** 2) * config.depth_to_seq + + # Patch PCA section. + # Does not apply PCA, but only splits image into patches and adds + # dequantization noise. + config.patch_pca = {} + config.patch_pca.model_name = 'proj.jetformer.patch_pca' + config.patch_pca.model = ml_collections.ConfigDict() + config.patch_pca.model.depth_to_seq = config.depth_to_seq + config.patch_pca.model.input_size = (config.res, config.res) + config.patch_pca.model.patch_size = (config.patch_size, config.patch_size) + config.patch_pca.model.code_len = sequence_length + config.patch_pca.model.codeword_dim = 128 // config.depth_to_seq + config.patch_pca.model.noise_std = 0.0 + config.patch_pca.model.add_dequant_noise = True + config.patch_pca.model.skip_pca = True + + # Transformer section. + config.model_name = 'proj.jetformer.jetformer' + config.model_init = '' + config.model = ml_collections.ConfigDict(GIVT_MODELS[config.model_size]) + num_labels = 1000 + config.model.bos_id = num_labels + config.model.boi_id = num_labels + 1 + config.model.nolabel_id = num_labels + 2 + config.model.vocab_size = num_labels + 3 + config.model.out_dim = config.patch_pca.model.codeword_dim + config.model.num_mixtures = 1024 + config.model.scale_tol = 1e-6 + config.model.dropout = 0.1 + config.model.drop_labels_probability = 0.1 + config.model_init = '' + config.model.head_dtype = 'bfloat16' + # Required for model sharding + config.model.scan = True + config.model.remat_policy = 'nothing_saveable' + config.model.num_vocab_repeats = 16 + + # Noise on the latent representation after NVP. + config.input_noise_std = 0.3 + # Noise curriculum. + config.noise_scale = 64.0 + # Dimensionality of factored out features. + config.latent_noise_dim = (16 * 16 * 3) - config.patch_pca.model.codeword_dim + + config.text_prefix_prob = 1.0 + config.loss_on_prefix = False + # NVP section. + config.adaptor_name = 'proj.jet.jet' if config.use_adaptor else '' + config.adaptor = {} + config.adaptor.model = dict( + depth=32, block_depth=NVP_BLOCK_DEPTH[config.model_size], + emb_dim=512, num_heads=8, ps=1, + kinds=('channels',), + channels_coupling_projs=('random',), + spatial_coupling_projs=('checkerboard', 'checkerboard-inv'),) + config.adaptor.class_conditional = False + + config.optax_name = 'scale_by_adam' + config.optax = dict(b2=0.95) + config.grad_clip_norm = 1.0 + config.ema_decay = 0.0 + + # FSDP training by default + config.sharding_strategy = [ + ('.*FREEZE_ME.*', 'replicate'), + ('.*', 'fsdp(axis="fsdp")'), + ] + config.mesh = [ + ('slice', config.num_slices), + ('replica', config.num_replicas), + ('fsdp', -1), + ] + config.sharding_rules = [('act_batch', ('slice', 'replica', 'fsdp',))] + + # Standard schedule + config.lr = 0.001 + config.wd = 0.0001 + config.wd_mults = [ + # Explicitly handling the Gemma kernels, which have different names. + ('^decoder/layers/attn/.*', 1.0), + ('^decoder/layers/mlp/.*', 1.0), + ('.*/kernel$', 1.0), + ] + config.schedule = [ + ('.*FREEZE_ME.*', None), + ('.*', dict(decay_type='cosine', warmup_percent=0.1)), + ] + + ### Evaluation section + config.evals = {} + config.evals.val = dict( + type='mean', + pred='validation', + data={**config.input.data, 'split': 'train[:4096]'}, + pp_fn=pp_eval, + log_steps=1_000, + ) + + # config.evals.save_pred_sampling = dict( + # type='proj.givt.save_predictions', + # pp_fn=pp_eval, + # log_percent=0.1, + # pred='sample_images', + # batch_size=512, + # data=dict(name=config.input.data.name, split='validation[:512]'), + # outfile='inference_sampled.npz', + # pred_kw={'decode_len': sequence_length}, + # ) + + config.seed = 0 + + config.ckpt_timeout = 30 + + if config.runlocal: + config.input.batch_size = 8 + config.input.shuffle_buffer_size = 10 + config.log_training_steps = 5 + config.model.depth = 2 + config.adaptor.model.depth = 1 + del config.evals.fid + for k in config.evals.keys(): + if not hasattr(config.evals[k], 'data'): + continue + config.evals[k].data.split = config.evals[k].data.split.split('[')[0] + '[:16]' + + return config diff --git a/Tipsomaly/model/big_vision/configs/proj/paligemma/README.md b/Tipsomaly/model/big_vision/configs/proj/paligemma/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b7f3b3717d0a48e6c610251f72aa351c570f2cbc --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/paligemma/README.md @@ -0,0 +1,282 @@ +# 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 checkpoints (PaliGemma and PaliGemma 2) we also provide transfer +checkpoints at multiple resolutions and a checkpoint transferred to a mixture of +tasks that can be used for off-the-shelf exploration (PaliGemma only). + +## Quick Reference + +This is the reference repository of the model, you may also want to check out the resources on + + - Technical reports on ArXiv: [PaliGemma](https://arxiv.org/abs/2407.07726), + [PaliGemma 2](https://arxiv.org/abs/2412.03555) + - Pre-trained / mix checkpoints and model card on Kaggle: + [PaliGemma](https://www.kaggle.com/models/google/paligemma), + [PaliGemma transfers](https://www.kaggle.com/models/google/paligemma-ft), + [PaliGemma 2](https://www.kaggle.com/models/google/paligemma-2) + - Google Cloud VertexAI Model Garden: + [PaliGemma](https://console.cloud.google.com/vertex-ai/publishers/google/model-garden/363) + - PyTorch and JAX models on Hugging Face: + [PaliGemma](https://huggingface.co/collections/google/paligemma-release-6643a9ffbf57de2ae0448dda), + [PaliGemma 2](https://huggingface.co/collections/google/paligemma-2-release-67500e1e1dbfdd4dee27ba48) + - Light fine-tuning using `big_vision` on a single (free) T4 GPU: + [Colab](https://colab.research.google.com/github/google-research/big_vision/blob/main/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb) + - Demo: [HuggingFace PaliGemma space](https://hf.co/spaces/google/paligemma) + +### 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} +} +@article{steiner2024paligemma2, + title={{PaliGemma 2: A Family of Versatile VLMs for Transfer}}, + author={Andreas Steiner and Andrรฉ Susano Pinto and Michael Tschannen and Daniel Keysers and Xiao Wang and Yonatan Bitton and Alexey Gritsenko and Matthias Minderer and Anthony Sherbondy and Shangbang Long and Siyang Qin and Reeve Ingle and Emanuele Bugliarello and Sahar Kazemzadeh and Thomas Mesnard and Ibrahim Alabdulmohsin and Lucas Beyer and Xiaohua Zhai}, + year={2024}, + journal={arXiv preprint arXiv:2412.03555} +} +``` + +## Model description + +### Overview + +PaliGemma 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 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](paligemma2.png) + +Note that PaliGemma uses Gemma 2B model, PaliGemma 2 uses Gemma 2 {2B,9B,27B} +models. + +### 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. See +above [Quick Reference](#quick-reference) for download links. + +### 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 + +(Currently only available for PaliGemma) + +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 + +(DOCCI only available for PaliGemma 2, others only available for PaliGemma) + +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-2 for a full list of models. +export MODEL_NAME=paligemma2-3b-pt-224 + +mkdir ckpts/ +cd ckpts/ + +# Store as a "vanity name" from models/proj/paligemma/paligemma.py +curl -L -u $KAGGLE_USERNAME:$KAGGLE_KEY\ + -o pt_3b_224.bf16.npz \ + https://www.kaggle.com/api/v1/models/google/paligemma-2/jax/$MODEL_NAME/1/download/$MODEL_NAME.b16.npz +``` + +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 Appendices of technical reports: +[PaliGemma](https://arxiv.org/abs/2407.07726), +[PaliGemma 2](https://arxiv.org/abs/2412.03555). diff --git a/Tipsomaly/model/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb b/Tipsomaly/model/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..519ff5508569e2ab8611ca6245d0bb21899064d0 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/paligemma/finetune_paligemma.ipynb @@ -0,0 +1,1157 @@ +{ + "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 2 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" + }, + "outputs": [], + "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')\n", + "\n", + "# The T4 runtime is tight on memory to finetune this model. Preallocate\n", + "# all memory ahead of time to avoid OOM'ing due to fragmentation.\n", + "os.environ[\"XLA_PYTHON_CLIENT_MEM_FRACTION\"] = \"1.0\"" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gQNOTfF24AV4", + "outputId": "5241dd5b-d5c2-473c-a5e0-0ad72db288d8" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Downloading the checkpoint from Kaggle, this could take a few minutes....\n", + "Model path: /root/.cache/kagglehub/models/google/paligemma-2/jax/paligemma2-3b-pt-224/1/./paligemma2-3b-pt-224.b16.npz\n" + ] + } + ], + "source": [ + "# @title Download checkpoint, tokenizer and dataset to local filesystem.\n", + "#\n", + "import os\n", + "import kagglehub\n", + "\n", + "# Use these for PaliGemma-2 3B 224pxยฒ\n", + "LLM_VARIANT = \"gemma2_2b\"\n", + "MODEL_PATH = \"./paligemma2-3b-pt-224.b16.npz\"\n", + "KAGGLE_HANDLE = \"google/paligemma-2/jax/paligemma2-3b-pt-224\" # Path to fetch from Kaggle.\n", + "\n", + "# Use these for PaliGemma 1:\n", + "# LLM_VARIANT = \"gemma_2b\"\n", + "# MODEL_PATH = \"./paligemma-3b-pt-224.f16.npz\"\n", + "# KAGGLE_HANDLE = \"google/paligemma/jax/paligemma-3b-pt-224\"\n", + "\n", + "if not os.path.exists(MODEL_PATH):\n", + " print(\"Downloading the checkpoint from Kaggle, this could take a few minutes....\")\n", + " MODEL_PATH = kagglehub.model_download(KAGGLE_HANDLE, 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": "51956b7f-8b7d-4565-cb11-1287595b054a" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "JAX version: 0.4.33\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.extend.backend.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", + "# IMPORTANT: Gemma-2 has a \"final_logits_softcap\" property, we set it to 0.0\n", + "# for better transfer results.\n", + "model_config = ml_collections.FrozenConfigDict({\n", + " \"llm\": {\"vocab_size\": 257_152, \"variant\": LLM_VARIANT, \"final_logits_softcap\": 0.0},\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": { + "id": "RWOdf_fw2SAO" + }, + "outputs": [], + "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", + " # Cast others to float16, since some GPUs don't support bf16.\n", + " return jax.tree.map(lambda p, m: p.astype(jnp.float32)\n", + " if m else p.astype(jnp.float16),\n", + " params, trainable)" + ] + }, + { + "cell_type": "code", + "source": [ + "# 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)" + ], + "metadata": { + "id": "ipJehqguO3T9", + "outputId": "bbb5c58b-243d-4172-fb35-df9ff25c159b", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "execution_count": 7, + "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 (2304,) float16\n", + "img/head/kernel (1152, 2304) float16\n", + "img/pos_embedding (1, 256, 1152) float16\n", + "llm/embedder/input_embedding (257152, 2304) float16\n", + "llm/final_norm/scale (2304,) float16\n", + "llm/layers/attn/attn_vec_einsum/w (26, 8, 256, 2304) float32\n", + "llm/layers/attn/kv_einsum/w (26, 2, 4, 2304, 256) float32\n", + "llm/layers/attn/q_einsum/w (26, 8, 2304, 256) float32\n", + "llm/layers/mlp/gating_einsum (26, 2, 2304, 9216) float16\n", + "llm/layers/mlp/linear (26, 9216, 2304) float16\n", + "llm/layers/post_attention_norm/scale (26, 2304) float16\n", + "llm/layers/post_ffw_norm/scale (26, 2304) float16\n", + "llm/layers/pre_attention_norm/scale (26, 2304) float16\n", + "llm/layers/pre_ffw_norm/scale (26, 2304) float16\n" + ] + } + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "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": 9, + "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": 10, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 397 + }, + "id": "BzJfb5t0nsLq", + "outputId": "8920b068-c56a-4f99-848d-810fd0cd0068" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Training examples\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a person stands on a tennis court, holding a racquet and a bunch of pink balls. the court is surrounded by a white wall with a brown circle on it. the person is wearing pink socks, pink shoes, and a pink skirt. the balls are on the ground. the lights are on.

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

a large city with a towering clock tower and numerous buildings. the sky is cloudy, and the sun shines through the clouds. the clock tower is tall and imposing, and the steeple on top of the building is a prominent feature. the buildings are clustered together, and the trees are tall and green. the overall atmosphere is serene and peaceful.

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

a lily pad and a lily pad flower float effortlessly on the ripples, while a brown leaf and a brown lily pad provide a contrast to the white flower. the lily pad flower's yellow center and orange center add a splash of color to the water's surface. the water reflects the flower's beauty, creating a serene and tranquil atmosphere.

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

a glass jar filled with a variety of colored pencils. the jar is clear and the pencils are arranged in a haphazard fashion. there are many different colored pencils in the jar, including a blue pencil, a green pencil, a red pencil, a pink pencil, and a purple pencil. the pencils are all different, with different colored tips and erasers. the jar is made of glass and has a clear rim. the background is gray.

\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", + "

two puffins stand on a grassy hill, their beaks open. the puffin on the right has its mouth open, revealing its orange beak and black eye. the other puffin on the left has its mouth closed, showcasing its white chest and black and white plumage. the hill is covered in green grass, and the flowers bloom in a variety of colors, including pink, purple, and yellow. the puffins' feet are orange, and their wings are black. the background is blue, and the overall scene is serene and peaceful.

\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 brown and white cat with a red collar looks to the left, its eyes shining yellow. the cat's fur is long and silky, and its whiskers are long and prominent. the cat's nose is pink, and its ears are pointy. the cat's eyes are yellow, and its fur is brown and white. the cat is standing in the dark, and its head is turned to the side.

\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": 11, + "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": 12, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "067wj_6bZAG3", + "outputId": "c3393bab-9c89-410c-9cf6-9e477e194a03" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 1/64 lr: 0.00500 loss: 3.2343\n", + "Model predictions at step 1\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman's hand on a white wall.

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

a woman in a white dress is standing by the water.

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

a black belt bag with the words " loading " on the front and the word " loading " on the back. the bag is black and the word " loading " is on the front. the word " loading " is on the back. the bag is black and the word " loading " is on the front. the word " loading " is on the back. the bag is black and the word " loading " is on the front. the word " loading " is on the back. the bag is black and the word " loading " is on the front. the word " loading " is on the back. the bag is black and

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

a woman in a pink shirt and a pink bag is walking on the street.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 2/64 lr: 0.01000 loss: 1.8795\n", + "step: 3/64 lr: 0.01500 loss: 1.6227\n", + "step: 4/64 lr: 0.02000 loss: 1.7171\n", + "step: 5/64 lr: 0.02500 loss: 1.8039\n", + "step: 6/64 lr: 0.03000 loss: 1.8505\n", + "step: 7/64 lr: 0.02998 loss: 2.4128\n", + "step: 8/64 lr: 0.02992 loss: 1.7908\n", + "step: 9/64 lr: 0.02981 loss: 1.6596\n", + "step: 10/64 lr: 0.02966 loss: 1.4779\n", + "step: 11/64 lr: 0.02947 loss: 1.5320\n", + "step: 12/64 lr: 0.02924 loss: 1.1854\n", + "step: 13/64 lr: 0.02897 loss: 1.1401\n", + "step: 14/64 lr: 0.02866 loss: 1.0224\n", + "step: 15/64 lr: 0.02831 loss: 1.1550\n", + "step: 16/64 lr: 0.02792 loss: 1.1351\n", + "Model predictions at step 16\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman's hand on a white wall, with a pink dress and white wall. the hand is white, with a pink nail. the dress is pink, with a white collar and white cuffs. the wall is white, with a white wall. the dress is long, with a white collar and white cuffs. the nail is white, with a pink nail. the hand is white, with a pink nail. the dress is pink, with a white collar and white cuffs. the wall is white, with a white wall. the dress is long, with a white collar and white cuffs. the nail is white, with a pink

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

a woman in a white dress with red flowers on it is standing on a white wall, holding a white woven bag. the dress is white and has a red flower on it. the bag is white and has a white handle. the woman is wearing a white shirt with a red flower on it, and the dress is white with red flowers on it. the dress is long and has a slit in the middle. the bag is white and has a white handle. the woman is wearing a white shirt with a red flower on it, and the dress is white with red flowers on it. the dress is white and has a slit in the

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

a person wearing a red blazer and black pants, with a black belt and a black bag on their waist. the bag has a white label and a silver zipper. the person is wearing a white shirt and has a white nail on their finger. the bag is black and has a white label. the person is wearing a red blazer and has a black belt on their waist. the bag is black and has a white label. the person is wearing a white shirt and has a white nail on their finger. the bag is black and has a white label. the person is wearing a red blazer and has a black belt on their waist. the

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

a woman in a pink shirt and blue jeans stands on a stone staircase, her hand on the bag. the jeans are blue, and the shirt is pink. the bag is pink, and the strap is white. the woman is wearing a silver bracelet and a silver bracelet on her hand. the bag is small and pink, and the strap is white. the shirt is pink, and the buttons are silver. the jeans are blue, and the belt is white. the woman is wearing a silver bracelet and a silver bracelet on her hand. the bag is pink, and the strap is white. the shirt is pink, and the buttons are

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 17/64 lr: 0.02750 loss: 1.3412\n", + "step: 18/64 lr: 0.02704 loss: 1.1616\n", + "step: 19/64 lr: 0.02655 loss: 0.9532\n", + "step: 20/64 lr: 0.02602 loss: 1.1109\n", + "step: 21/64 lr: 0.02546 loss: 1.0477\n", + "step: 22/64 lr: 0.02488 loss: 1.0161\n", + "step: 23/64 lr: 0.02426 loss: 0.7687\n", + "step: 24/64 lr: 0.02362 loss: 0.6345\n", + "step: 25/64 lr: 0.02296 loss: 0.6716\n", + "step: 26/64 lr: 0.02227 loss: 0.6888\n", + "step: 27/64 lr: 0.02156 loss: 0.6420\n", + "step: 28/64 lr: 0.02083 loss: 0.7701\n", + "step: 29/64 lr: 0.02009 loss: 0.6571\n", + "step: 30/64 lr: 0.01933 loss: 0.7011\n", + "step: 31/64 lr: 0.01856 loss: 0.6353\n", + "step: 32/64 lr: 0.01778 loss: 0.6679\n", + "Model predictions at step 32\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman's hand rests on a white wall, her fingers curled around the edge. the wall is made of concrete, and the shadow of her hand is cast on the wall. the woman's arm is extended out, and her hand is curled. the dress is pink, and the shirt is white.

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

a woman in a long white dress with a floral pattern stands on a stone wall overlooking the ocean. the dress is flowing in the wind, and the sky is clear. the woman is holding a white woven bag and a white hat. the water is calm and blue.

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

a person wears a red blazer with a black belt and a black skirt. the blazer has a button on the front and a pocket on the side. the person's hand is on the jacket's sleeve. the jacket is open. the belt is black and has a silver buckle. the bag is a fanny pack and has a zipper. the pants are black and have a white stripe on the back. the grass is green.

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

a woman stands on a stone staircase, her hand on the railing. her jeans are blue, and her shirt is pink. the bag is pink. the woman is wearing a white cardigan and a silver bracelet. the stairs are made of stone.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 33/64 lr: 0.01699 loss: 0.8104\n", + "step: 34/64 lr: 0.01620 loss: 0.6509\n", + "step: 35/64 lr: 0.01540 loss: 0.2895\n", + "step: 36/64 lr: 0.01460 loss: 0.3786\n", + "step: 37/64 lr: 0.01380 loss: 0.4475\n", + "step: 38/64 lr: 0.01301 loss: 0.4427\n", + "step: 39/64 lr: 0.01222 loss: 0.3183\n", + "step: 40/64 lr: 0.01144 loss: 0.3254\n", + "step: 41/64 lr: 0.01067 loss: 0.3482\n", + "step: 42/64 lr: 0.00991 loss: 0.2905\n", + "step: 43/64 lr: 0.00917 loss: 0.4441\n", + "step: 44/64 lr: 0.00844 loss: 0.3120\n", + "step: 45/64 lr: 0.00773 loss: 0.4281\n", + "step: 46/64 lr: 0.00704 loss: 0.1778\n", + "step: 47/64 lr: 0.00638 loss: 0.1648\n", + "step: 48/64 lr: 0.00574 loss: 0.1446\n", + "Model predictions at step 48\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman in a pink dress leans against a white wall, her hand delicately resting on the ledge. the dress is flowing and sheer, with long sleeves that drape gracefully. the woman's hand is visible, and her fingers are curled. the wall is white and the shadow on the wall is long and dark.

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

a woman in a long white floral dress stands on a pier overlooking the ocean. the dress is flowing gracefully, showcasing the woman's curves and the intricate design of the dress. the sky is clear and blue, with fluffy white clouds drifting above. the water is calm and blue, reflecting the sky. the woman's hand is on her dress, and her other hand is on the bag. the dress is long and flowing, and the flowers are red and white.

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

a person wears a red blazer with a black belt and a black skirt. the blazer has a single button and a single vent. the belt is black and has a silver zipper. the person's hand is on the jacket's sleeve. the jacket has a single vent and a single button. the skirt has a single vent and a single button. the bag is a fanny pack and has a silver zipper.

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

a woman stands on a stone staircase, her hand on her bag. the jeans are blue, and the shirt is pink. the bag is pink, and the belt is white. the woman is wearing a white cardigan and a silver bracelet. the stairs are made of stone, and the wall is made of concrete.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "step: 49/64 lr: 0.00512 loss: 0.1304\n", + "step: 50/64 lr: 0.00454 loss: 0.1808\n", + "step: 51/64 lr: 0.00398 loss: 0.1156\n", + "step: 52/64 lr: 0.00345 loss: 0.2323\n", + "step: 53/64 lr: 0.00296 loss: 0.1531\n", + "step: 54/64 lr: 0.00250 loss: 0.1296\n", + "step: 55/64 lr: 0.00208 loss: 0.2341\n", + "step: 56/64 lr: 0.00169 loss: 0.1462\n", + "step: 57/64 lr: 0.00134 loss: 0.1332\n", + "step: 58/64 lr: 0.00103 loss: 0.0951\n", + "step: 59/64 lr: 0.00076 loss: 0.1676\n", + "step: 60/64 lr: 0.00053 loss: 0.0734\n", + "step: 61/64 lr: 0.00034 loss: 0.0867\n", + "step: 62/64 lr: 0.00019 loss: 0.0694\n", + "step: 63/64 lr: 0.00008 loss: 0.1235\n", + "step: 64/64 lr: 0.00002 loss: 0.0907\n", + "Model predictions at step 64\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman in a pink dress leans against a white wall, her hand delicately resting on the ledge. the dress drapes gracefully, showcasing the contours of her body. the wall is tall and imposing, with a crack running along its length. the woman's hand is poised on the ledge, offering a warm greeting. the dress is long and flowing, draping gracefully.

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

a woman in a long white floral dress stands on a stone wall overlooking the ocean. the dress is flowing gracefully, showcasing the woman's curves and the intricate design of the fabric. her hand is on the side of the dress, offering a gentle caress. the sky is clear and blue, with fluffy white clouds drifting above. the water is calm and blue, reflecting the sky. the woman's legs are long and slender, showcasing her slender physique.

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

a person wears a red blazer with a black belt and a black skirt. the blazer has a single button and a single vent. the person wears a white tank top and a black belt. the bag is a fanny pack and has a zipper. the jacket is loose and the blazer has a belt loop. the person is standing next to a green plant and is wearing a black pants. the bag is a fanny pack and has a zipper.

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

a woman stands on a stone staircase, her hand on her bag. the stairs are made of stone, and the wall is made of concrete. the woman is wearing blue jeans, a pink shirt, and a white cardigan. the bag is pink, and the strap is pink. the sky is visible through the leaves on the tree.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 9min 6s, sys: 2min 43s, total: 11min 50s\n", + "Wall time: 17min 51s\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": 13, + "metadata": { + "id": "hgUhEKjzPdMQ", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 447 + }, + "outputId": "e0f9bfaf-7688-42a9-b1d8-22f7ad5743e6" + }, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Model predictions\n" + ] + }, + { + "output_type": "display_data", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + "
\n", + " \n", + "

a woman in a pink dress leans against a white wall, her hand delicately resting on the ledge. the dress drapes gracefully, showcasing the contours of her body. the wall is tall and imposing, with a crack running along its length. the woman's hand is poised on the ledge, offering a warm greeting. the dress is long and flowing, draping gracefully.

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

a woman in a long white floral dress stands on a stone wall overlooking the ocean. the dress is flowing gracefully, showcasing the woman's curves and the intricate design of the fabric. her hand is on the side of the dress, offering a gentle caress. the sky is clear and blue, with fluffy white clouds drifting above. the water is calm and blue, reflecting the sky. the woman's legs are long and slender, showcasing her slender physique.

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

a person wears a red blazer with a black belt and a black skirt. the blazer has a single button and a single vent. the person wears a white tank top and a black belt. the bag is a fanny pack and has a zipper. the jacket is loose and the blazer has a belt loop. the person is standing next to a green plant and is wearing a black pants. the bag is a fanny pack and has a zipper.

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

a woman stands on a stone staircase, her hand on her bag. the stairs are made of stone, and the wall is made of concrete. the woman is wearing blue jeans, a pink shirt, and a white cardigan. the bag is pink, and the strap is pink. the sky is visible through the leaves on the tree.

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

a person is laying in bed, and their hand is on the pillow. the bed is white, and the pillow is gray. the jeans are blue, and the shoes are white. the sweater is pink, and the writing on the sweater is red. the person is wearing a sweater and jeans.

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

a man stands with his hand in his hair, his eyes closed. he wears a black sweater and a checked shirt. the sweater is long-sleeved and has a collar. the man's hair is long and blonde. the background is pink.

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

a row of white hangers on a white clothes rack, with a white wall behind them. the hangers are made of wood and have silver hooks. the rack is tall and has a white pole running through it. the wall is white and has a gray shadow on it.

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

a white hoodie and white pants hang on a wooden hanger. the hoodie has a white drawstring and a white drawstring on the pants. the pants have a white drawstring and a white tag on the pants. the hanger is black. the wall is white.

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

a woman stands on the sidewalk, showcasing her black knee-high boots and a black bag with a gold chain. the boots are black, and the bag is black. the woman is wearing blue jeans and a black belt. the bag is on her shoulder, and the chain is on her bag. the boots are on her legs, and the bag is on her.

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

a man stands on a road, his hands in his pockets. his pants are brown, and his shirt is white. he wears a denim jacket and white shoes. the road is gray and the trees are green. the man is standing tall and straight, with his arms akimbo. the jacket is open, and his shirt is tucked in. the pants are loose, and the hem of his shirt is rolled up. the man is wearing a white t-shirt and a pair of white shoes.

\n", + "
\n", + " " + ] + }, + "metadata": {} + }, + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 33.5 s, sys: 246 ms, total: 33.7 s\n", + "Wall time: 56.7 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": 14, + "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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/activitynet_cap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/activitynet_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..2176595c357eeab9c2a4f57f819513409e97be23 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/activitynet_qa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/activitynet_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..556ce79d3dc6ee855e0e1c33d1c94605972f3a1e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/ai2d.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/ai2d.py new file mode 100644 index 0000000000000000000000000000000000000000..221091e795815493acf6ea58eadbbc2f5cda2cee --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/aokvqa_da.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/aokvqa_da.py new file mode 100644 index 0000000000000000000000000000000000000000..62ad8594bf97a6adb4b67c69933ccd806ecb59a3 --- /dev/null +++ b/Tipsomaly/model/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_da/{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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/aokvqa_mc.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/aokvqa_mc.py new file mode 100644 index 0000000000000000000000000000000000000000..66795ec0505f0010d5ca6b6e35767ce4e033841a --- /dev/null +++ b/Tipsomaly/model/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_mc/{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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/chartqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..df10fc301de8ae21e25fd1cc2ada520fc34dcbf2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/coco35l.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/coco35l.py new file mode 100644 index 0000000000000000000000000000000000000000..ba8aedcf2e721f88c32a652764f49a20e3f1e9be --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/cococap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/cococap.py new file mode 100644 index 0000000000000000000000000000000000000000..95202bb3105665932ff554a7d6300e7b71c626b8 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/common.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/common.py new file mode 100644 index 0000000000000000000000000000000000000000..3308bab66e847a86c45fdf54a10753c3eafecb4b --- /dev/null +++ b/Tipsomaly/model/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"], outkey="text", 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"], outkey="text", 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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/docvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/docvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..2bb9ea9f660820d8b2c7255813560bc8dfab766a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/forkme.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/forkme.py new file mode 100644 index 0000000000000000000000000000000000000000..210d886572eebf890e0ef3d22b56be018633db94 --- /dev/null +++ b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/forkme.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"""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, size='3b', 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) + if not c.size: + # PaliGemma + c.model.llm.variant = 'gemma_2b' + c.model_init = f'pt_{c.res}' + else: + # PaliGemma 2 + c.model.llm.variant = ( + 'gemma2_' + {'3b': '2b', '10b': '9b', '28b': '27b'}[c.size] + ) + c.model_init = f'pt_{c.size}_{c.res}' + c.model.llm.final_logits_softcap = 0.0 # Better for transfer. + + # 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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/gqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/gqa.py new file mode 100644 index 0000000000000000000000000000000000000000..531b0964d581c5dac68252cffae4c5eb44f3cffb --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/infovqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/infovqa.py new file mode 100644 index 0000000000000000000000000000000000000000..a9ea80adfbcc1acb82c9307f107f110bd648f896 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msrvtt_cap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msrvtt_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..6b81ae293deb99c85aac40366f41f83a555f2fed --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msrvtt_qa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msrvtt_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..08df9c56245b27db1d31a8f07de1b5967381e78c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msvd_qa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/msvd_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..0832ad7185c714056bb39c7390c711a5e03d3b48 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/nlvr2.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/nlvr2.py new file mode 100644 index 0000000000000000000000000000000000000000..3e76a158ba26986e890e9ab6b2e38d0499ebc34b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/ocrvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/ocrvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..3ce0efa8bcdf8aad9dd544301bab5586d1898552 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/okvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/okvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..cc5090362b3bf9ac8acfddbe8f02f279f80b4d2b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/pope.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..26610551504b5ecba78cecb0fd6903b1d9200f52 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/refcoco_seg.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/refcoco_seg.py new file mode 100644 index 0000000000000000000000000000000000000000..4e2725f2c4eee478a5799b607378e5615598b937 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/rsvqa_hr.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/rsvqa_hr.py new file mode 100644 index 0000000000000000000000000000000000000000..bfe5b1058db8f2d6962fb0d3442e33c920942219 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/rsvqa_lr.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/rsvqa_lr.py new file mode 100644 index 0000000000000000000000000000000000000000..2e0f91cefa97688209036be13b8900553d26c613 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/scicap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/scicap.py new file mode 100644 index 0000000000000000000000000000000000000000..51618033ef6d70db125b5e5cef092cc335cae9ae --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/science_qa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..5b8777b63f707a98b922be9a6eb6ee90834d27de --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/screen2words.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/screen2words.py new file mode 100644 index 0000000000000000000000000000000000000000..da0665291e0ed1bab103429694bda53034348f57 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/stvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/stvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..1f3be64e82cad0c29960fa06959340b0ce2c1231 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/tallyqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..738a95226e4fe31a558799e39a03e3c67dac0300 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/textcaps.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/textcaps.py new file mode 100644 index 0000000000000000000000000000000000000000..7249df806010fe0fd0ec80697df44a9f1816c5b4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/textvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/textvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0ddbea6e01221a390012f362da7e9375329bfb7f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vatex_cap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vatex_cap.py new file mode 100644 index 0000000000000000000000000000000000000000..78ae9f66da6bb2b4db2a6d3039b3d8f174f8de62 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vertexai_l4.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vertexai_l4.py new file mode 100644 index 0000000000000000000000000000000000000000..503f2eebf5d9f0ff0e4c24b2ad343d519bb0fc95 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vizwizvqa.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vizwizvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..58e361b61cf120ab329384e8cd7a1273b921e5ee --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vqav2.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/vqav2.py new file mode 100644 index 0000000000000000000000000000000000000000..0c7c00c7eef5b66664933600e240e2b556b9cd0b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/widgetcap.py b/Tipsomaly/model/big_vision/configs/proj/paligemma/transfers/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..d2dbfc4fcac63589ce2481a4331d9c403059f182 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/reward_tune/detection_reward.py b/Tipsomaly/model/big_vision/configs/proj/reward_tune/detection_reward.py new file mode 100644 index 0000000000000000000000000000000000000000..73e96c2b75e90ea74a5ffff40187e66a00dd8b4b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/scaling_laws/train_vit_g.py b/Tipsomaly/model/big_vision/configs/proj/scaling_laws/train_vit_g.py new file mode 100644 index 0000000000000000000000000000000000000000..137e51cb629613f473ed7d34584f0117ab308465 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/README.md b/Tipsomaly/model/big_vision/configs/proj/uvim/README.md new file mode 100644 index 0000000000000000000000000000000000000000..8a6bfa8313f3608f2e8c725908248783e6bb1140 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py b/Tipsomaly/model/big_vision/configs/proj/uvim/train_coco_panoptic_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..aa68eb7994115d06cf7b148fba62b89f20e6860a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/train_imagenet2012_colorization_pretrained.py b/Tipsomaly/model/big_vision/configs/proj/uvim/train_imagenet2012_colorization_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..0245db559e2454584be3dc617401be4cf327df78 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/train_nyu_depth_pretrained.py b/Tipsomaly/model/big_vision/configs/proj/uvim/train_nyu_depth_pretrained.py new file mode 100644 index 0000000000000000000000000000000000000000..d2e05810708e03ad9e198025a8641eec53811830 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_color_task.ipynb b/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_color_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a1ded392586375100e807f0bcdfd7b07be88bd5e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_depth_task.ipynb b/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_depth_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a4bfb3f0381238b4a0c3048006ff9fbc0ec848c7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_panoptic_task.ipynb b/Tipsomaly/model/big_vision/configs/proj/uvim/uvim_panoptic_task.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..c03324e5a6735f96c72f1eaecbba3cbf2b6dc843 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_coco_panoptic.py b/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..5a620bae982e59187a02e54a0b735e346ad7d7ac --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_imagenet2012_colorization.py b/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_imagenet2012_colorization.py new file mode 100644 index 0000000000000000000000000000000000000000..d4ecb7a57b6a47f573eca63f5b2652cdcc33dc47 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_nyu_depth.py b/Tipsomaly/model/big_vision/configs/proj/uvim/vqvae_nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..d5ae29321bfa38bd2df097092126e0f3f15870f4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/transfer.py b/Tipsomaly/model/big_vision/configs/transfer.py new file mode 100644 index 0000000000000000000000000000000000000000..1ee64e43b274bdf5d462c7e3f3d9b5fc085c1796 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/vit_i1k.py b/Tipsomaly/model/big_vision/configs/vit_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..8e6dd8d18ba723175a7a0cf887352e3023a11ccc --- /dev/null +++ b/Tipsomaly/model/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_data' 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/Tipsomaly/model/big_vision/configs/vit_i21k.py b/Tipsomaly/model/big_vision/configs/vit_i21k.py new file mode 100644 index 0000000000000000000000000000000000000000..adae41838736be4f4a9737e614152dc5c7fd329b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/configs/vit_s16_i1k.py b/Tipsomaly/model/big_vision/configs/vit_s16_i1k.py new file mode 100644 index 0000000000000000000000000000000000000000..d50dd26508713b67c434f0e677e58fbef7d8af13 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/ai2d/ai2d.py b/Tipsomaly/model/big_vision/datasets/ai2d/ai2d.py new file mode 100644 index 0000000000000000000000000000000000000000..710be04604a1d837de766f2b3ff436e56c585957 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/aokvqa/aokvqa.py b/Tipsomaly/model/big_vision/datasets/aokvqa/aokvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..497028110225a62cdab1b51e489a77f3d3719516 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/chartqa/chartqa.py b/Tipsomaly/model/big_vision/datasets/chartqa/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..90748b691cd47b16b81ba8fdb733d26694963745 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/coco35l/coco35l.py b/Tipsomaly/model/big_vision/datasets/coco35l/coco35l.py new file mode 100644 index 0000000000000000000000000000000000000000..0f390222506c8a16a0bcbe5eab495a2e7efa30d0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/core.py b/Tipsomaly/model/big_vision/datasets/core.py new file mode 100644 index 0000000000000000000000000000000000000000..07d2a2c6814646908fc5133cb5a54aec6d3b57b3 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/countbenchqa/countbenchqa.py b/Tipsomaly/model/big_vision/datasets/countbenchqa/countbenchqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0994cae86b2ff4758dd4611c0ae4295c2d86b4e6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/countbenchqa/data/countbench_paired_questions.json b/Tipsomaly/model/big_vision/datasets/countbenchqa/data/countbench_paired_questions.json new file mode 100644 index 0000000000000000000000000000000000000000..bb684fac07600d00e624d203878736e5e3e90e1d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/docvqa/docvqa.py b/Tipsomaly/model/big_vision/datasets/docvqa/docvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..f1a496ca24c4b2c5714c10d155585cb6194bd783 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/gqa/gqa.py b/Tipsomaly/model/big_vision/datasets/gqa/gqa.py new file mode 100644 index 0000000000000000000000000000000000000000..ea137cbfa41923fca712ee40977f75b855a7966d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/imagenet/class_names.py b/Tipsomaly/model/big_vision/datasets/imagenet/class_names.py new file mode 100644 index 0000000000000000000000000000000000000000..410ce3379a00f253222b94a8341fe99c2e0baa8d --- /dev/null +++ b/Tipsomaly/model/big_vision/datasets/imagenet/class_names.py @@ -0,0 +1,4758 @@ +# 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, +] + +# The name of the imagenet21k classes. from nltk with post-processing +# (replace "_", no alias names), more see +# https://colab.research.google.com/drive/1I7oVTyT8nDB8K_-rGIa8GgeSA9BeWAk5#scrollTo=byaZcr50X-S6 +IMAGENET21k_CLASS_NAMES = [ + 'organism', 'benthos', 'heterotroph', 'cell', 'person', 'animal', 'plant', + 'food', 'artifact', 'hop', 'check-in', 'dressage', 'curvet', 'piaffe', + 'funambulism', 'rock climbing', 'contact sport', 'outdoor sport', + 'gymnastics', 'acrobatics', 'track and field', 'track', 'jumping', + 'broad jump', 'high jump', 'fosbury flop', 'skiing', 'cross-country skiing', + 'ski jumping', 'water sport', 'swimming', 'bathe', 'dip', 'dive', + 'floating', 'dead-man\'s float', 'belly flop', 'cliff diving', 'flip', + 'gainer', 'half gainer', 'jackknife', 'swan dive', 'skin diving', + 'scuba diving', 'snorkeling', 'surfing', 'water-skiing', 'rowing', + 'sculling', 'boxing', 'professional boxing', 'in-fighting', 'fight', + 'rope-a-dope', 'spar', 'archery', 'sledding', 'tobogganing', 'luging', + 'bobsledding', 'wrestling', 'greco-roman wrestling', + 'professional wrestling', 'sumo', 'skating', 'ice skating', + 'figure skating', 'rollerblading', 'roller skating', 'skateboarding', + 'speed skating', 'racing', 'auto racing', 'boat racing', + 'hydroplane racing', 'camel racing', 'greyhound racing', 'horse racing', + 'riding', 'equestrian sport', 'pony-trekking', 'showjumping', + 'cross-country riding', 'cycling', 'bicycling', 'motorcycling', + 'dune cycling', 'blood sport', 'bullfighting', 'cockfighting', 'hunt', + 'battue', 'beagling', 'coursing', 'deer hunting', 'ducking', 'fox hunting', + 'pigsticking', 'fishing', 'angling', 'fly-fishing', 'troll', 'casting', + 'bait casting', 'fly casting', 'overcast', 'surf casting', 'day game', + 'athletic game', 'ice hockey', 'tetherball', 'water polo', 'outdoor game', + 'golf', 'professional golf', 'round of golf', 'medal play', 'match play', + 'miniature golf', 'croquet', 'quoits', 'shuffleboard', 'field game', + 'field hockey', 'shinny', 'football', 'american football', + 'professional football', 'touch football', 'hurling', 'rugby', 'ball game', + 'baseball', 'ball', 'professional baseball', 'hardball', 'perfect game', + 'no-hit game', 'one-hitter', 'two-hitter', 'three-hitter', 'four-hitter', + 'five-hitter', 'softball', 'rounders', 'stickball', 'cricket', 'lacrosse', + 'polo', 'pushball', 'soccer', 'court game', 'handball', 'racquetball', + 'fives', 'squash', 'volleyball', 'jai alai', 'badminton', 'battledore', + 'basketball', 'professional basketball', 'deck tennis', 'netball', 'tennis', + 'professional tennis', 'singles', 'singles', 'doubles', 'doubles', + 'royal tennis', 'pallone', 'sport', 'clasp', 'judo', 'team sport', + 'last supper', 'seder', 'camping', 'pest', 'critter', 'creepy-crawly', + 'darter', 'peeper', 'homeotherm', 'poikilotherm', 'range animal', + 'scavenger', 'bottom-feeder', 'bottom-feeder', 'work animal', + 'beast of burden', 'draft animal', 'pack animal', 'domestic animal', + 'feeder', 'feeder', 'stocker', 'hatchling', 'head', 'migrator', 'molter', + 'pet', 'stayer', 'stunt', 'marine animal', 'by-catch', 'female', 'hen', + 'male', 'adult', 'young', 'orphan', 'young mammal', 'baby', 'pup', + 'wolf pup', 'puppy', 'cub', 'lion cub', 'bear cub', 'tiger cub', 'kit', + 'suckling', 'sire', 'dam', 'thoroughbred', 'giant', 'mutant', 'carnivore', + 'herbivore', 'insectivore', 'acrodont', 'pleurodont', 'microorganism', + 'monohybrid', 'arbovirus', 'adenovirus', 'arenavirus', 'marburg virus', + 'arenaviridae', 'vesiculovirus', 'reoviridae', 'variola major', 'viroid', + 'coliphage', 'paramyxovirus', 'poliovirus', 'herpes', 'herpes simplex 1', + 'herpes zoster', 'herpes varicella zoster', 'cytomegalovirus', + 'varicella zoster virus', 'polyoma', 'lyssavirus', 'reovirus', 'rotavirus', + 'moneran', 'archaebacteria', 'bacteroid', 'bacillus anthracis', + 'yersinia pestis', 'brucella', 'spirillum', 'botulinus', + 'clostridium perfringens', 'cyanobacteria', 'trichodesmium', + 'nitric bacteria', 'spirillum', 'francisella', 'gonococcus', + 'corynebacterium diphtheriae', 'enteric bacteria', 'klebsiella', + 'salmonella typhimurium', 'typhoid bacillus', 'nitrate bacterium', + 'nitrite bacterium', 'actinomycete', 'streptomyces', + 'streptomyces erythreus', 'streptomyces griseus', 'tubercle bacillus', + 'pus-forming bacteria', 'streptobacillus', 'myxobacteria', 'staphylococcus', + 'diplococcus', 'pneumococcus', 'streptococcus', 'spirochete', + 'planktonic algae', 'zooplankton', 'parasite', 'endoparasite', + 'ectoparasite', 'pathogen', 'commensal', 'myrmecophile', 'protoctist', + 'protozoan', 'sarcodinian', 'heliozoan', 'endameba', 'ameba', 'globigerina', + 'testacean', 'arcella', 'difflugia', 'ciliate', 'paramecium', 'stentor', + 'alga', 'arame', 'seagrass', 'golden algae', 'yellow-green algae', + 'brown algae', 'kelp', 'fucoid', 'fucoid', 'fucus', 'bladderwrack', + 'green algae', 'pond scum', 'chlorella', 'stonewort', 'desmid', 'sea moss', + 'eukaryote', 'prokaryote', 'zooid', 'leishmania', 'zoomastigote', + 'polymastigote', 'costia', 'giardia', 'cryptomonad', 'sporozoan', + 'sporozoite', 'trophozoite', 'merozoite', 'coccidium', 'gregarine', + 'plasmodium', 'leucocytozoan', 'microsporidian', 'ostariophysi', + 'cypriniform fish', 'loach', 'cyprinid', 'carp', 'domestic carp', + 'leather carp', 'mirror carp', 'european bream', 'tench', 'dace', 'chub', + 'shiner', 'common shiner', 'roach', 'rudd', 'minnow', 'gudgeon', 'goldfish', + 'crucian carp', 'electric eel', 'catostomid', 'buffalo fish', + 'black buffalo', 'hog sucker', 'redhorse', 'cyprinodont', 'killifish', + 'mummichog', 'striped killifish', 'rivulus', 'flagfish', 'swordtail', + 'guppy', 'topminnow', 'mosquitofish', 'platy', 'mollie', 'squirrelfish', + 'reef squirrelfish', 'deepwater squirrelfish', 'holocentrus ascensionis', + 'soldierfish', 'anomalops', 'flashlight fish', 'john dory', 'boarfish', + 'boarfish', 'cornetfish', 'stickleback', 'three-spined stickleback', + 'ten-spined stickleback', 'pipefish', 'dwarf pipefish', + 'deepwater pipefish', 'seahorse', 'snipefish', 'shrimpfish', 'trumpetfish', + 'pellicle', 'embryo', 'fetus', 'abortus', 'spawn', 'blastula', 'blastocyst', + 'gastrula', 'morula', 'yolk', 'chordate', 'cephalochordate', 'lancelet', + 'tunicate', 'ascidian', 'sea squirt', 'salp', 'doliolum', 'larvacean', + 'appendicularia', 'ascidian tadpole', 'vertebrate', 'amniota', 'amniote', + 'aquatic vertebrate', 'jawless vertebrate', 'ostracoderm', 'heterostracan', + 'anaspid', 'conodont', 'cyclostome', 'lamprey', 'sea lamprey', 'hagfish', + 'myxine glutinosa', 'eptatretus', 'gnathostome', 'placoderm', + 'cartilaginous fish', 'holocephalan', 'chimaera', 'rabbitfish', + 'elasmobranch', 'shark', 'cow shark', 'mackerel shark', 'porbeagle', 'mako', + 'shortfin mako', 'longfin mako', 'bonito shark', 'great white shark', + 'basking shark', 'thresher', 'carpet shark', 'nurse shark', 'sand tiger', + 'whale shark', 'requiem shark', 'bull shark', 'sandbar shark', + 'blacktip shark', 'whitetip shark', 'dusky shark', 'lemon shark', + 'blue shark', 'tiger shark', 'soupfin shark', 'dogfish', 'smooth dogfish', + 'smoothhound', 'american smooth dogfish', 'florida smoothhound', + 'whitetip shark', 'spiny dogfish', 'atlantic spiny dogfish', + 'pacific spiny dogfish', 'hammerhead', 'smooth hammerhead', + 'smalleye hammerhead', 'shovelhead', 'angel shark', 'ray', 'electric ray', + 'sawfish', 'smalltooth sawfish', 'guitarfish', 'stingray', + 'roughtail stingray', 'butterfly ray', 'eagle ray', 'spotted eagle ray', + 'cownose ray', 'manta', 'atlantic manta', 'devil ray', 'skate', + 'grey skate', 'little skate', 'thorny skate', 'barndoor skate', 'bird', + 'dickeybird', 'fledgling', 'nestling', 'cock', 'gamecock', 'hen', 'nester', + 'night bird', 'night raven', 'bird of passage', 'archaeopteryx', + 'archaeornis', 'ratite', 'carinate', 'ostrich', 'cassowary', 'emu', 'kiwi', + 'rhea', 'rhea', 'elephant bird', 'moa', 'passerine', 'nonpasserine bird', + 'oscine', 'songbird', 'honey eater', 'accentor', 'hedge sparrow', 'lark', + 'skylark', 'wagtail', 'pipit', 'meadow pipit', 'finch', 'chaffinch', + 'brambling', 'goldfinch', 'linnet', 'siskin', 'red siskin', 'redpoll', + 'redpoll', 'new world goldfinch', 'pine siskin', 'house finch', + 'purple finch', 'canary', 'common canary', 'serin', 'crossbill', + 'bullfinch', 'junco', 'dark-eyed junco', 'new world sparrow', + 'vesper sparrow', 'white-throated sparrow', 'white-crowned sparrow', + 'chipping sparrow', 'field sparrow', 'tree sparrow', 'song sparrow', + 'swamp sparrow', 'bunting', 'indigo bunting', 'ortolan', 'reed bunting', + 'yellowhammer', 'yellow-breasted bunting', 'snow bunting', 'honeycreeper', + 'banana quit', 'sparrow', 'english sparrow', 'tree sparrow', 'grosbeak', + 'evening grosbeak', 'hawfinch', 'pine grosbeak', 'cardinal', 'pyrrhuloxia', + 'towhee', 'chewink', 'green-tailed towhee', 'weaver', 'baya', 'whydah', + 'java sparrow', 'avadavat', 'grassfinch', 'zebra finch', 'honeycreeper', + 'lyrebird', 'scrubbird', 'broadbill', 'tyrannid', 'new world flycatcher', + 'kingbird', 'arkansas kingbird', 'cassin\'s kingbird', 'eastern kingbird', + 'grey kingbird', 'pewee', 'western wood pewee', 'phoebe', + 'vermillion flycatcher', 'cotinga', 'cock of the rock', 'cock of the rock', + 'manakin', 'bellbird', 'umbrella bird', 'ovenbird', 'antbird', 'ant thrush', + 'ant shrike', 'spotted antbird', 'woodhewer', 'pitta', 'scissortail', + 'old world flycatcher', 'spotted flycatcher', 'thickhead', 'thrush', + 'missel thrush', 'song thrush', 'fieldfare', 'redwing', 'blackbird', + 'ring ouzel', 'robin', 'clay-colored robin', 'hermit thrush', 'veery', + 'wood thrush', 'nightingale', 'thrush nightingale', 'bulbul', + 'old world chat', 'stonechat', 'whinchat', 'solitaire', 'redstart', + 'wheatear', 'bluebird', 'robin', 'bluethroat', 'warbler', 'gnatcatcher', + 'kinglet', 'goldcrest', 'gold-crowned kinglet', 'ruby-crowned kinglet', + 'old world warbler', 'blackcap', 'greater whitethroat', + 'lesser whitethroat', 'wood warbler', 'sedge warbler', 'wren warbler', + 'tailorbird', 'babbler', 'new world warbler', 'parula warbler', + 'wilson\'s warbler', 'flycatching warbler', 'american redstart', + 'cape may warbler', 'yellow warbler', 'blackburn', 'audubon\'s warbler', + 'myrtle warbler', 'blackpoll', 'new world chat', 'yellow-breasted chat', + 'ovenbird', 'water thrush', 'yellowthroat', 'common yellowthroat', + 'riflebird', 'new world oriole', 'northern oriole', 'baltimore oriole', + 'bullock\'s oriole', 'orchard oriole', 'meadowlark', 'eastern meadowlark', + 'western meadowlark', 'cacique', 'bobolink', 'new world blackbird', + 'grackle', 'purple grackle', 'rusty blackbird', 'cowbird', + 'red-winged blackbird', 'old world oriole', 'golden oriole', 'fig-bird', + 'starling', 'common starling', 'rose-colored starling', 'myna', + 'crested myna', 'hill myna', 'corvine bird', 'crow', 'american crow', + 'raven', 'rook', 'jackdaw', 'chough', 'jay', 'old world jay', + 'common european jay', 'new world jay', 'blue jay', 'canada jay', + 'rocky mountain jay', 'nutcracker', 'common nutcracker', + 'clark\'s nutcracker', 'magpie', 'european magpie', 'american magpie', + 'australian magpie', 'butcherbird', 'currawong', 'piping crow', 'wren', + 'winter wren', 'house wren', 'marsh wren', 'long-billed marsh wren', + 'sedge wren', 'rock wren', 'carolina wren', 'cactus wren', 'mockingbird', + 'blue mockingbird', 'catbird', 'thrasher', 'brown thrasher', + 'new zealand wren', 'rock wren', 'rifleman bird', 'creeper', + 'brown creeper', 'european creeper', 'wall creeper', 'european nuthatch', + 'red-breasted nuthatch', 'white-breasted nuthatch', 'titmouse', 'chickadee', + 'black-capped chickadee', 'tufted titmouse', 'carolina chickadee', + 'blue tit', 'bushtit', 'wren-tit', 'verdin', 'fairy bluebird', 'swallow', + 'barn swallow', 'cliff swallow', 'tree swallow', 'white-bellied swallow', + 'martin', 'house martin', 'bank martin', 'purple martin', 'wood swallow', + 'tanager', 'scarlet tanager', 'western tanager', 'summer tanager', + 'hepatic tanager', 'shrike', 'butcherbird', 'european shrike', + 'northern shrike', 'white-rumped shrike', 'loggerhead shrike', + 'migrant shrike', 'bush shrike', 'black-fronted bush shrike', 'bowerbird', + 'satin bowerbird', 'great bowerbird', 'water ouzel', 'european water ouzel', + 'american water ouzel', 'vireo', 'red-eyed vireo', 'solitary vireo', + 'blue-headed vireo', 'waxwing', 'cedar waxwing', 'bohemian waxwing', + 'bird of prey', 'accipitriformes', 'hawk', 'eyas', 'tiercel', 'goshawk', + 'sparrow hawk', 'cooper\'s hawk', 'chicken hawk', 'buteonine', 'redtail', + 'rough-legged hawk', 'red-shouldered hawk', 'buzzard', 'honey buzzard', + 'kite', 'black kite', 'swallow-tailed kite', 'white-tailed kite', 'harrier', + 'marsh harrier', 'montagu\'s harrier', 'marsh hawk', 'harrier eagle', + 'falcon', 'peregrine', 'falcon-gentle', 'gyrfalcon', 'kestrel', + 'sparrow hawk', 'pigeon hawk', 'hobby', 'caracara', 'audubon\'s caracara', + 'carancha', 'eagle', 'young bird', 'eaglet', 'harpy', 'golden eagle', + 'tawny eagle', 'bald eagle', 'sea eagle', 'kamchatkan sea eagle', 'ern', + 'fishing eagle', 'osprey', 'vulture', 'aegypiidae', 'old world vulture', + 'griffon vulture', 'bearded vulture', 'egyptian vulture', 'black vulture', + 'secretary bird', 'new world vulture', 'buzzard', 'condor', 'andean condor', + 'california condor', 'black vulture', 'king vulture', 'owl', 'owlet', + 'little owl', 'horned owl', 'great horned owl', 'great grey owl', + 'tawny owl', 'barred owl', 'screech owl', 'screech owl', 'scops owl', + 'spotted owl', 'old world scops owl', 'oriental scops owl', 'hoot owl', + 'hawk owl', 'long-eared owl', 'laughing owl', 'barn owl', 'amphibian', + 'ichyostega', 'urodele', 'salamander', 'european fire salamander', + 'spotted salamander', 'alpine salamander', 'newt', 'common newt', 'red eft', + 'pacific newt', 'rough-skinned newt', 'california newt', 'eft', + 'ambystomid', 'mole salamander', 'spotted salamander', 'tiger salamander', + 'axolotl', 'waterdog', 'hellbender', 'giant salamander', 'olm', 'mud puppy', + 'dicamptodon', 'pacific giant salamander', 'olympic salamander', + 'lungless salamander', 'eastern red-backed salamander', + 'western red-backed salamander', 'dusky salamander', 'climbing salamander', + 'arboreal salamander', 'slender salamander', 'web-toed salamander', + 'shasta salamander', 'limestone salamander', 'amphiuma', 'siren', 'frog', + 'true frog', 'wood-frog', 'leopard frog', 'bullfrog', 'green frog', + 'cascades frog', 'goliath frog', 'pickerel frog', 'tarahumara frog', + 'grass frog', 'leptodactylid frog', 'robber frog', 'barking frog', + 'crapaud', 'tree frog', 'tailed frog', 'liopelma hamiltoni', 'true toad', + 'bufo', 'agua', 'european toad', 'natterjack', 'american toad', + 'eurasian green toad', 'american green toad', 'yosemite toad', 'texas toad', + 'southwestern toad', 'western toad', 'obstetrical toad', 'midwife toad', + 'fire-bellied toad', 'spadefoot', 'western spadefoot', 'southern spadefoot', + 'plains spadefoot', 'tree toad', 'spring peeper', 'pacific tree toad', + 'canyon treefrog', 'chameleon tree frog', 'cricket frog', + 'northern cricket frog', 'eastern cricket frog', 'chorus frog', + 'lowland burrowing treefrog', 'western narrow-mouthed toad', + 'eastern narrow-mouthed toad', 'sheep frog', 'tongueless frog', + 'surinam toad', 'african clawed frog', 'south american poison toad', + 'caecilian', 'reptile', 'anapsid', 'diapsid', 'diapsida', 'chelonian', + 'turtle', 'sea turtle', 'green turtle', 'loggerhead', 'ridley', + 'atlantic ridley', 'pacific ridley', 'hawksbill turtle', + 'leatherback turtle', 'snapping turtle', 'common snapping turtle', + 'alligator snapping turtle', 'mud turtle', 'musk turtle', 'terrapin', + 'diamondback terrapin', 'red-bellied terrapin', 'slider', 'cooter', + 'box turtle', 'western box turtle', 'painted turtle', 'tortoise', + 'european tortoise', 'giant tortoise', 'gopher tortoise', 'desert tortoise', + 'texas tortoise', 'soft-shelled turtle', 'spiny softshell', + 'smooth softshell', 'tuatara', 'saurian', 'lizard', 'gecko', 'flying gecko', + 'banded gecko', 'iguanid', 'common iguana', 'marine iguana', + 'desert iguana', 'chuckwalla', 'zebra-tailed lizard', 'fringe-toed lizard', + 'earless lizard', 'collared lizard', 'leopard lizard', 'spiny lizard', + 'fence lizard', 'western fence lizard', 'eastern fence lizard', + 'sagebrush lizard', 'side-blotched lizard', 'tree lizard', 'horned lizard', + 'texas horned lizard', 'basilisk', 'american chameleon', 'worm lizard', + 'night lizard', 'skink', 'western skink', 'mountain skink', 'teiid lizard', + 'whiptail', 'racerunner', 'plateau striped whiptail', + 'chihuahuan spotted whiptail', 'western whiptail', 'checkered whiptail', + 'teju', 'caiman lizard', 'agamid', 'agama', 'frilled lizard', 'moloch', + 'mountain devil', 'anguid lizard', 'alligator lizard', 'blindworm', + 'glass lizard', 'legless lizard', 'lanthanotus borneensis', + 'venomous lizard', 'gila monster', 'beaded lizard', 'lacertid lizard', + 'sand lizard', 'green lizard', 'chameleon', 'african chameleon', + 'horned chameleon', 'monitor', 'african monitor', 'komodo dragon', + 'crocodilian reptile', 'crocodile', 'african crocodile', 'asian crocodile', + 'morlett\'s crocodile', 'false gavial', 'alligator', 'american alligator', + 'chinese alligator', 'caiman', 'spectacled caiman', 'gavial', + 'armored dinosaur', 'stegosaur', 'ankylosaur', 'edmontonia', + 'bone-headed dinosaur', 'pachycephalosaur', 'ceratopsian', 'protoceratops', + 'triceratops', 'styracosaur', 'psittacosaur', 'ornithopod', 'hadrosaur', + 'trachodon', 'saurischian', 'sauropod', 'apatosaur', 'barosaur', + 'diplodocus', 'argentinosaur', 'theropod', 'ceratosaur', 'coelophysis', + 'tyrannosaur', 'allosaur', 'ornithomimid', 'maniraptor', 'oviraptorid', + 'velociraptor', 'deinonychus', 'utahraptor', 'synapsid', 'dicynodont', + 'pelycosaur', 'dimetrodon', 'pterosaur', 'pterodactyl', 'ichthyosaur', + 'ichthyosaurus', 'stenopterygius', 'plesiosaur', 'nothosaur', 'snake', + 'colubrid snake', 'hoop snake', 'thunder snake', 'ringneck snake', + 'hognose snake', 'leaf-nosed snake', 'green snake', 'smooth green snake', + 'rough green snake', 'green snake', 'racer', 'blacksnake', 'blue racer', + 'horseshoe whipsnake', 'whip-snake', 'coachwhip', 'california whipsnake', + 'sonoran whipsnake', 'rat snake', 'corn snake', 'black rat snake', + 'chicken snake', 'indian rat snake', 'glossy snake', 'bull snake', + 'gopher snake', 'pine snake', 'king snake', 'common kingsnake', + 'milk snake', 'garter snake', 'common garter snake', 'ribbon snake', + 'western ribbon snake', 'lined snake', 'ground snake', + 'eastern ground snake', 'water snake', 'common water snake', + 'water moccasin', 'grass snake', 'viperine grass snake', + 'red-bellied snake', 'sand snake', 'banded sand snake', + 'black-headed snake', 'vine snake', 'lyre snake', 'sonoran lyre snake', + 'night snake', 'blind snake', 'western blind snake', 'indigo snake', + 'eastern indigo snake', 'constrictor', 'boa', 'boa constrictor', + 'rubber boa', 'rosy boa', 'anaconda', 'python', 'carpet snake', + 'reticulated python', 'indian python', 'rock python', 'amethystine python', + 'elapid', 'coral snake', 'eastern coral snake', 'western coral snake', + 'coral snake', 'african coral snake', 'australian coral snake', + 'copperhead', 'cobra', 'indian cobra', 'asp', 'black-necked cobra', + 'hamadryad', 'ringhals', 'mamba', 'black mamba', 'green mamba', + 'death adder', 'tiger snake', 'australian blacksnake', 'krait', + 'banded krait', 'taipan', 'sea snake', 'viper', 'adder', 'asp', + 'puff adder', 'gaboon viper', 'horned viper', 'pit viper', 'copperhead', + 'water moccasin', 'rattlesnake', 'diamondback', 'timber rattlesnake', + 'canebrake rattlesnake', 'prairie rattlesnake', 'sidewinder', + 'western diamondback', 'rock rattlesnake', 'tiger rattlesnake', + 'mojave rattlesnake', 'speckled rattlesnake', 'massasauga', + 'ground rattler', 'fer-de-lance', 'carcase', 'carrion', 'arthropod', + 'trilobite', 'arachnid', 'harvestman', 'scorpion', 'false scorpion', + 'book scorpion', 'whip-scorpion', 'vinegarroon', 'spider', + 'orb-weaving spider', 'black and gold garden spider', 'barn spider', + 'garden spider', 'comb-footed spider', 'black widow', 'tarantula', + 'wolf spider', 'european wolf spider', 'trap-door spider', 'acarine', + 'tick', 'hard tick', 'ixodes dammini', 'ixodes neotomae', + 'ixodes pacificus', 'ixodes scapularis', 'sheep-tick', 'ixodes persulcatus', + 'ixodes dentatus', 'ixodes spinipalpis', 'wood tick', 'soft tick', 'mite', + 'web-spinning mite', 'acarid', 'trombidiid', 'trombiculid', 'harvest mite', + 'acarus', 'itch mite', 'rust mite', 'spider mite', 'red spider', 'myriapod', + 'garden centipede', 'tardigrade', 'centipede', 'house centipede', + 'millipede', 'sea spider', 'merostomata', 'horseshoe crab', + 'asian horseshoe crab', 'eurypterid', 'tongue worm', 'gallinaceous bird', + 'domestic fowl', 'dorking', 'plymouth rock', 'cornish', 'rock cornish', + 'game fowl', 'cochin', 'jungle fowl', 'jungle cock', 'jungle hen', + 'red jungle fowl', 'chicken', 'bantam', 'chick', 'cock', 'cockerel', + 'capon', 'hen', 'cackler', 'brood hen', 'mother hen', 'layer', 'pullet', + 'spring chicken', 'rhode island red', 'dominique', 'orpington', 'turkey', + 'turkey cock', 'ocellated turkey', 'grouse', 'black grouse', + 'european black grouse', 'asian black grouse', 'blackcock', 'greyhen', + 'ptarmigan', 'red grouse', 'moorhen', 'capercaillie', 'spruce grouse', + 'sage grouse', 'ruffed grouse', 'sharp-tailed grouse', 'prairie chicken', + 'greater prairie chicken', 'lesser prairie chicken', 'heath hen', 'guan', + 'curassow', 'piping guan', 'chachalaca', 'texas chachalaca', 'megapode', + 'mallee fowl', 'mallee hen', 'brush turkey', 'maleo', 'phasianid', + 'pheasant', 'ring-necked pheasant', 'afropavo', 'argus', 'golden pheasant', + 'bobwhite', 'northern bobwhite', 'old world quail', 'migratory quail', + 'monal', 'peafowl', 'peachick', 'peacock', 'peahen', 'blue peafowl', + 'green peafowl', 'quail', 'california quail', 'tragopan', 'partridge', + 'hungarian partridge', 'red-legged partridge', 'greek partridge', + 'mountain quail', 'guinea fowl', 'guinea hen', 'hoatzin', 'tinamou', + 'columbiform bird', 'dodo', 'pigeon', 'pouter pigeon', 'dove', 'rock dove', + 'band-tailed pigeon', 'wood pigeon', 'turtledove', 'streptopelia turtur', + 'ringdove', 'australian turtledove', 'mourning dove', 'domestic pigeon', + 'squab', 'fairy swallow', 'roller', 'homing pigeon', 'carrier pigeon', + 'passenger pigeon', 'sandgrouse', 'painted sandgrouse', + 'pin-tailed sandgrouse', 'pallas\'s sandgrouse', 'parrot', 'popinjay', + 'poll', 'african grey', 'amazon', 'macaw', 'kea', 'cockatoo', + 'sulphur-crested cockatoo', 'pink cockatoo', 'cockateel', 'lovebird', + 'lory', 'lorikeet', 'varied lorikeet', 'rainbow lorikeet', 'parakeet', + 'carolina parakeet', 'budgerigar', 'ring-necked parakeet', + 'cuculiform bird', 'cuckoo', 'european cuckoo', 'black-billed cuckoo', + 'roadrunner', 'ani', 'coucal', 'crow pheasant', 'touraco', + 'coraciiform bird', 'roller', 'european roller', 'ground roller', + 'kingfisher', 'eurasian kingfisher', 'belted kingfisher', 'kookaburra', + 'bee eater', 'hornbill', 'hoopoe', 'euopean hoopoe', 'wood hoopoe', + 'motmot', 'tody', 'apodiform bird', 'swift', 'european swift', + 'chimney swift', 'swiftlet', 'tree swift', 'hummingbird', + 'archilochus colubris', 'thornbill', 'goatsucker', 'european goatsucker', + 'chuck-will\'s-widow', 'whippoorwill', 'poorwill', 'frogmouth', 'oilbird', + 'piciform bird', 'woodpecker', 'green woodpecker', 'downy woodpecker', + 'flicker', 'yellow-shafted flicker', 'gilded flicker', + 'red-shafted flicker', 'ivorybill', 'redheaded woodpecker', 'sapsucker', + 'yellow-bellied sapsucker', 'red-breasted sapsucker', 'wryneck', 'piculet', + 'barbet', 'puffbird', 'honey guide', 'jacamar', 'toucan', 'toucanet', + 'trogon', 'quetzal', 'resplendent quetzel', 'aquatic bird', 'waterfowl', + 'anseriform bird', 'duck', 'drake', 'quack-quack', 'duckling', + 'diving duck', 'dabbling duck', 'mallard', 'black duck', 'teal', + 'greenwing', 'bluewing', 'garganey', 'widgeon', 'american widgeon', + 'shoveler', 'pintail', 'sheldrake', 'shelduck', 'ruddy duck', 'bufflehead', + 'goldeneye', 'barrow\'s goldeneye', 'canvasback', 'pochard', 'redhead', + 'scaup', 'greater scaup', 'lesser scaup', 'wild duck', 'wood duck', + 'wood drake', 'mandarin duck', 'muscovy duck', 'sea duck', 'eider', + 'scoter', 'common scoter', 'old squaw', 'merganser', 'goosander', + 'american merganser', 'red-breasted merganser', 'smew', 'hooded merganser', + 'goose', 'gosling', 'gander', 'chinese goose', 'greylag', 'blue goose', + 'snow goose', 'brant', 'common brant goose', 'honker', 'barnacle goose', + 'coscoroba', 'swan', 'cob', 'pen', 'cygnet', 'mute swan', 'whooper', + 'tundra swan', 'whistling swan', 'bewick\'s swan', 'trumpeter', + 'black swan', 'screamer', 'horned screamer', 'crested screamer', 'chaja', + 'mammal', 'female mammal', 'tusker', 'prototherian', 'monotreme', 'echidna', + 'echidna', 'platypus', 'marsupial', 'opossum', 'common opossum', + 'crab-eating opossum', 'opossum rat', 'bandicoot', 'rabbit-eared bandicoot', + 'kangaroo', 'giant kangaroo', 'wallaby', 'common wallaby', 'hare wallaby', + 'nail-tailed wallaby', 'rock wallaby', 'pademelon', 'tree wallaby', + 'musk kangaroo', 'rat kangaroo', 'potoroo', 'bettong', 'jerboa kangaroo', + 'phalanger', 'cuscus', 'brush-tailed phalanger', 'flying phalanger', + 'koala', 'wombat', 'dasyurid marsupial', 'dasyure', 'eastern dasyure', + 'native cat', 'thylacine', 'tasmanian devil', 'pouched mouse', 'numbat', + 'pouched mole', 'placental', 'livestock', 'bull', 'cow', 'calf', 'calf', + 'yearling', 'buck', 'doe', 'insectivore', 'mole', 'starnose mole', + 'brewer\'s mole', 'golden mole', 'shrew mole', 'asiatic shrew mole', + 'american shrew mole', 'shrew', 'common shrew', 'masked shrew', + 'short-tailed shrew', 'water shrew', 'american water shrew', + 'european water shrew', 'mediterranean water shrew', 'least shrew', + 'hedgehog', 'tenrec', 'tailless tenrec', 'otter shrew', 'eiderdown', + 'aftershaft', 'sickle feather', 'contour feather', 'bastard wing', + 'saddle hackle', 'encolure', 'hair', 'squama', 'scute', 'sclerite', + 'plastron', 'scallop shell', 'oyster shell', 'theca', 'invertebrate', + 'sponge', 'choanocyte', 'glass sponge', 'venus\'s flower basket', + 'metazoan', 'coelenterate', 'planula', 'polyp', 'medusa', 'jellyfish', + 'scyphozoan', 'chrysaora quinquecirrha', 'hydrozoan', 'hydra', + 'siphonophore', 'nanomia', 'portuguese man-of-war', 'praya', 'apolemia', + 'anthozoan', 'sea anemone', 'actinia', 'sea pen', 'coral', 'gorgonian', + 'sea feather', 'sea fan', 'red coral', 'stony coral', 'brain coral', + 'staghorn coral', 'mushroom coral', 'ctenophore', 'beroe', 'platyctenean', + 'sea gooseberry', 'venus\'s girdle', 'worm', 'helminth', 'woodworm', + 'woodborer', 'acanthocephalan', 'arrowworm', 'bladder worm', 'flatworm', + 'planarian', 'fluke', 'cercaria', 'liver fluke', 'fasciolopsis buski', + 'schistosome', 'tapeworm', 'echinococcus', 'taenia', 'ribbon worm', + 'beard worm', 'rotifer', 'nematode', 'common roundworm', + 'chicken roundworm', 'pinworm', 'eelworm', 'vinegar eel', 'trichina', + 'hookworm', 'filaria', 'guinea worm', 'annelid', 'archiannelid', + 'oligochaete', 'earthworm', 'polychaete', 'lugworm', 'sea mouse', + 'bloodworm', 'leech', 'medicinal leech', 'horseleech', 'mollusk', + 'scaphopod', 'tooth shell', 'gastropod', 'abalone', 'ormer', + 'scorpion shell', 'conch', 'giant conch', 'snail', 'edible snail', + 'garden snail', 'brown snail', 'helix hortensis', 'slug', 'seasnail', + 'neritid', 'nerita', 'bleeding tooth', 'neritina', 'whelk', 'moon shell', + 'periwinkle', 'limpet', 'common limpet', 'keyhole limpet', 'river limpet', + 'sea slug', 'sea hare', 'hermissenda crassicornis', 'bubble shell', 'physa', + 'cowrie', 'money cowrie', 'tiger cowrie', 'solenogaster', 'chiton', + 'bivalve', 'spat', 'clam', 'seashell', 'soft-shell clam', 'quahog', + 'littleneck', 'cherrystone', 'geoduck', 'razor clam', 'giant clam', + 'cockle', 'edible cockle', 'oyster', 'japanese oyster', 'virginia oyster', + 'pearl oyster', 'saddle oyster', 'window oyster', 'ark shell', 'blood clam', + 'mussel', 'marine mussel', 'edible mussel', 'freshwater mussel', + 'pearly-shelled mussel', 'thin-shelled mussel', 'zebra mussel', 'scallop', + 'bay scallop', 'sea scallop', 'shipworm', 'teredo', 'piddock', 'cephalopod', + 'chambered nautilus', 'octopod', 'octopus', 'paper nautilus', 'decapod', + 'squid', 'loligo', 'ommastrephes', 'architeuthis', 'cuttlefish', 'spirula', + 'crustacean', 'malacostracan crustacean', 'decapod crustacean', + 'brachyuran', 'crab', 'stone crab', 'hard-shell crab', 'soft-shell crab', + 'dungeness crab', 'rock crab', 'jonah crab', 'swimming crab', + 'english lady crab', 'american lady crab', 'blue crab', 'fiddler crab', + 'pea crab', 'king crab', 'spider crab', 'european spider crab', + 'giant crab', 'lobster', 'true lobster', 'american lobster', + 'european lobster', 'cape lobster', 'norway lobster', 'spiny lobster', + 'crayfish', 'old world crayfish', 'american crayfish', 'hermit crab', + 'shrimp', 'snapping shrimp', 'prawn', 'long-clawed prawn', 'tropical prawn', + 'krill', 'euphausia pacifica', 'opossum shrimp', 'stomatopod', + 'mantis shrimp', 'squilla', 'isopod', 'woodlouse', 'pill bug', 'sow bug', + 'sea louse', 'amphipod', 'skeleton shrimp', 'whale louse', 'daphnia', + 'fairy shrimp', 'brine shrimp', 'tadpole shrimp', 'copepod', 'cyclops', + 'seed shrimp', 'barnacle', 'acorn barnacle', 'goose barnacle', + 'onychophoran', 'wading bird', 'stork', 'white stork', 'black stork', + 'adjutant bird', 'marabou', 'openbill', 'jabiru', 'saddlebill', + 'policeman bird', 'wood ibis', 'shoebill', 'ibis', 'wood ibis', + 'sacred ibis', 'spoonbill', 'common spoonbill', 'roseate spoonbill', + 'flamingo', 'heron', 'great blue heron', 'great white heron', 'egret', + 'little blue heron', 'snowy egret', 'little egret', 'great white heron', + 'american egret', 'cattle egret', 'night heron', + 'black-crowned night heron', 'yellow-crowned night heron', 'boatbill', + 'bittern', 'american bittern', 'european bittern', 'least bittern', 'crane', + 'whooping crane', 'courlan', 'limpkin', 'crested cariama', 'chunga', 'rail', + 'weka', 'crake', 'corncrake', 'spotted crake', 'gallinule', + 'florida gallinule', 'moorhen', 'purple gallinule', 'european gallinule', + 'american gallinule', 'notornis', 'coot', 'american coot', 'old world coot', + 'bustard', 'great bustard', 'plain turkey', 'button quail', + 'striped button quail', 'plain wanderer', 'trumpeter', + 'brazilian trumpeter', 'seabird', 'shorebird', 'plover', 'piping plover', + 'killdeer', 'dotterel', 'golden plover', 'lapwing', 'turnstone', + 'ruddy turnstone', 'black turnstone', 'sandpiper', 'surfbird', + 'european sandpiper', 'spotted sandpiper', 'least sandpiper', + 'red-backed sandpiper', 'greenshank', 'redshank', 'yellowlegs', + 'greater yellowlegs', 'lesser yellowlegs', 'pectoral sandpiper', 'knot', + 'curlew sandpiper', 'sanderling', 'upland sandpiper', 'ruff', 'reeve', + 'tattler', 'polynesian tattler', 'willet', 'woodcock', 'eurasian woodcock', + 'american woodcock', 'snipe', 'whole snipe', 'wilson\'s snipe', + 'great snipe', 'jacksnipe', 'dowitcher', 'greyback', 'red-breasted snipe', + 'curlew', 'european curlew', 'eskimo curlew', 'godwit', 'hudsonian godwit', + 'stilt', 'black-necked stilt', 'black-winged stilt', 'white-headed stilt', + 'kaki', 'stilt', 'banded stilt', 'avocet', 'oystercatcher', 'phalarope', + 'red phalarope', 'northern phalarope', 'wilson\'s phalarope', 'pratincole', + 'courser', 'cream-colored courser', 'crocodile bird', 'stone curlew', + 'coastal diving bird', 'larid', 'gull', 'mew', 'black-backed gull', + 'herring gull', 'laughing gull', 'ivory gull', 'kittiwake', 'tern', + 'sea swallow', 'skimmer', 'jaeger', 'parasitic jaeger', 'skua', + 'great skua', 'auk', 'auklet', 'razorbill', 'little auk', 'guillemot', + 'black guillemot', 'pigeon guillemot', 'murre', 'common murre', + 'thick-billed murre', 'puffin', 'atlantic puffin', 'horned puffin', + 'tufted puffin', 'gaviiform seabird', 'loon', 'podicipitiform seabird', + 'grebe', 'great crested grebe', 'red-necked grebe', 'black-necked grebe', + 'dabchick', 'pied-billed grebe', 'pelecaniform seabird', 'pelican', + 'white pelican', 'old world white pelican', 'frigate bird', 'gannet', + 'solan', 'booby', 'cormorant', 'snakebird', 'water turkey', 'tropic bird', + 'sphenisciform seabird', 'penguin', 'adelie', 'king penguin', + 'emperor penguin', 'jackass penguin', 'rock hopper', 'pelagic bird', + 'procellariiform seabird', 'albatross', 'wandering albatross', + 'black-footed albatross', 'petrel', 'white-chinned petrel', 'giant petrel', + 'fulmar', 'shearwater', 'manx shearwater', 'storm petrel', 'stormy petrel', + 'mother carey\'s chicken', 'diving petrel', 'aquatic mammal', 'cetacean', + 'whale', 'baleen whale', 'right whale', 'bowhead', 'rorqual', 'blue whale', + 'finback', 'sei whale', 'lesser rorqual', 'humpback', 'grey whale', + 'toothed whale', 'sperm whale', 'pygmy sperm whale', 'dwarf sperm whale', + 'beaked whale', 'bottle-nosed whale', 'dolphin', 'common dolphin', + 'bottlenose dolphin', 'atlantic bottlenose dolphin', + 'pacific bottlenose dolphin', 'porpoise', 'harbor porpoise', 'vaquita', + 'grampus', 'killer whale', 'pilot whale', 'river dolphin', 'narwhal', + 'white whale', 'sea cow', 'manatee', 'dugong', 'steller\'s sea cow', + 'carnivore', 'omnivore', 'pinniped mammal', 'seal', 'crabeater seal', + 'eared seal', 'fur seal', 'guadalupe fur seal', 'fur seal', + 'alaska fur seal', 'sea lion', 'south american sea lion', + 'california sea lion', 'australian sea lion', 'steller sea lion', + 'earless seal', 'harbor seal', 'harp seal', 'elephant seal', 'bearded seal', + 'hooded seal', 'walrus', 'atlantic walrus', 'pacific walrus', 'fissipedia', + 'fissiped mammal', 'aardvark', 'canine', 'bitch', 'brood bitch', 'dog', + 'pooch', 'cur', 'feist', 'pariah dog', 'lapdog', 'toy dog', 'chihuahua', + 'japanese spaniel', 'maltese dog', 'pekinese', 'shih-tzu', 'toy spaniel', + 'english toy spaniel', 'blenheim spaniel', 'king charles spaniel', + 'papillon', 'toy terrier', 'hunting dog', 'courser', 'rhodesian ridgeback', + 'hound', 'afghan hound', 'basset', 'beagle', 'bloodhound', 'bluetick', + 'boarhound', 'coonhound', 'coondog', 'black-and-tan coonhound', 'dachshund', + 'sausage dog', 'foxhound', 'american foxhound', 'walker hound', + 'english foxhound', 'harrier', 'plott hound', 'redbone', 'wolfhound', + 'borzoi', 'irish wolfhound', 'greyhound', 'italian greyhound', 'whippet', + 'ibizan hound', 'norwegian elkhound', 'otterhound', 'saluki', + 'scottish deerhound', 'staghound', 'weimaraner', 'terrier', 'bullterrier', + 'staffordshire bullterrier', 'american staffordshire terrier', + 'bedlington terrier', 'border terrier', 'kerry blue terrier', + 'irish terrier', 'norfolk terrier', 'norwich terrier', 'yorkshire terrier', + 'rat terrier', 'manchester terrier', 'toy manchester', 'fox terrier', + 'smooth-haired fox terrier', 'wire-haired fox terrier', 'wirehair', + 'lakeland terrier', 'welsh terrier', 'sealyham terrier', 'airedale', + 'cairn', 'australian terrier', 'dandie dinmont', 'boston bull', 'schnauzer', + 'miniature schnauzer', 'giant schnauzer', 'standard schnauzer', + 'scotch terrier', 'tibetan terrier', 'silky terrier', 'skye terrier', + 'clydesdale terrier', 'soft-coated wheaten terrier', + 'west highland white terrier', 'lhasa', 'sporting dog', 'bird dog', + 'water dog', 'retriever', 'flat-coated retriever', 'curly-coated retriever', + 'golden retriever', 'labrador retriever', 'chesapeake bay retriever', + 'pointer', 'german short-haired pointer', 'setter', 'vizsla', + 'english setter', 'irish setter', 'gordon setter', 'spaniel', + 'brittany spaniel', 'clumber', 'field spaniel', 'springer spaniel', + 'english springer', 'welsh springer spaniel', 'cocker spaniel', + 'sussex spaniel', 'water spaniel', 'american water spaniel', + 'irish water spaniel', 'griffon', 'working dog', 'watchdog', 'kuvasz', + 'attack dog', 'housedog', 'schipperke', 'shepherd dog', 'belgian sheepdog', + 'groenendael', 'malinois', 'briard', 'kelpie', 'komondor', + 'old english sheepdog', 'shetland sheepdog', 'collie', 'border collie', + 'bouvier des flandres', 'rottweiler', 'german shepherd', 'police dog', + 'pinscher', 'doberman', 'miniature pinscher', 'sennenhunde', + 'greater swiss mountain dog', 'bernese mountain dog', 'appenzeller', + 'entlebucher', 'boxer', 'mastiff', 'bull mastiff', 'tibetan mastiff', + 'bulldog', 'french bulldog', 'great dane', 'guide dog', 'seeing eye dog', + 'hearing dog', 'saint bernard', 'seizure-alert dog', 'sled dog', + 'eskimo dog', 'malamute', 'siberian husky', 'dalmatian', + 'liver-spotted dalmatian', 'affenpinscher', 'basenji', 'pug', 'leonberg', + 'newfoundland', 'great pyrenees', 'spitz', 'samoyed', 'pomeranian', 'chow', + 'keeshond', 'griffon', 'brabancon griffon', 'corgi', 'pembroke', 'cardigan', + 'poodle', 'toy poodle', 'miniature poodle', 'standard poodle', + 'large poodle', 'mexican hairless', 'wolf', 'timber wolf', 'white wolf', + 'red wolf', 'coyote', 'coydog', 'jackal', 'wild dog', 'dingo', 'dhole', + 'crab-eating dog', 'raccoon dog', 'african hunting dog', 'hyena', + 'striped hyena', 'brown hyena', 'spotted hyena', 'aardwolf', 'fox', 'vixen', + 'reynard', 'red fox', 'black fox', 'silver fox', 'red fox', 'kit fox', + 'kit fox', 'arctic fox', 'blue fox', 'grey fox', 'feline', 'cat', + 'domestic cat', 'kitty', 'mouser', 'alley cat', 'stray', 'tom', 'gib', + 'tabby', 'kitten', 'tabby', 'tiger cat', 'tortoiseshell', 'persian cat', + 'angora', 'siamese cat', 'blue point siamese', 'burmese cat', + 'egyptian cat', 'maltese', 'abyssinian', 'manx', 'wildcat', 'sand cat', + 'european wildcat', 'cougar', 'ocelot', 'jaguarundi', 'kaffir cat', + 'jungle cat', 'serval', 'leopard cat', 'margay', 'manul', 'lynx', + 'common lynx', 'canada lynx', 'bobcat', 'spotted lynx', 'caracal', + 'big cat', 'leopard', 'leopardess', 'panther', 'snow leopard', 'jaguar', + 'lion', 'lioness', 'lionet', 'tiger', 'bengal tiger', 'tigress', 'liger', + 'tiglon', 'cheetah', 'saber-toothed tiger', 'smiledon californicus', 'bear', + 'brown bear', 'bruin', 'syrian bear', 'grizzly', 'alaskan brown bear', + 'american black bear', 'cinnamon bear', 'asiatic black bear', 'ice bear', + 'sloth bear', 'viverrine', 'civet', 'large civet', 'small civet', + 'binturong', 'cryptoprocta', 'fossa', 'fanaloka', 'genet', + 'banded palm civet', 'mongoose', 'indian mongoose', 'ichneumon', 'palm cat', + 'meerkat', 'slender-tailed meerkat', 'suricate', 'bat', 'fruit bat', + 'flying fox', 'pteropus capestratus', 'pteropus hypomelanus', 'harpy', + 'cynopterus sphinx', 'carnivorous bat', 'mouse-eared bat', 'leafnose bat', + 'macrotus', 'spearnose bat', 'phyllostomus hastatus', 'hognose bat', + 'horseshoe bat', 'horseshoe bat', 'orange bat', 'false vampire', + 'big-eared bat', 'vespertilian bat', 'frosted bat', 'red bat', 'brown bat', + 'little brown bat', 'cave myotis', 'big brown bat', 'serotine', + 'pallid bat', 'pipistrelle', 'eastern pipistrel', 'jackass bat', + 'long-eared bat', 'western big-eared bat', 'freetail', 'guano bat', + 'pocketed bat', 'mastiff bat', 'vampire bat', 'desmodus rotundus', + 'hairy-legged vampire bat', 'predator', 'prey', 'game', 'big game', + 'game bird', 'fossorial mammal', 'tetrapod', 'quadruped', 'hexapod', + 'biped', 'insect', 'social insect', 'holometabola', 'defoliator', + 'pollinator', 'gallfly', 'scorpion fly', 'hanging fly', 'collembolan', + 'beetle', 'tiger beetle', 'ladybug', 'two-spotted ladybug', + 'mexican bean beetle', 'hippodamia convergens', 'vedalia', 'ground beetle', + 'bombardier beetle', 'calosoma', 'searcher', 'firefly', 'glowworm', + 'long-horned beetle', 'sawyer', 'pine sawyer', 'leaf beetle', 'flea beetle', + 'colorado potato beetle', 'carpet beetle', 'buffalo carpet beetle', + 'black carpet beetle', 'clerid beetle', 'bee beetle', 'lamellicorn beetle', + 'scarabaeid beetle', 'dung beetle', 'scarab', 'tumblebug', 'dorbeetle', + 'june beetle', 'green june beetle', 'japanese beetle', 'oriental beetle', + 'rhinoceros beetle', 'melolonthid beetle', 'cockchafer', 'rose chafer', + 'rose chafer', 'stag beetle', 'elaterid beetle', 'click beetle', 'firefly', + 'wireworm', 'water beetle', 'whirligig beetle', 'deathwatch beetle', + 'weevil', 'snout beetle', 'boll weevil', 'blister beetle', 'oil beetle', + 'spanish fly', 'dutch-elm beetle', 'bark beetle', 'spruce bark beetle', + 'rove beetle', 'darkling beetle', 'mealworm', 'flour beetle', 'seed beetle', + 'pea weevil', 'bean weevil', 'rice weevil', 'asian longhorned beetle', + 'web spinner', 'louse', 'common louse', 'head louse', 'body louse', + 'crab louse', 'bird louse', 'flea', 'pulex irritans', 'dog flea', + 'cat flea', 'chigoe', 'sticktight', 'dipterous insect', 'gall midge', + 'hessian fly', 'fly', 'housefly', 'tsetse fly', 'blowfly', 'bluebottle', + 'greenbottle', 'flesh fly', 'tachina fly', 'gadfly', 'botfly', + 'human botfly', 'sheep botfly', 'warble fly', 'horsefly', 'bee fly', + 'robber fly', 'fruit fly', 'apple maggot', 'mediterranean fruit fly', + 'drosophila', 'vinegar fly', 'leaf miner', 'louse fly', 'horse tick', + 'sheep ked', 'horn fly', 'mosquito', 'wiggler', 'gnat', + 'yellow-fever mosquito', 'asian tiger mosquito', 'anopheline', + 'malarial mosquito', 'common mosquito', 'culex quinquefasciatus', 'gnat', + 'punkie', 'midge', 'fungus gnat', 'psychodid', 'sand fly', 'fungus gnat', + 'armyworm', 'crane fly', 'blackfly', 'hymenopterous insect', 'bee', 'drone', + 'queen bee', 'worker', 'soldier', 'worker bee', 'honeybee', + 'africanized bee', 'black bee', 'carniolan bee', 'italian bee', + 'carpenter bee', 'bumblebee', 'cuckoo-bumblebee', 'andrena', + 'nomia melanderi', 'leaf-cutting bee', 'mason bee', 'potter bee', 'wasp', + 'vespid', 'paper wasp', 'hornet', 'giant hornet', 'common wasp', + 'bald-faced hornet', 'yellow jacket', 'polistes annularis', 'mason wasp', + 'potter wasp', 'mutillidae', 'velvet ant', 'sphecoid wasp', 'mason wasp', + 'digger wasp', 'cicada killer', 'mud dauber', 'gall wasp', 'chalcid fly', + 'strawworm', 'chalcis fly', 'ichneumon fly', 'sawfly', 'birch leaf miner', + 'ant', 'pharaoh ant', 'little black ant', 'army ant', 'carpenter ant', + 'fire ant', 'wood ant', 'slave ant', 'formica fusca', 'slave-making ant', + 'sanguinary ant', 'bulldog ant', 'amazon ant', 'termite', + 'dry-wood termite', 'reticulitermes lucifugus', 'mastotermes darwiniensis', + 'mastotermes electrodominicus', 'powder-post termite', + 'orthopterous insect', 'grasshopper', 'short-horned grasshopper', 'locust', + 'migratory locust', 'migratory grasshopper', 'long-horned grasshopper', + 'katydid', 'mormon cricket', 'sand cricket', 'cricket', 'mole cricket', + 'european house cricket', 'field cricket', 'tree cricket', + 'snowy tree cricket', 'phasmid', 'walking stick', 'diapheromera', + 'walking leaf', 'cockroach', 'oriental cockroach', 'american cockroach', + 'australian cockroach', 'german cockroach', 'giant cockroach', 'mantis', + 'praying mantis', 'bug', 'hemipterous insect', 'leaf bug', 'mirid bug', + 'four-lined plant bug', 'lygus bug', 'tarnished plant bug', 'lace bug', + 'lygaeid', 'chinch bug', 'coreid bug', 'squash bug', 'leaf-footed bug', + 'bedbug', 'backswimmer', 'true bug', 'heteropterous insect', 'water bug', + 'giant water bug', 'water scorpion', 'water boatman', 'water strider', + 'common pond-skater', 'assassin bug', 'conenose', 'wheel bug', 'firebug', + 'cotton stainer', 'homopterous insect', 'whitefly', 'citrus whitefly', + 'greenhouse whitefly', 'sweet-potato whitefly', 'superbug', 'cotton strain', + 'coccid insect', 'scale insect', 'soft scale', 'brown soft scale', + 'armored scale', 'san jose scale', 'cochineal insect', 'mealybug', + 'citrophilous mealybug', 'comstock mealybug', 'citrus mealybug', + 'plant louse', 'aphid', 'apple aphid', 'blackfly', 'greenfly', + 'green peach aphid', 'ant cow', 'woolly aphid', 'woolly apple aphid', + 'woolly alder aphid', 'adelgid', 'balsam woolly aphid', 'spruce gall aphid', + 'woolly adelgid', 'jumping plant louse', 'cicada', 'dog-day cicada', + 'seventeen-year locust', 'spittle insect', 'froghopper', + 'meadow spittlebug', 'pine spittlebug', 'saratoga spittlebug', 'leafhopper', + 'plant hopper', 'treehopper', 'lantern fly', 'psocopterous insect', + 'psocid', 'bark-louse', 'booklouse', 'common booklouse', 'ephemerid', + 'mayfly', 'stonefly', 'neuropteron', 'ant lion', 'doodlebug', 'lacewing', + 'aphid lion', 'green lacewing', 'brown lacewing', 'dobson', 'hellgrammiate', + 'fish fly', 'alderfly', 'snakefly', 'mantispid', 'odonate', 'dragonfly', + 'damselfly', 'trichopterous insect', 'caddis fly', 'caseworm', 'caddisworm', + 'thysanuran insect', 'bristletail', 'silverfish', 'firebrat', + 'jumping bristletail', 'thysanopter', 'thrips', 'tobacco thrips', + 'onion thrips', 'earwig', 'common european earwig', 'lepidopterous insect', + 'butterfly', 'nymphalid', 'mourning cloak', 'tortoiseshell', + 'painted beauty', 'admiral', 'red admiral', 'white admiral', + 'banded purple', 'red-spotted purple', 'viceroy', 'anglewing', 'ringlet', + 'comma', 'fritillary', 'silverspot', 'emperor butterfly', 'purple emperor', + 'peacock', 'danaid', 'monarch', 'pierid', 'cabbage butterfly', + 'small white', 'large white', 'southern cabbage butterfly', + 'sulphur butterfly', 'lycaenid', 'blue', 'copper', 'american copper', + 'hairstreak', 'strymon melinus', 'moth', 'moth miller', 'tortricid', + 'leaf roller', 'tea tortrix', 'orange tortrix', 'codling moth', + 'lymantriid', 'tussock caterpillar', 'gypsy moth', 'browntail', + 'gold-tail moth', 'geometrid', 'paleacrita vernata', 'alsophila pometaria', + 'cankerworm', 'spring cankerworm', 'fall cankerworm', 'measuring worm', + 'pyralid', 'bee moth', 'corn borer', 'mediterranean flour moth', + 'tobacco moth', 'almond moth', 'raisin moth', 'tineoid', 'tineid', + 'clothes moth', 'casemaking clothes moth', 'webbing clothes moth', + 'carpet moth', 'gelechiid', 'grain moth', 'angoumois moth', 'potato moth', + 'potato tuberworm', 'noctuid moth', 'cutworm', 'underwing', 'red underwing', + 'antler moth', 'heliothis moth', 'army cutworm', 'armyworm', 'armyworm', + 'spodoptera exigua', 'beet armyworm', 'spodoptera frugiperda', + 'fall armyworm', 'hawkmoth', 'manduca sexta', 'tobacco hornworm', + 'manduca quinquemaculata', 'tomato hornworm', 'death\'s-head moth', + 'bombycid', 'domestic silkworm moth', 'silkworm', 'saturniid', 'emperor', + 'imperial moth', 'giant silkworm moth', 'silkworm', 'luna moth', 'cecropia', + 'cynthia moth', 'ailanthus silkworm', 'io moth', 'polyphemus moth', + 'pernyi moth', 'tussah', 'atlas moth', 'arctiid', 'tiger moth', 'cinnabar', + 'lasiocampid', 'eggar', 'tent-caterpillar moth', 'tent caterpillar', + 'tent-caterpillar moth', 'forest tent caterpillar', 'lappet', + 'lappet caterpillar', 'webworm', 'webworm moth', 'hyphantria cunea', + 'fall webworm', 'garden webworm', 'instar', 'caterpillar', 'corn borer', + 'bollworm', 'pink bollworm', 'corn earworm', 'cabbageworm', 'woolly bear', + 'woolly bear moth', 'larva', 'nymph', 'leptocephalus', 'grub', 'maggot', + 'leatherjacket', 'pupa', 'chrysalis', 'imago', 'queen', 'phoronid', + 'bryozoan', 'brachiopod', 'peanut worm', 'echinoderm', 'starfish', + 'brittle star', 'basket star', 'astrophyton muricatum', 'sea urchin', + 'edible sea urchin', 'sand dollar', 'heart urchin', 'crinoid', 'sea lily', + 'feather star', 'sea cucumber', 'trepang', 'duplicidentata', 'lagomorph', + 'leporid', 'rabbit', 'rabbit ears', 'lapin', 'bunny', 'european rabbit', + 'wood rabbit', 'eastern cottontail', 'swamp rabbit', 'marsh hare', 'hare', + 'leveret', 'european hare', 'jackrabbit', 'white-tailed jackrabbit', + 'blacktail jackrabbit', 'polar hare', 'snowshoe hare', 'belgian hare', + 'angora', 'pika', 'little chief hare', 'collared pika', 'rodent', 'mouse', + 'rat', 'pocket rat', 'murine', 'house mouse', 'harvest mouse', + 'field mouse', 'nude mouse', 'european wood mouse', 'brown rat', + 'wharf rat', 'sewer rat', 'black rat', 'bandicoot rat', 'jerboa rat', + 'kangaroo mouse', 'water rat', 'beaver rat', 'new world mouse', + 'american harvest mouse', 'wood mouse', 'white-footed mouse', 'deer mouse', + 'cactus mouse', 'cotton mouse', 'pygmy mouse', 'grasshopper mouse', + 'muskrat', 'round-tailed muskrat', 'cotton rat', 'wood rat', + 'dusky-footed wood rat', 'vole', 'packrat', 'dusky-footed woodrat', + 'eastern woodrat', 'rice rat', 'pine vole', 'meadow vole', 'water vole', + 'prairie vole', 'water vole', 'red-backed mouse', 'phenacomys', 'hamster', + 'eurasian hamster', 'golden hamster', 'gerbil', 'jird', 'tamarisk gerbil', + 'sand rat', 'lemming', 'european lemming', 'brown lemming', 'grey lemming', + 'pied lemming', 'hudson bay collared lemming', 'southern bog lemming', + 'northern bog lemming', 'porcupine', 'old world porcupine', + 'brush-tailed porcupine', 'long-tailed porcupine', 'new world porcupine', + 'canada porcupine', 'pocket mouse', 'silky pocket mouse', + 'plains pocket mouse', 'hispid pocket mouse', 'mexican pocket mouse', + 'kangaroo rat', 'ord kangaroo rat', 'kangaroo mouse', 'jumping mouse', + 'meadow jumping mouse', 'jerboa', 'typical jerboa', 'jaculus jaculus', + 'dormouse', 'loir', 'hazel mouse', 'lerot', 'gopher', + 'plains pocket gopher', 'southeastern pocket gopher', + 'valley pocket gopher', 'northern pocket gopher', 'squirrel', + 'tree squirrel', 'eastern grey squirrel', 'western grey squirrel', + 'fox squirrel', 'black squirrel', 'red squirrel', 'american red squirrel', + 'chickeree', 'antelope squirrel', 'ground squirrel', + 'mantled ground squirrel', 'suslik', 'flickertail', 'rock squirrel', + 'arctic ground squirrel', 'prairie dog', 'blacktail prairie dog', + 'whitetail prairie dog', 'eastern chipmunk', 'chipmunk', 'baronduki', + 'american flying squirrel', 'southern flying squirrel', + 'northern flying squirrel', 'marmot', 'groundhog', 'hoary marmot', + 'yellowbelly marmot', 'asiatic flying squirrel', 'beaver', + 'old world beaver', 'new world beaver', 'mountain beaver', 'cavy', + 'guinea pig', 'aperea', 'mara', 'capybara', 'agouti', 'paca', + 'mountain paca', 'coypu', 'chinchilla', 'mountain chinchilla', 'viscacha', + 'abrocome', 'mole rat', 'mole rat', 'sand rat', 'naked mole rat', 'queen', + 'damaraland mole rat', 'ungulata', 'ungulate', 'unguiculate', 'dinoceras', + 'hyrax', 'rock hyrax', 'odd-toed ungulate', 'equine', 'horse', 'roan', + 'stablemate', 'gee-gee', 'eohippus', 'foal', 'filly', 'colt', 'male horse', + 'ridgeling', 'stallion', 'stud', 'gelding', 'mare', 'broodmare', + 'saddle horse', 'remount', 'palfrey', 'warhorse', 'cavalry horse', + 'charger', 'steed', 'prancer', 'hack', 'cow pony', 'quarter horse', + 'morgan', 'tennessee walker', 'american saddle horse', 'appaloosa', + 'arabian', 'lippizan', 'pony', 'polo pony', 'mustang', 'bronco', + 'bucking bronco', 'buckskin', 'crowbait', 'dun', 'grey', 'wild horse', + 'tarpan', 'przewalski\'s horse', 'cayuse', 'hack', 'hack', 'plow horse', + 'pony', 'shetland pony', 'welsh pony', 'exmoor', 'racehorse', + 'thoroughbred', 'steeplechaser', 'racer', 'finisher', 'pony', 'yearling', + 'dark horse', 'mudder', 'nonstarter', 'stalking-horse', 'harness horse', + 'cob', 'hackney', 'workhorse', 'draft horse', 'packhorse', 'carthorse', + 'clydesdale', 'percheron', 'farm horse', 'shire', 'pole horse', + 'post horse', 'coach horse', 'pacer', 'pacer', 'trotting horse', + 'pole horse', 'stepper', 'chestnut', 'liver chestnut', 'bay', 'sorrel', + 'palomino', 'pinto', 'ass', 'domestic ass', 'burro', 'moke', 'jack', + 'jennet', 'mule', 'hinny', 'wild ass', 'african wild ass', 'kiang', + 'onager', 'chigetai', 'zebra', 'common zebra', 'mountain zebra', + 'grevy\'s zebra', 'quagga', 'rhinoceros', 'indian rhinoceros', + 'woolly rhinoceros', 'white rhinoceros', 'black rhinoceros', 'tapir', + 'new world tapir', 'malayan tapir', 'even-toed ungulate', 'swine', 'hog', + 'piglet', 'sucking pig', 'porker', 'boar', 'sow', 'razorback', 'wild boar', + 'babirusa', 'warthog', 'peccary', 'collared peccary', + 'white-lipped peccary', 'hippopotamus', 'ruminant', 'bovid', 'bovine', 'ox', + 'cattle', 'ox', 'stirk', 'bullock', 'bull', 'cow', 'heifer', 'bullock', + 'dogie', 'maverick', 'beef', 'longhorn', 'brahman', 'zebu', 'aurochs', + 'yak', 'banteng', 'welsh', 'red poll', 'santa gertrudis', 'aberdeen angus', + 'africander', 'dairy cattle', 'ayrshire', 'brown swiss', 'charolais', + 'jersey', 'devon', 'grade', 'durham', 'milking shorthorn', 'galloway', + 'friesian', 'guernsey', 'hereford', 'cattalo', 'old world buffalo', + 'water buffalo', 'indian buffalo', 'carabao', 'anoa', 'tamarau', + 'cape buffalo', 'asian wild ox', 'gaur', 'gayal', 'bison', 'american bison', + 'wisent', 'musk ox', 'sheep', 'ewe', 'ram', 'wether', 'lamb', 'lambkin', + 'baa-lamb', 'hog', 'teg', 'persian lamb', 'black sheep', 'domestic sheep', + 'cotswold', 'hampshire', 'lincoln', 'exmoor', 'cheviot', 'broadtail', + 'longwool', 'merino', 'rambouillet', 'wild sheep', 'argali', + 'marco polo sheep', 'urial', 'dall sheep', 'mountain sheep', 'bighorn', + 'mouflon', 'aoudad', 'goat', 'kid', 'billy', 'nanny', 'domestic goat', + 'cashmere goat', 'angora', 'wild goat', 'bezoar goat', 'markhor', 'ibex', + 'goat antelope', 'mountain goat', 'goral', 'serow', 'chamois', 'takin', + 'antelope', 'blackbuck', 'gerenuk', 'addax', 'gnu', 'dik-dik', 'hartebeest', + 'sassaby', 'impala', 'gazelle', 'thomson\'s gazelle', + 'gazella subgutturosa', 'springbok', 'bongo', 'kudu', 'greater kudu', + 'lesser kudu', 'harnessed antelope', 'nyala', 'mountain nyala', 'bushbuck', + 'nilgai', 'sable antelope', 'saiga', 'steenbok', 'eland', 'common eland', + 'giant eland', 'kob', 'lechwe', 'waterbuck', 'puku', 'oryx', 'gemsbok', + 'forest goat', 'pronghorn', 'deer', 'stag', 'royal', 'pricket', 'fawn', + 'red deer', 'hart', 'hind', 'brocket', 'sambar', 'wapiti', 'japanese deer', + 'virginia deer', 'mule deer', 'black-tailed deer', 'elk', 'fallow deer', + 'roe deer', 'roebuck', 'caribou', 'woodland caribou', + 'barren ground caribou', 'brocket', 'muntjac', 'musk deer', + 'pere david\'s deer', 'chevrotain', 'kanchil', 'napu', 'water chevrotain', + 'camel', 'arabian camel', 'bactrian camel', 'llama', 'domestic llama', + 'guanaco', 'alpaca', 'vicuna', 'giraffe', 'okapi', 'musteline mammal', + 'weasel', 'ermine', 'stoat', 'new world least weasel', + 'old world least weasel', 'longtail weasel', 'mink', 'american mink', + 'polecat', 'ferret', 'black-footed ferret', 'muishond', 'snake muishond', + 'striped muishond', 'otter', 'river otter', 'eurasian otter', 'sea otter', + 'skunk', 'striped skunk', 'hooded skunk', 'hog-nosed skunk', + 'spotted skunk', 'badger', 'american badger', 'eurasian badger', 'ratel', + 'ferret badger', 'hog badger', 'wolverine', 'glutton', 'grison', 'marten', + 'pine marten', 'sable', 'american marten', 'stone marten', 'fisher', + 'yellow-throated marten', 'tayra', 'fictional animal', 'pachyderm', + 'edentate', 'armadillo', 'peba', 'apar', 'tatouay', 'peludo', + 'giant armadillo', 'pichiciago', 'sloth', 'three-toed sloth', + 'two-toed sloth', 'two-toed sloth', 'megatherian', 'mylodontid', 'anteater', + 'ant bear', 'silky anteater', 'tamandua', 'pangolin', 'coronet', 'scapular', + 'tadpole', 'primate', 'simian', 'ape', 'anthropoid', 'anthropoid ape', + 'hominoid', 'hominid', 'homo', 'world', 'homo erectus', 'pithecanthropus', + 'java man', 'peking man', 'sinanthropus', 'homo soloensis', 'javanthropus', + 'homo habilis', 'homo sapiens', 'neandertal man', 'cro-magnon', + 'homo sapiens sapiens', 'australopithecine', 'australopithecus afarensis', + 'australopithecus africanus', 'australopithecus boisei', 'zinjanthropus', + 'australopithecus robustus', 'paranthropus', 'sivapithecus', 'rudapithecus', + 'proconsul', 'aegyptopithecus', 'great ape', 'orangutan', 'gorilla', + 'western lowland gorilla', 'eastern lowland gorilla', 'mountain gorilla', + 'silverback', 'chimpanzee', 'western chimpanzee', 'eastern chimpanzee', + 'central chimpanzee', 'pygmy chimpanzee', 'lesser ape', 'gibbon', 'siamang', + 'monkey', 'old world monkey', 'guenon', 'talapoin', 'grivet', 'vervet', + 'green monkey', 'mangabey', 'patas', 'baboon', 'chacma', 'mandrill', + 'drill', 'macaque', 'rhesus', 'bonnet macaque', 'barbary ape', + 'crab-eating macaque', 'langur', 'entellus', 'colobus', 'guereza', + 'proboscis monkey', 'new world monkey', 'marmoset', 'true marmoset', + 'pygmy marmoset', 'tamarin', 'silky tamarin', 'pinche', 'capuchin', + 'douroucouli', 'howler monkey', 'saki', 'uakari', 'titi', 'spider monkey', + 'squirrel monkey', 'woolly monkey', 'tree shrew', 'prosimian', 'lemur', + 'madagascar cat', 'aye-aye', 'slender loris', 'slow loris', 'potto', + 'angwantibo', 'galago', 'indri', 'woolly indris', 'tarsier', + 'tarsius syrichta', 'tarsius glis', 'flying lemur', + 'cynocephalus variegatus', 'proboscidean', 'elephant', 'rogue elephant', + 'indian elephant', 'african elephant', 'mammoth', 'woolly mammoth', + 'columbian mammoth', 'imperial mammoth', 'mastodon', 'plantigrade mammal', + 'digitigrade mammal', 'procyonid', 'raccoon', 'common raccoon', + 'crab-eating raccoon', 'bassarisk', 'kinkajou', 'coati', 'lesser panda', + 'giant panda', 'twitterer', 'fish', 'fingerling', 'game fish', 'food fish', + 'rough fish', 'groundfish', 'young fish', 'parr', 'mouthbreeder', 'spawner', + 'barracouta', 'crossopterygian', 'coelacanth', 'lungfish', 'ceratodus', + 'catfish', 'silurid', 'european catfish', 'electric catfish', 'bullhead', + 'horned pout', 'brown bullhead', 'channel catfish', 'blue catfish', + 'flathead catfish', 'armored catfish', 'sea catfish', 'gadoid', 'cod', + 'codling', 'atlantic cod', 'pacific cod', 'whiting', 'burbot', 'haddock', + 'pollack', 'hake', 'silver hake', 'ling', 'cusk', 'grenadier', 'eel', + 'elver', 'common eel', 'tuna', 'moray', 'conger', 'teleost fish', + 'beaked salmon', 'clupeid fish', 'whitebait', 'brit', 'shad', + 'common american shad', 'river shad', 'allice shad', 'alewife', 'menhaden', + 'herring', 'atlantic herring', 'pacific herring', 'sardine', 'sild', + 'brisling', 'pilchard', 'pacific sardine', 'anchovy', + 'mediterranean anchovy', 'salmonid', 'salmon', 'parr', 'blackfish', + 'redfish', 'atlantic salmon', 'landlocked salmon', 'sockeye', 'chinook', + 'coho', 'trout', 'brown trout', 'rainbow trout', 'sea trout', 'lake trout', + 'brook trout', 'char', 'arctic char', 'whitefish', 'lake whitefish', + 'cisco', 'round whitefish', 'smelt', 'sparling', 'capelin', 'tarpon', + 'ladyfish', 'bonefish', 'argentine', 'lanternfish', 'lizardfish', + 'lancetfish', 'opah', 'new world opah', 'ribbonfish', 'dealfish', 'oarfish', + 'batfish', 'goosefish', 'toadfish', 'oyster fish', 'frogfish', + 'sargassum fish', 'needlefish', 'timucu', 'flying fish', + 'monoplane flying fish', 'halfbeak', 'saury', 'spiny-finned fish', + 'lingcod', 'percoid fish', 'perch', 'climbing perch', 'perch', + 'yellow perch', 'european perch', 'pike-perch', 'walleye', 'blue pike', + 'snail darter', 'cusk-eel', 'brotula', 'pearlfish', 'robalo', 'snook', + 'pike', 'northern pike', 'muskellunge', 'pickerel', 'chain pickerel', + 'redfin pickerel', 'sunfish', 'crappie', 'black crappie', 'white crappie', + 'freshwater bream', 'pumpkinseed', 'bluegill', 'spotted sunfish', + 'freshwater bass', 'rock bass', 'black bass', 'kentucky black bass', + 'smallmouth', 'largemouth', 'bass', 'serranid fish', 'white perch', + 'yellow bass', 'blackmouth bass', 'rock sea bass', 'striped bass', + 'stone bass', 'grouper', 'hind', 'rock hind', 'creole-fish', 'jewfish', + 'soapfish', 'surfperch', 'rainbow seaperch', 'bigeye', 'catalufa', + 'cardinalfish', 'flame fish', 'tilefish', 'bluefish', 'cobia', 'remora', + 'sharksucker', 'whale sucker', 'carangid fish', 'jack', 'crevalle jack', + 'yellow jack', 'runner', 'rainbow runner', 'leatherjacket', 'threadfish', + 'moonfish', 'lookdown', 'amberjack', 'yellowtail', 'kingfish', 'pompano', + 'florida pompano', 'permit', 'scad', 'horse mackerel', 'horse mackerel', + 'bigeye scad', 'mackerel scad', 'round scad', 'dolphinfish', + 'coryphaena hippurus', 'coryphaena equisetis', 'pomfret', 'characin', + 'tetra', 'cardinal tetra', 'piranha', 'cichlid', 'bolti', 'snapper', + 'red snapper', 'grey snapper', 'mutton snapper', 'schoolmaster', + 'yellowtail', 'grunt', 'margate', 'spanish grunt', 'tomtate', 'cottonwick', + 'sailor\'s-choice', 'porkfish', 'pompon', 'pigfish', 'sparid', 'sea bream', + 'porgy', 'red porgy', 'european sea bream', 'atlantic sea bream', + 'sheepshead', 'pinfish', 'sheepshead porgy', 'snapper', 'black bream', + 'scup', 'scup', 'sciaenid fish', 'striped drum', 'jackknife-fish', + 'silver perch', 'red drum', 'mulloway', 'maigre', 'croaker', + 'atlantic croaker', 'yellowfin croaker', 'whiting', 'kingfish', + 'king whiting', 'northern whiting', 'corbina', 'white croaker', + 'white croaker', 'sea trout', 'weakfish', 'spotted weakfish', 'mullet', + 'goatfish', 'red goatfish', 'yellow goatfish', 'mullet', 'striped mullet', + 'white mullet', 'liza', 'silversides', 'jacksmelt', 'barracuda', + 'great barracuda', 'sweeper', 'sea chub', 'bermuda chub', 'spadefish', + 'butterfly fish', 'chaetodon', 'angelfish', 'rock beauty', 'damselfish', + 'beaugregory', 'anemone fish', 'clown anemone fish', 'sergeant major', + 'wrasse', 'pigfish', 'hogfish', 'slippery dick', 'puddingwife', 'bluehead', + 'pearly razorfish', 'tautog', 'cunner', 'parrotfish', 'threadfin', + 'jawfish', 'stargazer', 'sand stargazer', 'blenny', 'shanny', + 'molly miller', 'clinid', 'pikeblenny', 'bluethroat pikeblenny', 'gunnel', + 'rock gunnel', 'eelblenny', 'wrymouth', 'wolffish', 'viviparous eelpout', + 'ocean pout', 'sand lance', 'dragonet', 'goby', 'mudskipper', 'sleeper', + 'flathead', 'archerfish', 'surgeonfish', 'gempylid', 'snake mackerel', + 'escolar', 'oilfish', 'cutlassfish', 'scombroid', 'mackerel', + 'common mackerel', 'spanish mackerel', 'chub mackerel', 'wahoo', + 'spanish mackerel', 'king mackerel', 'scomberomorus maculatus', 'cero', + 'sierra', 'tuna', 'albacore', 'bluefin', 'yellowfin', 'bonito', 'skipjack', + 'chile bonito', 'skipjack', 'bonito', 'swordfish', 'sailfish', + 'atlantic sailfish', 'billfish', 'marlin', 'blue marlin', 'black marlin', + 'striped marlin', 'white marlin', 'spearfish', 'louvar', 'dollarfish', + 'palometa', 'harvestfish', 'driftfish', 'barrelfish', 'clingfish', + 'tripletail', 'atlantic tripletail', 'pacific tripletail', 'mojarra', + 'yellowfin mojarra', 'silver jenny', 'whiting', 'ganoid', 'bowfin', + 'paddlefish', 'chinese paddlefish', 'sturgeon', 'pacific sturgeon', + 'beluga', 'gar', 'scorpaenoid', 'scorpaenid', 'scorpionfish', + 'plumed scorpionfish', 'lionfish', 'stonefish', 'rockfish', + 'copper rockfish', 'vermillion rockfish', 'red rockfish', 'rosefish', + 'bullhead', 'miller\'s-thumb', 'sea raven', 'lumpfish', 'lumpsucker', + 'pogge', 'greenling', 'kelp greenling', 'painted greenling', 'flathead', + 'gurnard', 'tub gurnard', 'sea robin', 'northern sea robin', + 'flying gurnard', 'plectognath', 'triggerfish', 'queen triggerfish', + 'filefish', 'leatherjacket', 'boxfish', 'cowfish', 'puffer', 'spiny puffer', + 'porcupinefish', 'balloonfish', 'burrfish', 'ocean sunfish', + 'sharptail mola', 'flatfish', 'flounder', 'righteye flounder', 'plaice', + 'european flatfish', 'yellowtail flounder', 'winter flounder', 'lemon sole', + 'american plaice', 'halibut', 'atlantic halibut', 'pacific halibut', + 'lefteye flounder', 'southern flounder', 'summer flounder', 'whiff', + 'horned whiff', 'sand dab', 'windowpane', 'brill', 'turbot', 'tonguefish', + 'sole', 'european sole', 'english sole', 'hogchoker', 'aba', 'abacus', + 'abandoned ship', 'a battery', 'abattoir', 'abaya', 'abbe condenser', + 'abbey', 'abbey', 'abbey', 'abney level', 'abrader', 'abrading stone', + 'abutment', 'abutment arch', 'academic costume', 'academic gown', + 'accelerator', 'accelerator', 'accelerator', 'accelerometer', 'accessory', + 'accommodating lens implant', 'accommodation', 'accordion', 'acetate disk', + 'acetate rayon', 'achromatic lens', 'acoustic delay line', + 'acoustic device', 'acoustic guitar', 'acoustic modem', 'acropolis', + 'acrylic', 'acrylic', 'actinometer', 'action', 'active matrix screen', + 'actuator', 'adapter', 'adder', 'adding machine', 'addressing machine', + 'adhesive bandage', 'adit', 'adjoining room', 'adjustable wrench', 'adobe', + 'adz', 'aeolian harp', 'aerator', 'aerial torpedo', 'aerosol', 'aertex', + 'afghan', 'afro-wig', 'afterburner', 'after-shave', 'agateware', + 'agglomerator', 'aglet', 'aglet', 'agora', 'aigrette', 'aileron', 'air bag', + 'airbrake', 'airbrush', 'airbus', 'air compressor', 'air conditioner', + 'aircraft', 'aircraft carrier', 'aircraft engine', 'air cushion', 'airdock', + 'airfield', 'air filter', 'airfoil', 'airframe', 'air gun', 'air hammer', + 'air horn', 'airing cupboard', 'airliner', 'airmailer', 'airplane', + 'airplane propeller', 'airport', 'air pump', 'air search radar', 'airship', + 'air terminal', 'air-to-air missile', 'air-to-ground missile', 'aisle', + 'aladdin\'s lamp', 'alarm', 'alarm clock', 'alb', 'alcazar', + 'alcohol thermometer', 'alehouse', 'alembic', 'algometer', 'alidade', + 'alidade', 'a-line', 'allen screw', 'allen wrench', 'alligator wrench', + 'alms dish', 'alpaca', 'alpenstock', 'altar', 'altar', 'altarpiece', + 'altazimuth', 'alternator', 'altimeter', 'amati', 'ambulance', + 'amen corner', 'american organ', 'ammeter', 'ammonia clock', 'ammunition', + 'amphibian', 'amphibian', 'amphitheater', 'amphitheater', 'amphora', + 'amplifier', 'ampulla', 'amusement arcade', 'analog clock', + 'analog computer', 'analog watch', 'analytical balance', 'analyzer', + 'anamorphosis', 'anastigmat', 'anchor', 'anchor chain', 'anchor light', + 'and circuit', 'andiron', 'android', 'anechoic chamber', 'anemometer', + 'aneroid barometer', 'angiocardiogram', 'angioscope', 'angle bracket', + 'angledozer', 'ankle brace', 'anklet', 'anklet', 'ankus', 'anode', 'anode', + 'answering machine', 'antenna', 'anteroom', 'antiaircraft', + 'antiballistic missile', 'antifouling paint', 'anti-g suit', 'antimacassar', + 'antiperspirant', 'anti-submarine rocket', 'anvil', 'ao dai', 'apadana', + 'apartment', 'apartment building', 'aperture', 'aperture', 'apiary', + 'apparatus', 'apparel', 'applecart', 'appliance', 'appliance', 'applicator', + 'appointment', 'apron', 'apron string', 'apse', 'aqualung', 'aquaplane', + 'aquarium', 'arabesque', 'arbor', 'arcade', 'arch', 'architecture', + 'architrave', 'arch support', 'arc lamp', 'arctic', 'area', 'areaway', + 'argyle', 'ark', 'arm', 'armament', 'armature', 'armband', 'armchair', + 'armet', 'arm guard', 'armhole', 'armilla', 'armlet', 'armoire', 'armor', + 'armored car', 'armored car', 'armored personnel carrier', + 'armored vehicle', 'armor plate', 'armory', 'armrest', 'arquebus', 'array', + 'array', 'arrester', 'arrow', 'arsenal', 'arterial road', 'arthrogram', + 'arthroscope', 'artificial heart', 'artificial horizon', 'artificial joint', + 'artificial kidney', 'artificial skin', 'artillery', 'artillery shell', + 'artist\'s loft', 'art school', 'ascot', 'ashcan', 'ash-pan', 'ashtray', + 'aspergill', 'aspersorium', 'aspirator', 'aspirin powder', 'assault gun', + 'assault rifle', 'assegai', 'assembly', 'assembly', 'assembly hall', + 'assembly plant', 'astatic coils', 'astatic galvanometer', 'astrodome', + 'astrolabe', 'astronomical telescope', 'astronomy satellite', 'athenaeum', + 'athletic sock', 'athletic supporter', 'atlas', 'atmometer', 'atom bomb', + 'atomic clock', 'atomic pile', 'atomizer', 'atrium', 'attache case', + 'attachment', 'attack submarine', 'attenuator', 'attic', 'attic fan', + 'attire', 'audio amplifier', 'audiocassette', 'audio cd', 'audiometer', + 'audio system', 'audiotape', 'audiotape', 'audiovisual', 'auditorium', + 'auger', 'autobahn', 'autoclave', 'autofocus', 'autogiro', 'autoinjector', + 'autoloader', 'automat', 'automat', 'automatic choke', 'automatic firearm', + 'automatic pistol', 'automatic rifle', 'automatic transmission', + 'automation', 'automaton', 'automobile engine', 'automobile factory', + 'automobile horn', 'autopilot', 'autoradiograph', 'autostrada', + 'auxiliary boiler', 'auxiliary engine', 'auxiliary pump', + 'auxiliary research submarine', 'auxiliary storage', 'aviary', 'awl', + 'awning', 'ax', 'ax handle', 'ax head', 'axis', 'axle', 'axle bar', + 'axletree', 'babushka', 'baby bed', 'baby buggy', 'baby grand', + 'baby powder', 'baby shoe', 'back', 'back', 'backbench', 'backboard', + 'backboard', 'backbone', 'back brace', 'backgammon board', 'background', + 'backhoe', 'backlighting', 'backpack', 'backpacking tent', 'backplate', + 'back porch', 'backsaw', 'backscratcher', 'backseat', 'backspace key', + 'backstairs', 'backstay', 'backstop', 'backsword', 'backup system', + 'badminton court', 'badminton equipment', 'badminton racket', 'bag', 'bag', + 'bag', 'baggage', 'baggage', 'baggage car', 'baggage claim', 'bagpipe', + 'bailey', 'bailey', 'bailey bridge', 'bain-marie', 'bait', 'baize', + 'bakery', 'balaclava', 'balalaika', 'balance', 'balance beam', + 'balance wheel', 'balbriggan', 'balcony', 'balcony', 'baldachin', 'baldric', + 'bale', 'baling wire', 'ball', 'ball', 'ball and chain', + 'ball-and-socket joint', 'ballast', 'ball bearing', 'ball cartridge', + 'ballcock', 'balldress', 'ballet skirt', 'ball gown', + 'ballistic galvanometer', 'ballistic missile', 'ballistic pendulum', + 'ballistocardiograph', 'balloon', 'balloon bomb', 'balloon sail', + 'ballot box', 'ballpark', 'ball-peen hammer', 'ballpoint', 'ballroom', + 'ball valve', 'balsa raft', 'baluster', 'banana boat', 'band', 'bandage', + 'band aid', 'bandanna', 'bandbox', 'banderilla', 'bandoleer', 'bandoneon', + 'bandsaw', 'bandwagon', 'bangalore torpedo', 'bangle', 'banjo', 'banner', + 'bannister', 'banquette', 'banyan', 'baptismal font', 'bar', 'bar', + 'barbecue', 'barbed wire', 'barbell', 'barber chair', 'barbershop', + 'barbette carriage', 'barbican', 'bar bit', 'bareboat', 'barge', + 'barge pole', 'baritone', 'bark', 'bar magnet', 'bar mask', 'barn', + 'barndoor', 'barn door', 'barnyard', 'barograph', 'barometer', 'barong', + 'barouche', 'bar printer', 'barrack', 'barrage balloon', 'barrel', 'barrel', + 'barrelhouse', 'barrel knot', 'barrel organ', 'barrel vault', 'barrette', + 'barricade', 'barrier', 'barroom', 'barrow', 'bascule', 'base', 'base', + 'baseball', 'baseball bat', 'baseball cap', 'baseball equipment', + 'baseball glove', 'basement', 'basement', + 'basic point defense missile system', 'basilica', 'basilica', 'basilisk', + 'basin', 'basinet', 'basket', 'basket', 'basketball', 'basketball court', + 'basketball equipment', 'basket weave', 'bass', 'bass clarinet', + 'bass drum', 'basset horn', 'bass fiddle', 'bass guitar', 'bass horn', + 'bassinet', 'bassinet', 'bassoon', 'baster', 'bastinado', 'bastion', + 'bastion', 'bat', 'bath', 'bath chair', 'bathhouse', 'bathhouse', + 'bathing cap', 'bath oil', 'bathrobe', 'bathroom', 'bath salts', + 'bath towel', 'bathtub', 'bathyscaphe', 'bathysphere', 'batik', 'batiste', + 'baton', 'baton', 'baton', 'baton', 'battering ram', 'batter\'s box', + 'battery', 'battery', 'batting cage', 'batting glove', 'batting helmet', + 'battle-ax', 'battle cruiser', 'battle dress', 'battlement', 'battleship', + 'battle sight', 'bay', 'bay', 'bayonet', 'bay rum', 'bay window', 'bazaar', + 'bazaar', 'bazooka', 'b battery', 'bb gun', 'beach house', 'beach towel', + 'beach wagon', 'beachwear', 'beacon', 'beading plane', 'beaker', 'beaker', + 'beam', 'beam balance', 'beanbag', 'beanie', 'bearing', 'bearing rein', + 'bearing wall', 'bearskin', 'beater', 'beating-reed instrument', 'beaver', + 'beaver', 'beckman thermometer', 'bed', 'bed', 'bed and breakfast', + 'bedclothes', 'bedford cord', 'bed jacket', 'bedpan', 'bedpost', 'bedroll', + 'bedroom', 'bedroom furniture', 'bedsitting room', 'bedspread', 'bedspring', + 'bedstead', 'beefcake', 'beehive', 'beeper', 'beer barrel', 'beer bottle', + 'beer can', 'beer garden', 'beer glass', 'beer hall', 'beer mat', + 'beer mug', 'belaying pin', 'belfry', 'bell', 'bell arch', 'bellarmine', + 'bellbottom trousers', 'bell cote', 'bell foundry', 'bell gable', + 'bell jar', 'bellows', 'bellpull', 'bell push', 'bell seat', 'bell tent', + 'bell tower', 'bellyband', 'belt', 'belt', 'belt buckle', 'belting', + 'bench', 'bench clamp', 'bench hook', 'bench lathe', 'bench press', + 'bender', 'beret', 'berlin', 'bermuda shorts', 'berth', 'besom', + 'bessemer converter', 'bethel', 'betting shop', 'bevatron', 'bevel', + 'bevel gear', 'b-flat clarinet', 'bib', 'bib-and-tucker', 'bicorn', + 'bicycle', 'bicycle-built-for-two', 'bicycle chain', 'bicycle clip', + 'bicycle pump', 'bicycle rack', 'bicycle seat', 'bicycle wheel', 'bidet', + 'bier', 'bier', 'bi-fold door', 'bifocals', 'big blue', 'big board', + 'bight', 'bikini', 'bikini pants', 'bilge', 'bilge keel', 'bilge pump', + 'bilge well', 'bill', 'bill', 'billboard', 'billiard ball', 'billiard room', + 'bin', 'binder', 'binder', 'bindery', 'binding', 'bin liner', 'binnacle', + 'binoculars', 'binocular microscope', 'biochip', 'biohazard suit', + 'bioscope', 'biplane', 'birch', 'birchbark canoe', 'birdbath', 'birdcage', + 'birdcall', 'bird feeder', 'birdhouse', 'bird shot', 'biretta', 'bishop', + 'bistro', 'bit', 'bit', 'bite plate', 'bitewing', 'bitumastic', 'black', + 'black', 'blackboard', 'blackboard eraser', 'black box', 'blackface', + 'blackjack', 'black tie', 'blackwash', 'bladder', 'blade', 'blade', 'blade', + 'blank', 'blanket', 'blast furnace', 'blasting cap', 'blazer', 'blender', + 'blimp', 'blind', 'blind curve', 'blindfold', 'bling', 'blinker', + 'blister pack', 'block', 'blockade', 'blockade-runner', 'block and tackle', + 'blockbuster', 'blockhouse', 'block plane', 'bloodmobile', 'bloomers', + 'blouse', 'blower', 'blowtorch', 'blucher', 'bludgeon', 'blue', 'blue chip', + 'blunderbuss', 'blunt file', 'boarding', 'boarding house', 'boardroom', + 'boards', 'boat', 'boater', 'boat hook', 'boathouse', 'boatswain\'s chair', + 'boat train', 'boatyard', 'bobbin', 'bobby pin', 'bobsled', 'bobsled', + 'bocce ball', 'bodega', 'bodice', 'bodkin', 'bodkin', 'bodkin', 'body', + 'body armor', 'body lotion', 'body stocking', 'body plethysmograph', + 'body pad', 'bodywork', 'bofors gun', 'bogy', 'boiler', + 'boiling water reactor', 'bolero', 'bollard', 'bolo', 'bolo tie', 'bolt', + 'bolt', 'bolt', 'bolt cutter', 'bomb', 'bombazine', 'bomb calorimeter', + 'bomber', 'bomber jacket', 'bomblet', 'bomb rack', 'bombshell', + 'bomb shelter', 'bone-ash cup', 'bone china', 'bones', 'boneshaker', + 'bongo', 'bonnet', 'book', 'book bag', 'bookbindery', 'bookcase', 'bookend', + 'bookmark', 'bookmobile', 'bookshelf', 'bookshop', 'boom', 'boom', + 'boomerang', 'booster', 'booster', 'boot', 'boot', 'boot camp', 'bootee', + 'booth', 'booth', 'booth', 'boothose', 'bootjack', 'bootlace', 'bootleg', + 'bootstrap', 'bore bit', 'boron chamber', 'borstal', 'bosom', + 'boston rocker', 'bota', 'bottle', 'bottle', 'bottle bank', 'bottlebrush', + 'bottlecap', 'bottle opener', 'bottling plant', 'bottom', 'boucle', + 'boudoir', 'boulle', 'bouncing betty', 'bouquet', 'boutique', 'boutonniere', + 'bow', 'bow', 'bow', 'bow and arrow', 'bowed stringed instrument', + 'bowie knife', 'bowl', 'bowl', 'bowl', 'bowler hat', 'bowline', + 'bowling alley', 'bowling ball', 'bowling equipment', 'bowling pin', + 'bowling shoe', 'bowsprit', 'bowstring', 'bow tie', 'box', 'box', 'box', + 'box beam', 'box camera', 'boxcar', 'box coat', 'boxing equipment', + 'boxing glove', 'box office', 'box spring', 'box wrench', 'brace', 'brace', + 'brace', 'brace', 'brace and bit', 'bracelet', 'bracer', 'brace wrench', + 'bracket', 'bradawl', 'brake', 'brake', 'brake band', 'brake cylinder', + 'brake disk', 'brake drum', 'brake lining', 'brake pad', 'brake pedal', + 'brake shoe', 'brake system', 'brass', 'brass', 'brass', 'brassard', + 'brasserie', 'brassie', 'brassiere', 'brass knucks', 'brattice', 'brazier', + 'breadbasket', 'bread-bin', 'bread knife', 'breakable', 'breakfast area', + 'breakfast table', 'breakwater', 'breast drill', 'breast implant', + 'breastplate', 'breast pocket', 'breathalyzer', 'breechblock', + 'breechcloth', 'breeches', 'breeches buoy', 'breechloader', + 'breeder reactor', 'bren', 'brewpub', 'brick', 'brickkiln', + 'bricklayer\'s hammer', 'brick trowel', 'brickwork', 'bridal gown', + 'bridge', 'bridge', 'bridle', 'bridle path', 'bridoon', 'briefcase', + 'briefcase bomb', 'briefcase computer', 'briefs', 'brig', 'brig', + 'brigandine', 'brigantine', 'brilliantine', 'brilliant pebble', 'brim', + 'bristle brush', 'britches', 'broad arrow', 'broadax', 'brochette', + 'broadcaster', 'broadcloth', 'broadcloth', 'broad hatchet', 'broadloom', + 'broadside', 'broadsword', 'brocade', 'brogan', 'broiler', 'broken arch', + 'bronchoscope', 'broom', 'broom closet', 'broomstick', 'brougham', + 'browning automatic rifle', 'browning machine gun', 'brownstone', + 'brunch coat', 'brush', 'brussels carpet', 'brussels lace', 'bubble', + 'bubble chamber', 'bubble jet printer', 'buckboard', 'bucket', + 'bucket seat', 'bucket shop', 'buckle', 'buckram', 'bucksaw', 'buckskins', + 'buff', 'buffer', 'buffer', 'buffet', 'buffing wheel', 'buggy', 'bugle', + 'building', 'building complex', 'bulldog clip', 'bulldog wrench', + 'bulldozer', 'bullet', 'bulletproof vest', 'bullet train', 'bullhorn', + 'bullion', 'bullnose', 'bullpen', 'bullpen', 'bullring', 'bulwark', + 'bumboat', 'bumper', 'bumper', 'bumper car', 'bumper guard', 'bumper jack', + 'bundle', 'bung', 'bungalow', 'bungee', 'bunghole', 'bunk', 'bunk', + 'bunk bed', 'bunker', 'bunker', 'bunker', 'bunsen burner', 'bunting', 'bur', + 'burberry', 'burette', 'burglar alarm', 'burial chamber', 'burial garment', + 'burial mound', 'burin', 'burqa', 'burlap', 'burn bag', 'burner', 'burnous', + 'burp gun', 'burr', 'bus', 'bushel basket', 'bushing', 'bush jacket', + 'business suit', 'buskin', 'bustier', 'bustle', 'butcher knife', + 'butcher shop', 'butter dish', 'butterfly valve', 'butter knife', + 'butt hinge', 'butt joint', 'button', 'buttonhook', 'buttress', + 'butt shaft', 'butt weld', 'buzz bomb', 'buzzer', 'bvd', 'bypass condenser', + 'byway', 'cab', 'cab', 'cab', 'cabana', 'cabaret', 'caber', 'cabin', + 'cabin', 'cabin car', 'cabin class', 'cabin cruiser', 'cabinet', 'cabinet', + 'cabinet', 'cabinetwork', 'cabin liner', 'cable', 'cable', 'cable car', + 'cache', 'caddy', 'caesium clock', 'cafe', 'cafeteria', 'cafeteria tray', + 'caff', 'caftan', 'caftan', 'cage', 'cage', 'cagoule', 'caisson', 'calash', + 'calceus', 'calcimine', 'calculator', 'caldron', 'calico', 'caliper', + 'call-board', 'call center', 'caller id', 'calliope', 'calorimeter', + 'calpac', 'camail', 'camber arch', 'cambric', 'camcorder', 'camel\'s hair', + 'camera', 'camera lens', 'camera lucida', 'camera obscura', 'camera tripod', + 'camise', 'camisole', 'camisole', 'camlet', 'camouflage', 'camouflage', + 'camp', 'camp', 'camp', 'campaign hat', 'campanile', 'camp chair', 'camper', + 'camper trailer', 'campstool', 'camshaft', 'can', 'canal', 'canal boat', + 'candelabrum', 'candid camera', 'candle', 'candlepin', 'candlesnuffer', + 'candlestick', 'candlewick', 'candy thermometer', 'cane', 'cane', 'cangue', + 'canister', 'cannery', 'cannikin', 'cannikin', 'cannon', 'cannon', 'cannon', + 'cannon', 'cannonball', 'canoe', 'can opener', 'canopic jar', 'canopy', + 'canopy', 'canopy', 'canteen', 'canteen', 'canteen', 'canteen', 'canteen', + 'cant hook', 'cantilever', 'cantilever bridge', 'cantle', 'canton crepe', + 'canvas', 'canvas', 'canvas tent', 'cap', 'cap', 'cap', 'capacitor', + 'caparison', 'cape', 'capital ship', 'capitol', 'cap opener', 'capote', + 'capote', 'cap screw', 'capstan', 'capstone', 'capsule', 'captain\'s chair', + 'car', 'car', 'car', 'carabiner', 'carafe', 'caravansary', 'car battery', + 'carbine', 'car bomb', 'carbon arc lamp', 'carboy', 'carburetor', + 'car carrier', 'cardcase', 'cardiac monitor', 'cardigan', 'card index', + 'cardiograph', 'cardioid microphone', 'car door', 'cardroom', 'card table', + 'card table', 'car-ferry', 'cargo area', 'cargo container', 'cargo door', + 'cargo hatch', 'cargo helicopter', 'cargo liner', 'cargo ship', 'carillon', + 'car mirror', 'caroche', 'carousel', 'carpenter\'s hammer', + 'carpenter\'s kit', 'carpenter\'s level', 'carpenter\'s mallet', + 'carpenter\'s rule', 'carpenter\'s square', 'carpetbag', 'carpet beater', + 'carpet loom', 'carpet pad', 'carpet sweeper', 'carpet tack', 'carport', + 'carrack', 'carrel', 'carriage', 'carriage', 'carriage bolt', 'carriageway', + 'carriage wrench', 'carrick bend', 'carrier', 'carryall', 'carrycot', + 'car seat', 'cart', 'car tire', 'carton', 'cartouche', 'car train', + 'cartridge', 'cartridge', 'cartridge belt', 'cartridge extractor', + 'cartridge fuse', 'cartridge holder', 'cartwheel', 'carving fork', + 'carving knife', 'car wheel', 'caryatid', 'cascade liquefier', + 'cascade transformer', 'case', 'case', 'case', 'casein paint', 'case knife', + 'case knife', 'casement', 'casement window', 'casern', 'case shot', + 'cash bar', 'cashbox', 'cash machine', 'cashmere', 'cash register', + 'casing', 'casino', 'casket', 'casque', 'casquet', + 'cassegrainian telescope', 'casserole', 'cassette', 'cassette deck', + 'cassette player', 'cassette recorder', 'cassette tape', 'cassock', 'cast', + 'caster', 'caster', 'castle', 'castle', 'catacomb', 'catafalque', + 'catalytic converter', 'catalytic cracker', 'catamaran', 'catapult', + 'catapult', 'catboat', 'cat box', 'catch', 'catchall', 'catcher\'s mask', + 'catchment', 'caterpillar', 'cathedra', 'cathedral', 'cathedral', + 'catheter', 'cathode', 'cathode-ray tube', 'cat-o\'-nine-tails', + 'cat\'s-paw', 'catsup bottle', 'cattle car', 'cattle guard', 'cattleship', + 'cautery', 'cavalier hat', 'cavalry sword', 'cavetto', 'cavity wall', + 'c battery', 'c-clamp', 'cd drive', 'cd player', 'cd-r', 'cd-rom', + 'cd-rom drive', 'cedar chest', 'ceiling', 'celesta', 'cell', 'cell', + 'cellar', 'cellblock', 'cello', 'cellophane', 'cellular telephone', + 'cellulose tape', 'cenotaph', 'censer', 'center', 'center punch', + 'centigrade thermometer', 'central processing unit', 'centrifugal pump', + 'centrifuge', 'ceramic', 'ceramic ware', 'cereal bowl', 'cereal box', + 'cerecloth', 'cesspool', 'chachka', 'chador', 'chafing dish', 'chain', + 'chain', 'chainlink fence', 'chain mail', 'chain printer', 'chain saw', + 'chain store', 'chain tongs', 'chain wrench', 'chair', 'chair', + 'chair of state', 'chairlift', 'chaise', 'chaise longue', 'chalet', + 'chalice', 'chalk', 'challis', 'chamberpot', 'chambray', 'chamfer bit', + 'chamfer plane', 'chamois cloth', 'chancel', 'chancellery', 'chancery', + 'chandelier', 'chandlery', 'chanfron', 'chanter', 'chantry', 'chap', + 'chapel', 'chapterhouse', 'chapterhouse', 'character printer', + 'charcuterie', 'charge-exchange accelerator', 'charger', 'chariot', + 'chariot', 'charnel house', 'chassis', 'chassis', 'chasuble', 'chateau', + 'chatelaine', 'checker', 'checkout', 'cheekpiece', 'cheeseboard', + 'cheesecloth', 'cheese cutter', 'cheese press', 'chemical bomb', + 'chemical plant', 'chemical reactor', 'chemise', 'chemise', 'chenille', + 'chessman', 'chest', 'chesterfield', 'chest of drawers', 'chest protector', + 'cheval-de-frise', 'cheval glass', 'chicane', 'chicken coop', + 'chicken wire', 'chicken yard', 'chiffon', 'chiffonier', 'child\'s room', + 'chime', 'chimney breast', 'chimney corner', 'china', 'china cabinet', + 'chinchilla', 'chinese lantern', 'chinese puzzle', 'chinning bar', 'chino', + 'chino', 'chin rest', 'chin strap', 'chintz', 'chip', 'chip', 'chisel', + 'chlamys', 'choir', 'choir loft', 'choke', 'choke', 'chokey', 'choo-choo', + 'chopine', 'chordophone', 'christmas stocking', 'chronograph', + 'chronometer', 'chronoscope', 'chuck', 'chuck wagon', 'chukka', 'church', + 'church bell', 'church hat', 'church key', 'church tower', 'churidars', + 'churn', 'ciderpress', 'cigar band', 'cigar box', 'cigar cutter', + 'cigarette butt', 'cigarette case', 'cigarette holder', 'cigar lighter', + 'cinch', 'cinema', 'cinquefoil', 'circle', 'circlet', 'circuit', + 'circuit board', 'circuit breaker', 'circuitry', 'circular plane', + 'circular saw', 'circus tent', 'cistern', 'cistern', 'cittern', 'city hall', + 'cityscape', 'city university', 'civies', 'civilian clothing', + 'clack valve', 'clamp', 'clamshell', 'clapper', 'clapperboard', 'clarence', + 'clarinet', 'clark cell', 'clasp', 'clasp knife', 'classroom', 'clavichord', + 'clavier', 'clay pigeon', 'claymore mine', 'claymore', 'cleaners', + 'cleaning implement', 'cleaning pad', 'clean room', 'clearway', 'cleat', + 'cleat', 'cleats', 'cleaver', 'clerestory', 'clevis', 'clews', + 'cliff dwelling', 'climbing frame', 'clinch', 'clinch', 'clincher', + 'clinic', 'clinical thermometer', 'clinker', 'clinometer', 'clip', + 'clip lead', 'clip-on', 'clipper', 'clipper', 'clipper', 'cloak', 'cloak', + 'cloakroom', 'cloche', 'cloche', 'clock', 'clock pendulum', 'clock radio', + 'clock tower', 'clockwork', 'clog', 'cloisonne', 'cloister', + 'closed circuit', 'closed-circuit television', 'closed loop', 'closet', + 'closeup lens', 'cloth cap', 'cloth covering', 'clothesbrush', + 'clothes closet', 'clothes dryer', 'clothes hamper', 'clotheshorse', + 'clothespin', 'clothes tree', 'clothing', 'clothing store', 'clout nail', + 'clove hitch', 'club car', 'clubroom', 'cluster bomb', 'clutch', 'clutch', + 'clutch bag', 'coach', 'coach house', 'coal car', 'coal chute', + 'coal house', 'coal shovel', 'coaming', 'coaster brake', 'coat', + 'coat button', 'coat closet', 'coatdress', 'coatee', 'coat hanger', + 'coating', 'coating', 'coat of paint', 'coatrack', 'coattail', + 'coaxial cable', 'cobweb', 'cobweb', 'cockcroft and walton accelerator', + 'cocked hat', 'cockhorse', 'cockleshell', 'cockpit', 'cockpit', 'cockpit', + 'cockscomb', 'cocktail dress', 'cocktail lounge', 'cocktail shaker', + 'cocotte', 'codpiece', 'coelostat', 'coffee can', 'coffee cup', + 'coffee filter', 'coffee maker', 'coffee mill', 'coffee mug', 'coffeepot', + 'coffee stall', 'coffee table', 'coffee urn', 'coffer', 'coffey still', + 'coffin', 'cog', 'coif', 'coil', 'coil', 'coil', 'coil spring', 'coin box', + 'colander', 'cold cathode', 'cold chisel', 'cold cream', 'cold frame', + 'collar', 'collar', 'college', 'collet', 'collider', 'colliery', + 'collimator', 'collimator', 'cologne', 'colonnade', 'colonoscope', + 'colorimeter', 'colors', 'color television', 'color tube', 'color wash', + 'colt', 'colter', 'columbarium', 'columbarium', 'column', 'column', 'comb', + 'comb', 'comber', 'combination lock', 'combination plane', 'combine', + 'comforter', 'command module', 'commissary', 'commissary', 'commodity', + 'common ax', 'common room', 'communications satellite', + 'communication system', 'community center', 'commutator', 'commuter', + 'compact', 'compact', 'compact disk', 'compact-disk burner', 'companionway', + 'compartment', 'compartment', 'compass', 'compass', 'compass card', + 'compass saw', 'compound', 'compound lens', 'compound lever', + 'compound microscope', 'compress', 'compression bandage', 'compressor', + 'computer', 'computer circuit', 'computerized axial tomography scanner', + 'computer keyboard', 'computer monitor', 'computer network', + 'computer screen', 'computer store', 'computer system', + 'concentration camp', 'concert grand', 'concert hall', 'concertina', + 'concertina', 'concrete mixer', 'condensation pump', 'condenser', + 'condenser', 'condenser', 'condenser microphone', 'condominium', + 'condominium', 'conductor', 'cone clutch', 'confectionery', + 'conference center', 'conference room', 'conference table', 'confessional', + 'conformal projection', 'congress boot', 'conic projection', + 'connecting rod', 'connecting room', 'connection', 'conning tower', + 'conning tower', 'conservatory', 'conservatory', 'console', 'console', + 'console table', 'consulate', 'contact', 'contact', 'container', + 'container ship', 'containment', 'contrabassoon', 'control', + 'control center', 'control circuit', 'control key', 'control panel', + 'control rod', 'control room', 'control system', 'control tower', + 'convector', 'convenience store', 'convent', 'conventicle', + 'converging lens', 'converter', 'convertible', 'convertible', 'conveyance', + 'conveyer belt', 'cooker', 'cookfire', 'cookhouse', 'cookie cutter', + 'cookie jar', 'cookie sheet', 'cooking utensil', 'cookstove', + 'coolant system', 'cooler', 'cooling system', 'cooling system', + 'cooling tower', 'coonskin cap', 'cope', 'coping saw', 'copperware', + 'copyholder', 'coquille', 'coracle', 'corbel', 'corbel arch', 'corbel step', + 'corbie gable', 'cord', 'cord', 'cordage', 'cords', 'core', 'core bit', + 'core drill', 'corer', 'cork', 'corker', 'corkscrew', 'corncrib', 'corner', + 'corner', 'corner post', 'cornet', 'cornice', 'cornice', 'cornice', + 'correctional institution', 'corrugated fastener', 'corselet', 'corset', + 'cosmetic', 'cosmotron', 'costume', 'costume', 'costume', 'costume', 'cosy', + 'cot', 'cottage tent', 'cotter', 'cotter pin', 'cotton', 'cotton flannel', + 'cotton mill', 'couch', 'couch', 'couchette', 'coude telescope', 'counter', + 'counter', 'counter', 'counterbore', 'counter tube', 'country house', + 'country store', 'coupe', 'coupling', 'court', 'court', 'court', 'court', + 'courtelle', 'courthouse', 'courthouse', 'coverall', 'covered bridge', + 'covered couch', 'covered wagon', 'covering', 'coverlet', 'cover plate', + 'cowbarn', 'cowbell', 'cowboy boot', 'cowboy hat', 'cowhide', 'cowl', + 'cow pen', 'cpu board', 'crackle', 'cradle', 'craft', 'cramp', 'crampon', + 'crampon', 'crane', 'craniometer', 'crank', 'crankcase', 'crankshaft', + 'crash barrier', 'crash helmet', 'crate', 'cravat', 'crayon', 'crazy quilt', + 'cream', 'cream pitcher', 'creche', 'creche', 'credenza', 'creel', + 'crematory', 'crematory', 'crepe', 'crepe de chine', 'crescent wrench', + 'cretonne', 'crib', 'crib', 'cricket ball', 'cricket bat', + 'cricket equipment', 'cringle', 'crinoline', 'crinoline', 'crochet needle', + 'crock', 'crock pot', 'crook', 'crookes radiometer', 'crookes tube', + 'croquet ball', 'croquet equipment', 'croquet mallet', 'cross', 'crossbar', + 'crossbar', 'crossbar', 'crossbench', 'cross bit', 'crossbow', + 'crosscut saw', 'crossjack', 'crosspiece', 'crotchet', 'croupier\'s rake', + 'crowbar', 'crown', 'crown', 'crown jewels', 'crown lens', 'crow\'s nest', + 'crucible', 'crucifix', 'cruet', 'cruet-stand', 'cruise control', + 'cruise missile', 'cruiser', 'cruiser', 'cruise ship', 'crupper', 'cruse', + 'crusher', 'crutch', 'cryometer', 'cryoscope', 'cryostat', 'crypt', + 'crystal', 'crystal detector', 'crystal microphone', 'crystal oscillator', + 'crystal set', 'cubitiere', 'cucking stool', 'cuckoo clock', 'cuddy', + 'cudgel', 'cue', 'cue ball', 'cuff', 'cuirass', 'cuisse', 'cul', + 'culdoscope', 'cullis', 'culotte', 'cultivator', 'culverin', 'culverin', + 'culvert', 'cup', 'cupboard', 'cup hook', 'cupola', 'cupola', 'curb', + 'curb roof', 'curbstone', 'curette', 'curler', 'curling iron', 'currycomb', + 'cursor', 'curtain', 'customhouse', 'cutaway', 'cutlas', 'cutoff', 'cutout', + 'cutter', 'cutter', 'cutting implement', 'cutting room', 'cutty stool', + 'cutwork', 'cybercafe', 'cyclopean masonry', 'cyclostyle', 'cyclotron', + 'cylinder', 'cylinder', 'cylinder lock', 'cymbal', 'dacha', 'dacron', + 'dado', 'dado plane', 'dagger', 'dairy', 'dais', 'daisy print wheel', + 'daisywheel printer', 'dam', 'damask', 'dampener', 'damper', 'damper block', + 'dark lantern', 'darkroom', 'darning needle', 'dart', 'dart', 'dashboard', + 'dashiki', 'dash-pot', 'data converter', 'data input device', + 'data multiplexer', 'data system', 'davenport', 'davenport', 'davit', + 'daybed', 'daybook', 'day nursery', 'day school', 'dead axle', 'deadeye', + 'deadhead', 'deanery', 'deathbed', 'death camp', 'death house', + 'death knell', 'death seat', 'deck', 'deck', 'deck chair', 'deck-house', + 'deckle', 'deckle edge', 'declinometer', 'decoder', 'decolletage', + 'decoupage', 'dedicated file server', 'deep-freeze', 'deerstalker', + 'defense system', 'defensive structure', 'defibrillator', 'defilade', + 'deflector', 'delayed action', 'delay line', 'delft', 'delicatessen', + 'delivery truck', 'delta wing', 'demijohn', 'demitasse', 'den', 'denim', + 'densimeter', 'densitometer', 'dental appliance', 'dental floss', + 'dental implant', 'dentist\'s drill', 'denture', 'deodorant', + 'department store', 'departure lounge', 'depilatory', 'depressor', + 'depth finder', 'depth gauge', 'derrick', 'derrick', 'derringer', 'desk', + 'desk phone', 'desktop computer', 'dessert spoon', 'destroyer', + 'destroyer escort', 'detached house', 'detector', 'detector', + 'detention home', 'detonating fuse', 'detonator', 'developer', 'device', + 'dewar flask', 'dhoti', 'dhow', 'dial', 'dial', 'dial', 'dialog box', + 'dial telephone', 'dialyzer', 'diamante', 'diaper', 'diaper', 'diaphone', + 'diaphragm', 'diaphragm', 'diathermy machine', 'dibble', 'dice cup', + 'dicer', 'dickey', 'dickey', 'dictaphone', 'die', 'diesel', + 'diesel-electric locomotive', 'diesel-hydraulic locomotive', + 'diesel locomotive', 'diestock', 'differential analyzer', + 'differential gear', 'diffuser', 'diffuser', 'digester', 'diggings', + 'digital-analog converter', 'digital audiotape', 'digital camera', + 'digital clock', 'digital computer', 'digital display', + 'digital subscriber line', 'digital voltmeter', 'digital watch', + 'digitizer', 'dilator', 'dildo', 'dimity', 'dimmer', 'diner', 'dinette', + 'dinghy', 'dining area', 'dining car', 'dining-hall', 'dining room', + 'dining-room furniture', 'dining-room table', 'dining table', 'dinner bell', + 'dinner dress', 'dinner jacket', 'dinner napkin', 'dinner pail', + 'dinner table', 'dinner theater', 'diode', 'diode', 'dip', + 'diplomatic building', 'dipole', 'dipper', 'dipstick', 'dip switch', + 'directional antenna', 'directional microphone', 'direction finder', 'dirk', + 'dirndl', 'dirndl', 'dirty bomb', 'discharge lamp', 'discharge pipe', + 'disco', 'discount house', 'discus', 'disguise', 'dish', 'dish', 'dishpan', + 'dish rack', 'dishrag', 'dishtowel', 'dishwasher', 'disk', 'disk brake', + 'disk clutch', 'disk controller', 'disk drive', 'diskette', 'disk harrow', + 'dispatch case', 'dispensary', 'dispenser', 'display', 'display adapter', + 'display panel', 'display window', 'disposal', 'disrupting explosive', + 'distaff', 'distillery', 'distributor', 'distributor cam', + 'distributor cap', 'distributor housing', 'distributor point', 'ditch', + 'ditch spade', 'ditty bag', 'divan', 'divan', 'dive bomber', + 'diverging lens', 'divided highway', 'divider', 'diving bell', + 'divining rod', 'diving suit', 'dixie', 'dixie cup', 'dock', 'doeskin', + 'dogcart', 'doggie bag', 'dogsled', 'dog wrench', 'doily', 'doll', + 'dollhouse', 'dolly', 'dolman', 'dolman', 'dolman sleeve', 'dolmen', 'dome', + 'dome', 'domino', 'dongle', 'donkey jacket', 'door', 'door', 'door', + 'doorbell', 'doorframe', 'doorjamb', 'doorlock', 'doormat', 'doornail', + 'doorplate', 'doorsill', 'doorstop', 'doppler radar', 'dormer', + 'dormer window', 'dormitory', 'dormitory', 'dosemeter', 'dossal', + 'dot matrix printer', 'double bed', 'double-bitted ax', 'double boiler', + 'double-breasted jacket', 'double-breasted suit', 'double door', + 'double glazing', 'double-hung window', 'double knit', 'doubler', + 'double reed', 'double-reed instrument', 'doublet', 'doubletree', 'douche', + 'dovecote', 'dover\'s powder', 'dovetail', 'dovetail plane', 'dowel', + 'downstage', 'drafting instrument', 'drafting table', 'dragunov', + 'drainage ditch', 'drainage system', 'drain basket', 'drainplug', 'drape', + 'drapery', 'drawbar', 'drawbridge', 'drawer', 'drawers', 'drawing chalk', + 'drawing room', 'drawing room', 'drawknife', 'drawstring bag', 'dray', + 'dreadnought', 'dredge', 'dredger', 'dredging bucket', 'dress', + 'dress blues', 'dresser', 'dress hat', 'dressing', 'dressing case', + 'dressing gown', 'dressing room', 'dressing sack', 'dressing table', + 'dress rack', 'dress shirt', 'dress suit', 'dress uniform', 'drift net', + 'drill', 'electric drill', 'drilling platform', 'drill press', 'drill rig', + 'drinking fountain', 'drinking vessel', 'drip loop', 'drip mat', 'drip pan', + 'dripping pan', 'drip pot', 'drive', 'drive', 'drive line', 'driver', + 'driveshaft', 'driveway', 'driving iron', 'driving wheel', 'drogue', + 'drogue parachute', 'drone', 'drone', 'drop arch', 'drop cloth', + 'drop curtain', 'drop forge', 'drop-leaf table', 'dropper', 'droshky', + 'drove', 'drugget', 'drugstore', 'drum', 'drum', 'drum brake', 'drumhead', + 'drum printer', 'drum sander', 'drumstick', 'dry battery', + 'dry-bulb thermometer', 'dry cell', 'dry dock', 'dryer', 'dry fly', + 'dry kiln', 'dry masonry', 'dry point', 'dry wall', 'dual scan display', + 'duck', 'duckboard', 'duckpin', 'dudeen', 'duffel', 'duffel bag', + 'duffel coat', 'dugout', 'dugout canoe', 'dulciana', 'dulcimer', 'dulcimer', + 'dumbbell', 'dumb bomb', 'dumbwaiter', 'dumdum', 'dumpcart', 'dumpster', + 'dump truck', 'dumpy level', 'dunce cap', 'dune buggy', 'dungeon', + 'duplex apartment', 'duplex house', 'duplicator', 'dust bag', 'dustcloth', + 'dust cover', 'dust cover', 'dustmop', 'dustpan', 'dutch oven', + 'dutch oven', 'dwelling', 'dye-works', 'dynamo', 'dynamometer', + 'eames chair', 'earflap', 'early warning radar', 'early warning system', + 'earmuff', 'earphone', 'earplug', 'earplug', 'earthenware', 'earthwork', + 'easel', 'easy chair', 'eaves', 'ecclesiastical attire', 'echinus', + 'echocardiograph', 'edger', 'edge tool', 'efficiency apartment', + 'egg-and-dart', 'eggbeater', 'egg timer', 'eiderdown', 'eight ball', + 'ejection seat', 'elastic', 'elastic bandage', 'elastoplast', 'elbow', + 'elbow pad', 'electric', 'electrical cable', 'electrical contact', + 'electrical converter', 'electrical device', 'electrical system', + 'electric bell', 'electric blanket', 'electric chair', 'electric clock', + 'electric-discharge lamp', 'electric fan', 'electric frying pan', + 'electric furnace', 'electric guitar', 'electric hammer', 'electric heater', + 'electric lamp', 'electric locomotive', 'electric meter', 'electric mixer', + 'electric motor', 'electric organ', 'electric range', + 'electric refrigerator', 'electric toothbrush', 'electric typewriter', + 'electro-acoustic transducer', 'electrode', 'electrodynamometer', + 'electroencephalograph', 'electrograph', 'electrolytic', + 'electrolytic cell', 'electromagnet', 'electrometer', 'electromyograph', + 'electron accelerator', 'electron gun', 'electronic balance', + 'electronic converter', 'electronic device', 'electronic equipment', + 'electronic fetal monitor', 'electronic instrument', 'electronic voltmeter', + 'electron microscope', 'electron multiplier', 'electrophorus', + 'electroscope', 'electrostatic generator', 'electrostatic printer', + 'elevator', 'elevator', 'elevator shaft', 'embankment', 'embassy', + 'embellishment', 'emergency room', 'emesis basin', 'emitter', 'empty', + 'emulsion', 'enamel', 'enamel', 'enamelware', 'encaustic', 'encephalogram', + 'enclosure', 'endoscope', 'energizer', 'engine', 'engine', 'engineering', + 'enginery', 'english horn', 'english saddle', 'enlarger', 'ensemble', + 'ensign', 'entablature', 'entertainment center', 'entrenching tool', + 'entrenchment', 'envelope', 'envelope', 'envelope', 'eolith', 'epauliere', + 'epee', 'epergne', 'epicyclic train', 'epidiascope', 'epilating wax', + 'equalizer', 'equatorial', 'equipment', + 'erasable programmable read-only memory', 'eraser', 'erecting prism', + 'erection', 'erlenmeyer flask', 'escape hatch', 'escapement', + 'escape wheel', 'escarpment', 'escutcheon', 'esophagoscope', 'espadrille', + 'espalier', 'espresso maker', 'espresso shop', 'establishment', 'estaminet', + 'estradiol patch', 'etagere', 'etamine', 'etching', 'ethernet', + 'ethernet cable', 'eton jacket', 'etui', 'eudiometer', 'euphonium', + 'evaporative cooler', 'evening bag', 'exercise bike', 'exercise device', + 'exhaust', 'exhaust fan', 'exhaust valve', 'exhibition hall', 'exocet', + 'expansion bit', 'expansion bolt', 'explosive detection system', + 'explosive device', 'explosive trace detection', 'express', 'extension', + 'extension cord', 'external-combustion engine', 'external drive', + 'extractor', 'eyebrow pencil', 'eyecup', 'eyeliner', 'eyepatch', 'eyepiece', + 'eyeshadow', 'fabric', 'facade', 'face guard', 'face mask', 'faceplate', + 'face powder', 'face veil', 'facing', 'facing', 'facing', 'facsimile', + 'factory', 'factory ship', 'fagot', 'fagot stitch', + 'fahrenheit thermometer', 'faience', 'faille', 'fairlead', 'fairy light', + 'falchion', 'fallboard', 'fallout shelter', 'false face', 'false teeth', + 'family room', 'fan', 'fan belt', 'fan blade', 'fancy dress', 'fanion', + 'fanlight', 'fanjet', 'fanjet', 'fanny pack', 'fan tracery', 'fan vaulting', + 'farm building', 'farmer\'s market', 'farmhouse', 'farm machine', + 'farmplace', 'farmyard', 'farthingale', 'fastener', 'fast reactor', + 'fat farm', 'fatigues', 'faucet', 'fauld', 'fauteuil', 'feather boa', + 'featheredge', 'fedora', 'feedback circuit', 'feedlot', 'fell', 'felloe', + 'felt', 'felt-tip pen', 'felucca', 'fence', 'fencing mask', 'fencing sword', + 'fender', 'fender', 'ferris wheel', 'ferrule', 'ferry', 'ferule', 'festoon', + 'fetoscope', 'fetter', 'fez', 'fiber', 'fiber optic cable', 'fiberscope', + 'fichu', 'fiddlestick', 'field artillery', 'field coil', + 'field-effect transistor', 'field-emission microscope', 'field glass', + 'field hockey ball', 'field hospital', 'field house', 'field lens', + 'field magnet', 'field-sequential color television', 'field tent', + 'fieldwork', 'fife', 'fifth wheel', 'fighter', 'fighting chair', 'fig leaf', + 'figure eight', 'figure loom', 'figure skate', 'filament', 'filature', + 'file', 'file', 'file folder', 'file server', 'filigree', 'filling', 'film', + 'film', 'film advance', 'filter', 'filter', 'finder', 'finery', + 'fine-tooth comb', 'finger', 'fingerboard', 'finger bowl', 'finger paint', + 'finger-painting', 'finger plate', 'fingerstall', 'finish coat', + 'finish coat', 'finisher', 'fin keel', 'fipple', 'fipple flute', 'fire', + 'fire alarm', 'firearm', 'fire bell', 'fireboat', 'firebox', 'firebrick', + 'fire control radar', 'fire control system', 'fire engine', + 'fire extinguisher', 'fire iron', 'fireman\'s ax', 'fireplace', + 'fire screen', 'fire tongs', 'fire tower', 'firewall', 'firing chamber', + 'firing pin', 'firkin', 'firmer chisel', 'first-aid kit', + 'first-aid station', 'first base', 'first class', 'fishbowl', + 'fisherman\'s bend', 'fisherman\'s knot', 'fisherman\'s lure', 'fishhook', + 'fishing boat', 'fishing gear', 'fishing rod', 'fish joint', 'fish knife', + 'fishnet', 'fish slice', 'fitment', 'fixative', 'fixer-upper', 'flag', + 'flageolet', 'flagon', 'flagpole', 'flagship', 'flail', 'flambeau', + 'flamethrower', 'flange', 'flannel', 'flannel', 'flannelette', 'flap', + 'flash', 'flash', 'flash camera', 'flasher', 'flashlight', + 'flashlight battery', 'flash memory', 'flask', 'flat arch', 'flatbed', + 'flatbed press', 'flat bench', 'flatcar', 'flat file', 'flatlet', + 'flat panel display', 'flats', 'flat tip screwdriver', 'fleece', + 'fleet ballistic missile submarine', 'fleur-de-lis', 'flight simulator', + 'flintlock', 'flintlock', 'flip-flop', 'flipper', 'float', 'floating dock', + 'floatplane', 'flood', 'floor', 'floor', 'floor', 'floorboard', + 'floor cover', 'floor joist', 'floor lamp', 'flophouse', 'florist', 'floss', + 'flotsam', 'flour bin', 'flour mill', 'flowerbed', 'flugelhorn', + 'fluid drive', 'fluid flywheel', 'flume', 'fluorescent lamp', 'fluoroscope', + 'flush toilet', 'flute', 'flute', 'flux applicator', 'fluxmeter', 'fly', + 'flying boat', 'flying buttress', 'flying carpet', 'flying jib', 'fly rod', + 'fly tent', 'flytrap', 'flywheel', 'fob', 'foghorn', 'foglamp', 'foil', + 'fold', 'folder', 'folding chair', 'folding door', 'folding saw', + 'food court', 'food processor', 'food hamper', 'foot', 'footage', + 'football', 'football helmet', 'football stadium', 'footbath', 'foot brake', + 'footbridge', 'foothold', 'footlocker', 'foot rule', 'footstool', + 'footwear', 'footwear', 'forceps', 'force pump', 'fore-and-after', + 'fore-and-aft sail', 'forecastle', 'forecourt', 'foredeck', 'fore edge', + 'foreground', 'foremast', 'fore plane', 'foresail', 'forestay', 'foretop', + 'fore-topmast', 'fore-topsail', 'forge', 'fork', 'forklift', 'formalwear', + 'formica', 'fortification', 'fortress', 'forty-five', 'foucault pendulum', + 'foulard', 'foul-weather gear', 'foundation garment', 'foundry', 'fountain', + 'fountain pen', 'four-in-hand', 'four-poster', 'four-pounder', + 'four-stroke engine', 'four-wheel drive', 'four-wheel drive', + 'four-wheeler', 'fowling piece', 'foxhole', 'fragmentation bomb', 'frail', + 'fraise', 'frame', 'frame', 'frame buffer', 'framework', 'francis turbine', + 'franking machine', 'free house', 'free-reed', 'free-reed instrument', + 'freewheel', 'freight car', 'freight elevator', 'freight liner', + 'freight train', 'french door', 'french horn', 'french polish', + 'french roof', 'french window', 'fresnel lens', 'fret', 'friary', + 'friction clutch', 'frieze', 'frieze', 'frigate', 'frigate', 'frill', + 'frisbee', 'frock', 'frock coat', 'frontlet', 'front porch', + 'front projector', 'fruit machine', 'frying pan', 'fuel filter', + 'fuel gauge', 'fuel injection', 'fuel system', 'full-dress uniform', + 'full metal jacket', 'full skirt', 'fumigator', 'funeral home', 'funnel', + 'funny wagon', 'fur', 'fur coat', 'fur hat', 'furnace', 'furnace lining', + 'furnace room', 'furnishing', 'furnishing', 'furniture', 'fur-piece', + 'furrow', 'fuse', 'fusee drive', 'fuselage', 'fusil', 'fustian', 'futon', + 'gabardine', 'gable', 'gable roof', 'gadgetry', 'gaff', 'gaff', 'gaff', + 'gaffsail', 'gaff topsail', 'gag', 'gaiter', 'gaiter', 'galilean telescope', + 'galleon', 'gallery', 'gallery', 'galley', 'galley', 'galley', 'gallows', + 'gallows tree', 'galvanometer', 'gambling house', 'gambrel', 'game', + 'gamebag', 'game equipment', 'gaming table', 'gamp', 'gangplank', 'gangsaw', + 'gangway', 'gantlet', 'gantry', 'garage', 'garage', 'garand rifle', + 'garbage', 'garbage truck', 'garboard', 'garden', 'garden', 'garden rake', + 'garden spade', 'garden tool', 'garden trowel', 'gargoyle', 'garibaldi', + 'garlic press', 'garment', 'garment bag', 'garrison cap', 'garrote', + 'garter', 'garter belt', 'garter stitch', 'gas guzzler', 'gas shell', + 'gas bracket', 'gas burner', 'gas-cooled reactor', 'gas-discharge tube', + 'gas engine', 'gas fixture', 'gas furnace', 'gas gun', 'gas heater', + 'gas holder', 'gasket', 'gas lamp', 'gas maser', 'gasmask', 'gas meter', + 'gasoline engine', 'gasoline gauge', 'gas oven', 'gas oven', 'gas pump', + 'gas range', 'gas ring', 'gas tank', 'gas thermometer', 'gastroscope', + 'gas turbine', 'gas-turbine ship', 'gat', 'gate', 'gatehouse', + 'gateleg table', 'gatepost', 'gathered skirt', 'gatling gun', 'gauge', + 'gauntlet', 'gauntlet', 'gauze', 'gauze', 'gavel', 'gazebo', 'gear', 'gear', + 'gear', 'gearbox', 'gearing', 'gearset', 'gearshift', 'geiger counter', + 'geiger tube', 'gene chip', 'general-purpose bomb', 'generator', + 'generator', 'generator', 'geneva gown', 'geodesic dome', 'georgette', + 'gharry', 'ghat', 'ghetto blaster', 'gift shop', 'gift wrapping', 'gig', + 'gig', 'gig', 'gig', 'gildhall', 'gill net', 'gilt', 'gimbal', 'gingham', + 'girandole', 'girder', 'girdle', 'glass', 'glass', 'glass cutter', + 'glasses case', 'glebe house', 'glengarry', 'glider', + 'global positioning system', 'glockenspiel', 'glory hole', 'glove', + 'glove compartment', 'glow lamp', 'glow tube', 'glyptic art', 'glyptics', + 'gnomon', 'goal', 'goalmouth', 'goalpost', 'goblet', 'godown', 'goggles', + 'go-kart', 'gold plate', 'golf bag', 'golf ball', 'golfcart', 'golf club', + 'golf-club head', 'golf equipment', 'golf glove', 'golliwog', 'gondola', + 'gong', 'goniometer', 'gordian knot', 'gorget', 'gossamer', 'gothic arch', + 'gouache', 'gouge', 'gourd', 'government building', 'government office', + 'gown', 'gown', 'gown', 'grab', 'grab bag', 'grab bar', 'grace cup', + 'grade separation', 'graduated cylinder', 'graffito', 'gramophone', + 'granary', 'grandfather clock', 'grand piano', 'graniteware', 'granny knot', + 'grape arbor', 'grapnel', 'grapnel', 'grass skirt', 'grate', 'grate', + 'grater', 'graver', 'gravestone', 'gravimeter', 'gravure', 'gravy boat', + 'grey', 'grease-gun', 'greasepaint', 'greasy spoon', 'greatcoat', + 'great hall', 'greave', 'greengrocery', 'greenhouse', 'grenade', 'grid', + 'griddle', 'grill', 'grille', 'grillroom', 'grinder', 'grinding wheel', + 'grindstone', 'gripsack', 'gristmill', 'grocery bag', 'grocery store', + 'grogram', 'groined vault', 'groover', 'grosgrain', 'gros point', 'ground', + 'ground bait', 'ground control', 'ground floor', 'groundsheet', 'g-string', + 'guard', 'guard boat', 'guardroom', 'guardroom', 'guard ship', + 'guard\'s van', 'gueridon', 'guarnerius', 'guesthouse', 'guestroom', + 'guidance system', 'guided missile', 'guided missile cruiser', + 'guided missile frigate', 'guildhall', 'guilloche', 'guillotine', 'guimpe', + 'guimpe', 'guitar', 'guitar pick', 'gulag', 'gun', 'gunboat', + 'gun carriage', 'gun case', 'gun emplacement', 'gun enclosure', 'gunlock', + 'gunnery', 'gunnysack', 'gun pendulum', 'gun room', 'gunsight', + 'gun trigger', 'gurney', 'gusher', 'gusset', 'gusset', 'guy', + 'gymnastic apparatus', 'gym shoe', 'gym suit', 'gymslip', 'gypsy cab', + 'gyrocompass', 'gyroscope', 'gyrostabilizer', 'habergeon', 'habit', 'habit', + 'hacienda', 'hacksaw', 'haft', 'hairbrush', 'haircloth', 'hairdressing', + 'hairnet', 'hairpiece', 'hairpin', 'hair shirt', 'hair slide', 'hair spray', + 'hairspring', 'hair trigger', 'halberd', 'half binding', 'half hatchet', + 'half hitch', 'half track', 'hall', 'hall', 'hall', 'hall of fame', + 'hall of residence', 'hallstand', 'halter', 'halter', 'hame', 'hammer', + 'hammer', 'hammer', 'hammerhead', 'hammock', 'hamper', 'hand', 'handball', + 'handbarrow', 'handbell', 'hand blower', 'handbow', 'hand brake', + 'hand calculator', 'handcar', 'handcart', 'hand cream', 'handcuff', + 'hand drill', 'hand glass', 'hand glass', 'hand grenade', + 'hand-held computer', 'handhold', 'handkerchief', 'handlebar', 'handloom', + 'hand lotion', 'hand luggage', 'hand-me-down', 'hand mower', 'hand pump', + 'handrest', 'handsaw', 'handset', 'hand shovel', 'handspike', 'handstamp', + 'hand throttle', 'hand tool', 'hand towel', 'hand truck', 'handwear', + 'handwheel', 'handwheel', 'hangar queen', 'hanger', 'hang glider', + 'hangman\'s rope', 'hank', 'hansom', 'harbor', 'hard disc', 'hard hat', + 'hardtop', 'hardware', 'hardware store', 'harmonica', 'harmonium', + 'harness', 'harness', 'harp', 'harp', 'harpoon', 'harpoon gun', + 'harpoon log', 'harpsichord', 'harris tweed', 'harrow', 'harvester', + 'hash house', 'hasp', 'hat', 'hatbox', 'hatch', 'hatchback', 'hatchback', + 'hatchel', 'hatchet', 'hatpin', 'hauberk', 'hawaiian guitar', 'hawse', + 'hawser', 'hawser bend', 'hay bale', 'hayfork', 'hayloft', 'haymaker', + 'hayrack', 'hayrack', 'hazard', 'head', 'head', 'head', 'headboard', + 'head covering', 'headdress', 'header', 'header', 'header', 'header', + 'headfast', 'head gasket', 'head gate', 'headgear', 'headlight', + 'headpiece', 'headpin', 'headquarters', 'headrace', 'headrest', 'headsail', + 'headscarf', 'headset', 'head shop', 'headstall', 'headstock', 'health spa', + 'hearing aid', 'hearing aid', 'hearse', 'hearth', 'hearthrug', + 'heart-lung machine', 'heat engine', 'heater', 'heat exchanger', + 'heating pad', 'heat lamp', 'heat pump', 'heat-seeking missile', + 'heat shield', 'heat sink', 'heaume', 'heaver', 'heavier-than-air craft', + 'heckelphone', 'hectograph', 'hedge', 'hedge trimmer', 'helicon', + 'helicopter', 'heliograph', 'heliometer', 'helm', 'helmet', 'helmet', + 'hematocrit', 'hemming-stitch', 'hemostat', 'hemstitch', 'henroost', + 'heraldry', 'hermitage', 'herringbone', 'herringbone', + 'herschelian telescope', 'hessian boot', 'heterodyne receiver', 'hibachi', + 'hideaway', 'hi-fi', 'high altar', 'high-angle gun', 'highball glass', + 'highboard', 'highboy', 'highchair', 'high gear', 'high-hat cymbal', + 'highlighter', 'highlighter', 'high-pass filter', 'high-rise', 'high table', + 'high-warp loom', 'hijab', 'hinge', 'hinging post', 'hip boot', 'hipflask', + 'hip pad', 'hip pocket', 'hippodrome', 'hip roof', 'hitch', 'hitch', + 'hitching post', 'hitchrack', 'hob', 'hobble skirt', 'hockey skate', + 'hockey stick', 'hod', 'hodoscope', 'hoe', 'hoe handle', 'hogshead', + 'hoist', 'hold', 'holder', 'holding cell', 'holding device', 'holding pen', + 'hollowware', 'holster', 'holster', 'holy of holies', 'home', + 'home appliance', 'home computer', 'home plate', 'home room', 'homespun', + 'homestead', 'home theater', 'homing torpedo', 'hone', 'honeycomb', 'hood', + 'hood', 'hood', 'hood', 'hood', 'hood latch', 'hook', 'hook', 'hook', + 'hookah', 'hook and eye', 'hookup', 'hookup', 'hook wrench', 'hoopskirt', + 'hoosegow', 'hoover', 'hope chest', 'hopper', 'hopsacking', + 'horizontal bar', 'horizontal stabilizer', 'horizontal tail', 'horn', + 'horn', 'horn', 'horn button', 'hornpipe', 'horse', 'horsebox', 'horsecar', + 'horse cart', 'horsecloth', 'horse-drawn vehicle', 'horsehair', + 'horsehair wig', 'horseless carriage', 'horse pistol', 'horseshoe', + 'horseshoe', 'horse-trail', 'horsewhip', 'hose', 'hosiery', 'hospice', + 'hospital', 'hospital bed', 'hospital room', 'hospital ship', + 'hospital train', 'hostel', 'hostel', 'hot-air balloon', 'hotel', + 'hotel-casino', 'hotel-casino', 'hotel room', 'hot line', 'hot pants', + 'hot plate', 'hot rod', 'hot spot', 'hot tub', 'hot-water bottle', + 'houndstooth check', 'hourglass', 'hour hand', 'house', 'house', + 'houseboat', 'houselights', 'house of cards', 'house of correction', + 'house paint', 'housetop', 'housing', 'hovel', 'hovercraft', 'howdah', + 'huarache', 'hub-and-spoke', 'hubcap', 'huck', 'hug-me-tight', 'hula-hoop', + 'hulk', 'hull', 'humeral veil', 'humvee', 'hunter', 'hunting knife', + 'hurdle', 'hurricane deck', 'hurricane lamp', 'hut', 'hutch', 'hutment', + 'hydraulic brake', 'hydraulic press', 'hydraulic pump', 'hydraulic system', + 'hydraulic transmission', 'hydroelectric turbine', 'hydrofoil', 'hydrofoil', + 'hydrogen bomb', 'hydrometer', 'hygrodeik', 'hygrometer', 'hygroscope', + 'hyperbaric chamber', 'hypercoaster', 'hypermarket', 'hypodermic needle', + 'hypodermic syringe', 'hypsometer', 'hysterosalpingogram', 'i-beam', + 'ice ax', 'iceboat', 'icebreaker', 'iced-tea spoon', 'ice hockey rink', + 'ice machine', 'ice maker', 'ice pack', 'icepick', 'ice rink', 'ice skate', + 'ice tongs', 'icetray', 'iconoscope', 'identikit', 'idle pulley', 'igloo', + 'ignition coil', 'ignition key', 'ignition switch', 'imaret', + 'immovable bandage', 'impact printer', 'impeller', 'implant', 'implement', + 'impression', 'imprint', 'improvised explosive device', 'impulse turbine', + 'in-basket', 'incendiary bomb', 'incinerator', 'inclined plane', + 'inclinometer', 'inclinometer', 'incrustation', 'incubator', + 'index register', 'indiaman', 'indian club', 'indicator', 'induction coil', + 'inductor', 'industrial watercourse', 'inertial guidance system', + 'inflater', 'inhaler', 'injector', 'ink bottle', 'ink eraser', + 'ink-jet printer', 'inkle', 'inkstand', 'inkwell', 'inlay', + 'inside caliper', 'insole', 'instep', 'instillator', 'institution', + 'instrument', 'instrument of punishment', 'instrument of torture', + 'intaglio', 'intake valve', 'integrated circuit', 'integrator', 'intelnet', + 'interceptor', 'interchange', 'intercommunication system', + 'intercontinental ballistic missile', 'interface', 'interferometer', + 'interior door', 'internal-combustion engine', 'internal drive', 'internet', + 'interphone', 'interrupter', 'intersection', 'interstice', + 'intraocular lens', 'intravenous pyelogram', 'inverter', 'ion engine', + 'ionization chamber', 'ipod', 'video ipod', 'iron', 'iron', 'iron', 'irons', + 'ironclad', 'iron foundry', 'iron horse', 'ironing', 'iron lung', + 'ironmongery', 'ironworks', 'irrigation ditch', 'izar', 'jabot', 'jack', + 'jack', 'jack', 'jack', 'jacket', 'jacket', 'jacket', 'jack-in-the-box', + 'jack-o\'-lantern', 'jack plane', 'jacob\'s ladder', 'jaconet', + 'jacquard loom', 'jacquard', 'jag', 'jail', 'jalousie', 'jamb', 'jammer', + 'jampot', 'japan', 'jar', 'jarvik heart', 'jaunting car', 'javelin', 'jaw', + 'jaws of life', 'jean', 'jeep', 'jellaba', 'jerkin', 'jeroboam', 'jersey', + 'jersey', 'jet', 'jet bridge', 'jet engine', 'jetliner', 'jeweler\'s glass', + 'jewelled headdress', 'jew\'s harp', 'jib', 'jibboom', 'jig', 'jig', + 'jiggermast', 'jigsaw', 'jigsaw puzzle', 'jinrikisha', 'jobcentre', + 'jodhpurs', 'jodhpur', 'joinery', 'joint', 'joint direct attack munition', + 'jointer', 'joist', 'jolly boat', 'jorum', 'joss house', 'journal bearing', + 'journal box', 'joystick', 'jungle gym', 'junk', 'jug', 'jukebox', + 'jumbojet', 'jumper', 'jumper', 'jumper', 'jumper', 'jumper cable', + 'jump seat', 'jump suit', 'jump suit', 'junction', 'junction', + 'junction barrier', 'junk shop', 'jury box', 'jury mast', 'kachina', + 'kaffiyeh', 'kalansuwa', 'kalashnikov', 'kameez', 'kanzu', 'katharometer', + 'kayak', 'kazoo', 'keel', 'keelboat', 'keelson', 'keep', 'keg', 'kennel', + 'kepi', 'keratoscope', 'kerchief', 'ketch', 'kettle', 'kettle', 'key', + 'key', 'keyboard', 'keyboard buffer', 'keyboard instrument', 'keyhole', + 'keyhole saw', 'khadi', 'khaki', 'khakis', 'khimar', 'khukuri', + 'kick pleat', 'kicksorter', 'kickstand', 'kick starter', 'kid glove', + 'kiln', 'kilt', 'kimono', 'kinescope', 'kinetoscope', 'king', 'king', + 'kingbolt', 'king post', 'kipp\'s apparatus', 'kirk', 'kirpan', 'kirtle', + 'kirtle', 'kit', 'kit', 'kitbag', 'kitchen', 'kitchen appliance', + 'kitchenette', 'kitchen table', 'kitchen utensil', 'kitchenware', + 'kite balloon', 'klaxon', 'klieg light', 'klystron', 'knee brace', + 'knee-high', 'knee pad', 'knee piece', 'knife', 'knife', 'knife blade', + 'knight', 'knit', 'knitting machine', 'knitting needle', 'knitwear', 'knob', + 'knob', 'knobble', 'knobkerrie', 'knocker', 'knot', 'knuckle joint', 'kohl', + 'koto', 'kraal', 'kremlin', 'kris', 'krummhorn', 'kundt\'s tube', + 'kurdistan', 'kurta', 'kylix', 'kymograph', 'lab bench', 'lab coat', 'lace', + 'lacquer', 'lacquerware', 'lacrosse ball', 'ladder-back', 'ladder-back', + 'ladder truck', 'ladies\' room', 'ladle', 'lady chapel', 'lagerphone', + 'lag screw', 'lake dwelling', 'lally', 'lamasery', 'lambrequin', 'lame', + 'laminar flow clean room', 'laminate', 'lamination', 'lamp', 'lamp', + 'lamp house', 'lamppost', 'lampshade', 'lanai', 'lancet arch', + 'lancet window', 'landau', 'lander', 'landing craft', 'landing flap', + 'landing gear', 'landing net', 'landing skid', 'land line', 'land mine', + 'land office', 'lanolin', 'lantern', 'lanyard', 'lap', 'laparoscope', + 'lapboard', 'lapel', 'lap joint', 'laptop', 'laryngoscope', 'laser', + 'laser-guided bomb', 'laser printer', 'lash', 'lashing', 'lasso', 'latch', + 'latch', 'latchet', 'latchkey', 'lateen', 'latex paint', 'lath', 'lathe', + 'latrine', 'lattice', 'launch', 'launcher', 'laundry', 'laundry cart', + 'laundry truck', 'lavalava', 'lavaliere', 'laver', 'lawn chair', + 'lawn furniture', 'lawn mower', 'layette', 'lead-acid battery', 'lead-in', + 'leading rein', 'lead pencil', 'leaf spring', 'lean-to', 'lean-to tent', + 'leash', 'leatherette', 'leather strip', 'leclanche cell', 'lectern', + 'lecture room', 'lederhosen', 'ledger board', 'leg', 'leg', 'legging', + 'leiden jar', 'leisure wear', 'lens', 'lens', 'lens cap', 'lens implant', + 'leotard', 'letter case', 'letter opener', 'levee', 'level', 'lever', + 'lever', 'lever', 'lever lock', 'levi\'s', 'liberty ship', 'library', + 'library', 'lid', 'liebig condenser', 'lie detector', 'lifeboat', + 'life buoy', 'life jacket', 'life office', 'life preserver', + 'life-support system', 'life-support system', 'lifting device', 'lift pump', + 'ligament', 'ligature', 'light', 'light arm', 'light bulb', 'light circuit', + 'light-emitting diode', 'lighter', 'lighter-than-air craft', 'light filter', + 'lighting', 'light machine gun', 'light meter', 'light microscope', + 'lightning rod', 'light pen', 'lightship', 'lilo', 'limber', 'limekiln', + 'limiter', 'limousine', 'linear accelerator', 'linen', 'line printer', + 'liner', 'liner', 'lingerie', 'lining', 'link', 'linkage', 'link trainer', + 'linocut', 'linoleum knife', 'linotype', 'linsey-woolsey', 'linstock', + 'lion-jaw forceps', 'lip-gloss', 'lipstick', 'liqueur glass', + 'liquid crystal display', 'liquid metal reactor', 'lisle', 'lister', + 'litterbin', 'little theater', 'live axle', 'living quarters', + 'living room', 'load', 'loafer', 'loaner', 'lobe', 'lobster pot', 'local', + 'local area network', 'local oscillator', 'lochaber ax', 'lock', 'lock', + 'lock', 'lock', 'lockage', 'locker', 'locker room', 'locket', 'lock-gate', + 'locking pliers', 'lockring', 'lockstitch', 'lockup', 'locomotive', 'lodge', + 'lodge', 'lodge', 'lodging house', 'loft', 'loft', 'loft', 'log cabin', + 'loggia', 'longbow', 'long iron', 'long johns', 'long sleeve', 'long tom', + 'long trousers', 'long underwear', 'looking glass', 'lookout', 'loom', + 'loop knot', 'lorgnette', 'lorraine cross', 'lorry', 'lota', 'lotion', + 'loudspeaker', 'lounge', 'lounger', 'lounging jacket', 'lounging pajama', + 'loungewear', 'loupe', 'louvered window', 'love knot', 'love seat', + 'loving cup', 'lowboy', 'low-pass filter', 'low-warp-loom', 'lp', 'l-plate', + 'lubber\'s hole', 'lubricating system', 'luff', 'lug', 'luge', 'luger', + 'luggage carrier', 'luggage compartment', 'luggage rack', 'lugger', + 'lugsail', 'lug wrench', 'lumberjack', 'lumbermill', + 'lunar excursion module', 'lunchroom', 'lunette', 'lungi', 'lunula', + 'lusterware', 'lute', 'luxury liner', 'lyceum', 'lychgate', 'lyre', + 'machete', 'machicolation', 'machine', 'machine', 'machine bolt', + 'machine gun', 'machinery', 'machine screw', 'machine tool', + 'machinist\'s vise', 'machmeter', 'mackinaw', 'mackinaw', 'mackinaw', + 'mackintosh', 'macrame', 'madras', 'mae west', 'magazine rack', + 'magic lantern', 'magnet', 'magnetic bottle', 'magnetic compass', + 'magnetic core memory', 'magnetic disk', 'magnetic head', 'magnetic mine', + 'magnetic needle', 'magnetic recorder', 'magnetic stripe', 'magnetic tape', + 'magneto', 'magnetometer', 'magnetron', 'magnifier', 'magnum', + 'magnus hitch', 'mail', 'mailbag', 'mailbag', 'mailboat', 'mailbox', + 'mail car', 'maildrop', 'mailer', 'maillot', 'maillot', 'mailsorter', + 'mail train', 'mainframe', 'mainmast', 'main rotor', 'mainsail', + 'mainspring', 'main-topmast', 'main-topsail', 'main yard', 'maisonette', + 'majolica', 'makeup', 'maksutov telescope', 'malacca', 'mallet', 'mallet', + 'mallet', 'mammogram', 'mandola', 'mandolin', 'manger', 'mangle', 'manhole', + 'manhole cover', 'man-of-war', 'manometer', 'manor', 'manor hall', 'manpad', + 'mansard', 'manse', 'mansion', 'mantel', 'mantelet', 'mantilla', + 'mao jacket', 'map', 'maquiladora', 'maraca', 'marble', 'marching order', + 'marimba', 'marina', 'marker', 'marketplace', 'marlinespike', 'marocain', + 'marquee', 'marquetry', 'marriage bed', 'martello tower', 'martingale', + 'mascara', 'maser', 'masher', 'mashie', 'mashie niblick', 'masjid', 'mask', + 'mask', 'masonite', 'mason jar', 'masonry', 'mason\'s level', + 'massage parlor', 'massage parlor', 'mass spectrograph', + 'mass spectrometer', 'mast', 'mast', 'mastaba', 'master bedroom', + 'masterpiece', 'mat', 'mat', 'match', 'match', 'matchboard', 'matchbook', + 'matchbox', 'matchlock', 'match plane', 'matchstick', 'material', + 'materiel', 'maternity hospital', 'maternity ward', 'matrix', + 'matthew walker', 'matting', 'mattock', 'mattress cover', 'maul', + 'maulstick', 'mauser', 'mausoleum', 'maxi', 'maxim gun', + 'maximum and minimum thermometer', 'maypole', 'maze', 'mazer', 'means', + 'measure', 'measuring cup', 'measuring instrument', 'measuring stick', + 'meat counter', 'meat grinder', 'meat hook', 'meat house', 'meat safe', + 'meat thermometer', 'mechanical device', 'mechanical piano', + 'mechanical system', 'mechanism', 'medical building', 'medical instrument', + 'medicine ball', 'medicine chest', 'medline', 'megalith', 'megaphone', + 'memorial', 'memory', 'memory chip', 'memory device', 'menagerie', + 'mending', 'menhir', 'menorah', 'menorah', 'man\'s clothing', 'men\'s room', + 'mercantile establishment', 'mercury barometer', 'mercury cell', + 'mercury thermometer', 'mercury-vapor lamp', 'mercy seat', 'merlon', 'mess', + 'mess jacket', 'mess kit', 'messuage', 'metal detector', 'metallic', + 'metal screw', 'metal wood', 'meteorological balloon', 'meter', + 'meterstick', 'metronome', 'mezzanine', 'mezzanine', 'microbalance', + 'microbrewery', 'microfiche', 'microfilm', 'micrometer', 'microphone', + 'microprocessor', 'microscope', 'microtome', 'microwave', + 'microwave diathermy machine', 'microwave linear accelerator', 'middy', + 'midiron', 'mihrab', 'mihrab', 'military hospital', 'military quarters', + 'military uniform', 'military vehicle', 'milk bar', 'milk can', + 'milk float', 'milking machine', 'milking stool', 'milk wagon', 'mill', + 'milldam', 'miller', 'milliammeter', 'millinery', 'millinery', 'milling', + 'millivoltmeter', 'millstone', 'millstone', 'millwheel', 'mimeograph', + 'minaret', 'mincer', 'mine', 'mine detector', 'minelayer', 'mineshaft', + 'minibar', 'minibike', 'minibus', 'minicar', 'minicomputer', 'ministry', + 'miniskirt', 'minisub', 'minivan', 'miniver', 'mink', 'minster', 'mint', + 'minute hand', 'minuteman', 'mirror', 'missile', 'missile defense system', + 'miter box', 'miter joint', 'mitten', 'mixer', 'mixer', 'mixing bowl', + 'mixing faucet', 'mizzen', 'mizzenmast', 'mobcap', 'mobile home', + 'moccasin', 'mock-up', 'mod con', 'model t', 'modem', 'modillion', 'module', + 'module', 'mohair', 'moire', 'mold', 'moldboard', 'moldboard plow', + 'moleskin', 'molotov cocktail', 'monastery', 'monastic habit', 'moneybag', + 'money belt', 'monitor', 'monitor', 'monitor', 'monkey-wrench', + 'monk\'s cloth', 'monochrome', 'monocle', 'monofocal lens implant', + 'monoplane', 'monotype', 'monstrance', 'mooring tower', 'moorish arch', + 'moped', 'mop handle', 'moquette', 'morgue', 'morion', 'morning dress', + 'morning dress', 'morning room', 'morris chair', 'mortar', 'mortar', + 'mortarboard', 'mortise joint', 'mosaic', 'mosque', 'mosquito net', 'motel', + 'motel room', 'mother hubbard', 'motion-picture camera', + 'motion-picture film', 'motley', 'motley', 'motor', 'motorboat', + 'motorcycle', 'motor hotel', 'motorized wheelchair', 'motor scooter', + 'motor vehicle', 'mound', 'mound', 'mount', 'mountain bike', + 'mountain tent', 'mouse', 'mouse button', 'mousetrap', 'mousse', + 'mouthpiece', 'mouthpiece', 'mouthpiece', 'movement', 'movie projector', + 'moving-coil galvanometer', 'moving van', 'mud brick', 'mudguard', 'mudhif', + 'muff', 'muffle', 'muffler', 'mufti', 'mug', 'mulch', 'mule', + 'multichannel recorder', 'multiengine airplane', 'multiplex', 'multiplexer', + 'multiprocessor', 'multistage rocket', 'munition', 'murphy bed', 'musette', + 'musette pipe', 'museum', 'mushroom anchor', 'musical instrument', + 'music box', 'music hall', 'music school', 'music stand', 'music stool', + 'musket', 'musket ball', 'muslin', 'mustache cup', 'mustard plaster', + 'mute', 'muzzle loader', 'muzzle', 'myelogram', 'nacelle', 'nail', + 'nailbrush', 'nailfile', 'nailhead', 'nailhead', 'nail polish', 'nainsook', + 'napier\'s bones', 'nard', 'narrowbody aircraft', 'narrow wale', 'narthex', + 'narthex', 'nasotracheal tube', 'national monument', 'nautilus', + 'navigational system', 'naval equipment', 'naval gun', 'naval missile', + 'naval radar', 'naval tactical data system', 'naval weaponry', 'nave', + 'navigational instrument', 'nebuchadnezzar', 'neckband', 'neck brace', + 'neckcloth', 'neckerchief', 'necklace', 'necklet', 'neckline', 'neckpiece', + 'necktie', 'neckwear', 'needle', 'needle', 'needlenose pliers', + 'needlework', 'negative', 'negative magnetic pole', 'negative pole', + 'negligee', 'neolith', 'neon lamp', 'nephoscope', 'nest', 'nest egg', 'net', + 'net', 'net', 'net', 'network', 'network', 'neutron bomb', 'newel', + 'newel post', 'newspaper', 'newsroom', 'newsroom', 'newsstand', + 'newtonian telescope', 'nib', 'niblick', 'nicad', 'nickel-iron battery', + 'nicol prism', 'night bell', 'nightcap', 'nightgown', 'night latch', + 'night-light', 'nightshirt', 'nightwear', 'ninepin', 'ninepin ball', + 'ninon', 'nipple', 'nipple shield', 'niqab', 'nissen hut', 'nogging', + 'noisemaker', 'nonsmoker', 'non-volatile storage', 'norfolk jacket', + 'noria', 'nosebag', 'noseband', 'nose flute', 'nosewheel', 'notebook', + 'nuclear-powered ship', 'nuclear reactor', 'nuclear rocket', + 'nuclear weapon', 'nude', 'numdah', 'nun\'s habit', 'nursery', + 'nut and bolt', 'nutcracker', 'nylon', 'nylons', 'oar', 'oast', + 'oast house', 'obelisk', 'object ball', 'objective', 'oblique bandage', + 'oboe', 'oboe da caccia', 'oboe d\'amore', 'observation dome', + 'observatory', 'obstacle', 'obturator', 'ocarina', 'octant', + 'odd-leg caliper', 'odometer', 'oeil de boeuf', 'office', 'office building', + 'office furniture', 'officer\'s mess', 'off-line equipment', 'ogee', + 'ogee arch', 'ohmmeter', 'oil', 'oilcan', 'oilcloth', 'oil filter', + 'oil heater', 'oil lamp', 'oil paint', 'oil pump', 'oil refinery', + 'oilskin', 'oil slick', 'oilstone', 'oil tanker', 'old school tie', + 'olive drab', 'olive drab', 'olympian zeus', 'omelet pan', + 'omnidirectional antenna', 'omnirange', 'onion dome', 'open-air market', + 'open circuit', 'open-end wrench', 'opener', 'open-hearth furnace', + 'openside plane', 'open sight', 'openwork', 'opera', 'opera cloak', + 'operating microscope', 'operating room', 'operating table', + 'ophthalmoscope', 'optical device', 'optical disk', 'optical instrument', + 'optical pyrometer', 'optical telescope', 'orchestra pit', 'ordinary', + 'organ', 'organdy', 'organic light-emitting diode', 'organ loft', + 'organ pipe', 'organza', 'oriel', 'oriflamme', 'o ring', 'orlon', + 'orlop deck', 'orphanage', 'orphrey', 'orrery', 'orthicon', + 'orthochromatic film', 'orthopter', 'orthoscope', 'oscillograph', + 'oscilloscope', 'ossuary', 'otoscope', 'ottoman', 'oubliette', 'out-basket', + 'outboard motor', 'outboard motorboat', 'outbuilding', 'outerwear', + 'outfall', 'outfit', 'outfitter', 'outhouse', 'output device', 'outrigger', + 'outrigger canoe', 'outside caliper', 'outside mirror', 'outwork', 'oven', + 'oven thermometer', 'overall', 'overall', 'overcoat', 'overdrive', + 'overgarment', 'overhand knot', 'overhang', 'overhead projector', + 'overmantel', 'overnighter', 'overpass', 'override', 'overshoe', + 'overskirt', 'oxbow', 'oxbridge', 'oxcart', 'oxeye', 'oxford', 'oximeter', + 'oxyacetylene torch', 'oxygen mask', 'oyster bar', 'oyster bed', 'pace car', + 'pacemaker', 'pack', 'pack', 'pack', 'package', 'package store', + 'packaging', 'packet', 'packing box', 'packinghouse', 'packinghouse', + 'packing needle', 'packsaddle', 'paddle', 'paddle', 'paddle', 'paddle box', + 'paddle steamer', 'paddlewheel', 'paddock', 'padlock', 'page printer', + 'paint', 'paintball', 'paintball gun', 'paintbox', 'paintbrush', 'paisley', + 'pajama', 'pajama', 'palace', 'palace', 'palace', 'palanquin', 'paleolith', + 'palestra', 'palette', 'palette knife', 'palisade', 'pallet', 'pallette', + 'pallium', 'pallium', 'pan', 'pan', 'pancake turner', 'panchromatic film', + 'panda car', 'paneling', 'panhandle', 'panic button', 'pannier', 'pannier', + 'pannikin', 'panopticon', 'panopticon', 'panpipe', 'pantaloon', + 'pantechnicon', 'pantheon', 'pantheon', 'pantie', 'panting', 'pant leg', + 'pantograph', 'pantry', 'pants suit', 'panty girdle', 'pantyhose', 'panzer', + 'paper chain', 'paper clip', 'paper cutter', 'paper fastener', 'paper feed', + 'paper mill', 'paper towel', 'parabolic mirror', 'parabolic reflector', + 'parachute', 'parallel bars', 'parallel circuit', 'parallel interface', + 'parang', 'parapet', 'parapet', 'parasail', 'parasol', 'parer', + 'parfait glass', 'pargeting', 'pari-mutuel machine', 'parka', 'park bench', + 'parking meter', 'parlor', 'parquet', 'parquetry', 'parsonage', + 'parsons table', 'partial denture', 'particle detector', 'partition', + 'parts bin', 'party line', 'party wall', 'parvis', 'passenger car', + 'passenger ship', 'passenger train', 'passenger van', 'passe-partout', + 'passive matrix display', 'passkey', 'pass-through', 'pastry cart', 'patch', + 'patchcord', 'patchouli', 'patch pocket', 'patchwork', 'patent log', + 'paternoster', 'patina', 'patio', 'patisserie', 'patka', 'patrol boat', + 'patty-pan', 'pave', 'pavilion', 'pavior', 'pavis', 'pawn', + 'pawnbroker\'s shop', 'pay-phone', 'pc board', 'peach orchard', + 'pea jacket', 'peavey', 'pectoral', 'pedal', 'pedal pusher', 'pedestal', + 'pedestal table', 'pedestrian crossing', 'pedicab', 'pediment', 'pedometer', + 'peeler', 'peep sight', 'peg', 'peg', 'peg', 'peg', 'pegboard', 'pelham', + 'pelican crossing', 'pelisse', 'pelvimeter', 'pen', 'penal colony', + 'penal institution', 'penalty box', 'pen-and-ink', 'pencil', 'pencil', + 'pencil box', 'pencil sharpener', 'pendant earring', 'pendulum', + 'pendulum clock', 'pendulum watch', 'penetration bomb', 'penile implant', + 'penitentiary', 'penknife', 'penlight', 'pennant', 'pennywhistle', + 'penthouse', 'pentode', 'peplos', 'peplum', 'pepper mill', 'pepper shaker', + 'pepper spray', 'percale', 'percolator', 'percussion cap', + 'percussion instrument', 'perforation', 'perfume', 'perfumery', 'perfumery', + 'perfumery', 'peripheral', 'periscope', 'peristyle', 'periwig', + 'permanent press', 'perpetual motion machine', 'personal computer', + 'personal digital assistant', 'personnel carrier', 'pestle', 'pestle', + 'petcock', 'petri dish', 'petrolatum gauze', 'pet shop', 'petticoat', 'pew', + 'phial', 'phillips screw', 'phillips screwdriver', 'phonograph needle', + 'phonograph record', 'photocathode', 'photocoagulator', 'photocopier', + 'photographic equipment', 'photographic paper', 'photometer', + 'photomicrograph', 'photostat', 'photostat', 'physical pendulum', 'piano', + 'piano action', 'piano keyboard', 'piano wire', 'piccolo', 'pick', 'pick', + 'pick', 'pickelhaube', 'picket boat', 'picket fence', 'picket ship', + 'pickle barrel', 'pickup', 'picture', 'picture frame', 'picture hat', + 'picture rail', 'picture window', 'piece of cloth', 'pied-a-terre', 'pier', + 'pier', 'pier arch', 'pier glass', 'pier table', 'pieta', 'piezometer', + 'pig bed', 'piggery', 'piggy bank', 'pilaster', 'pile', 'pile driver', + 'pill bottle', 'pillbox', 'pillion', 'pillory', 'pillow', 'pillow block', + 'pillow lace', 'pillow sham', 'pilot bit', 'pilot boat', 'pilot burner', + 'pilot cloth', 'pilot engine', 'pilothouse', 'pilot light', 'pin', 'pin', + 'pin', 'pinata', 'pinball machine', 'pince-nez', 'pincer', 'pinch bar', + 'pincurl clip', 'pinfold', 'ping-pong ball', 'pinhead', 'pinion', + 'pinnacle', 'pinprick', 'pinstripe', 'pinstripe', 'pinstripe', 'pintle', + 'pinwheel', 'pinwheel', 'tabor pipe', 'pipe', 'pipe bomb', 'pipe cleaner', + 'pipe cutter', 'pipefitting', 'pipet', 'pipe vise', 'pipe wrench', 'pique', + 'pirate', 'piste', 'pistol', 'pistol grip', 'piston', 'piston ring', + 'piston rod', 'pit', 'pitcher', 'pitchfork', 'pitching wedge', 'pitch pipe', + 'pith hat', 'piton', 'pitot-static tube', 'pitot tube', 'pitsaw', 'pivot', + 'pivoting window', 'pizzeria', 'place of business', 'place of worship', + 'placket', 'planchet', 'plane', 'plane', 'plane seat', 'planetarium', + 'planetarium', 'planetarium', 'planetary gear', 'plank-bed', 'planking', + 'planner', 'plant', 'planter', 'plaster', 'plasterboard', + 'plastering trowel', 'plastic bag', 'plastic bomb', 'plastic laminate', + 'plastic wrap', 'plastron', 'plastron', 'plastron', 'plate', 'plate', + 'plate', 'platen', 'platen', 'plate rack', 'plate rail', 'platform', + 'platform', 'platform', 'platform bed', 'platform rocker', 'plating', + 'platter', 'playback', 'playbox', 'playground', 'playpen', 'playsuit', + 'plaza', 'pleat', 'plenum', 'plethysmograph', 'pleximeter', 'plexor', + 'pliers', 'plimsoll', 'plotter', 'plow', 'plug', 'plug', 'plug fuse', + 'plughole', 'plumb bob', 'plumb level', 'plunger', 'plus fours', 'plush', + 'plywood', 'pneumatic drill', 'p-n junction', 'p-n-p transistor', 'poacher', + 'pocket', 'pocket battleship', 'pocketcomb', 'pocket flap', + 'pocket-handkerchief', 'pocketknife', 'pocket watch', 'pod', 'pogo stick', + 'point-and-shoot camera', 'pointed arch', 'pointing trowel', 'point lace', + 'poker', 'polarimeter', 'polaroid', 'polaroid camera', 'pole', 'pole', + 'poleax', 'poleax', 'police boat', 'police van', 'polling booth', + 'polo ball', 'polo mallet', 'polonaise', 'polo shirt', 'polyester', + 'polygraph', 'pomade', 'pommel horse', 'poncho', 'pongee', 'poniard', + 'pontifical', 'pontoon', 'pontoon bridge', 'pony cart', 'pool ball', + 'poolroom', 'pool table', 'poop deck', 'poor box', 'poorhouse', + 'pop bottle', 'popgun', 'poplin', 'popper', 'poppet', 'pop tent', + 'porcelain', 'porch', 'porkpie', 'porringer', 'portable', + 'portable computer', 'portable circular saw', 'portcullis', 'porte-cochere', + 'porte-cochere', 'portfolio', 'porthole', 'portico', 'portiere', + 'portmanteau', 'portrait camera', 'portrait lens', 'positive pole', + 'positive pole', 'positron emission tomography scanner', 'post', + 'postage meter', 'post and lintel', 'post chaise', 'postern', + 'post exchange', 'posthole digger', 'post horn', 'posthouse', 'pot', 'pot', + 'potbelly', 'potemkin village', 'potential divider', 'potentiometer', + 'potentiometer', 'potpourri', 'potsherd', 'potter\'s wheel', 'pottery', + 'pottle', 'potty seat', 'pouch', 'poultice', 'pound', 'pound net', 'powder', + 'powder and shot', 'powdered mustard', 'powder horn', 'powder keg', + 'power brake', 'power cord', 'power drill', 'power line', 'power loom', + 'power mower', 'power pack', 'power saw', 'power shovel', 'power steering', + 'power takeoff', 'power tool', 'praetorium', 'prayer rug', 'prayer shawl', + 'precipitator', 'prefab', 'presbytery', 'presence chamber', 'press', + 'press', 'press', 'press box', 'press gallery', 'press of sail', + 'pressure cabin', 'pressure cooker', 'pressure dome', 'pressure gauge', + 'pressurized water reactor', 'pressure suit', 'pricket', 'prie-dieu', + 'primary coil', 'primus stove', 'prince albert', 'print', 'print buffer', + 'printed circuit', 'printer', 'printer', 'printer cable', 'priory', + 'prison', 'prison camp', 'privateer', 'private line', 'privet hedge', + 'probe', 'proctoscope', 'prod', 'production line', 'projectile', + 'projector', 'projector', 'prolonge', 'prolonge knot', 'prompter', 'prong', + 'propeller', 'propeller plane', 'propjet', 'proportional counter tube', + 'propulsion system', 'proscenium', 'proscenium arch', 'prosthesis', + 'protective covering', 'protective garment', 'proton accelerator', + 'protractor', 'pruner', 'pruning knife', 'pruning saw', 'pruning shears', + 'psaltery', 'psychrometer', 'pt boat', 'public address system', + 'public house', 'public toilet', 'public transport', 'public works', 'puck', + 'pull', 'pullback', 'pull chain', 'pulley', 'pull-off', 'pullman', + 'pullover', 'pull-through', 'pulse counter', 'pulse generator', + 'pulse timing circuit', 'pump', 'pump', 'pump action', 'pump house', + 'pump room', 'pump-type pliers', 'pump well', 'punch', 'punchboard', + 'punch bowl', 'punching bag', 'punch pliers', 'punch press', 'punnet', + 'punt', 'pup tent', 'purdah', 'purifier', 'purl', 'purse', 'push-bike', + 'push broom', 'push button', 'push-button radio', 'pusher', 'put-put', + 'puttee', 'putter', 'putty knife', 'puzzle', 'pylon', 'pylon', + 'pyramidal tent', 'pyrograph', 'pyrometer', 'pyrometric cone', 'pyrostat', + 'pyx', 'pyx', 'pyxis', 'quad', 'quadrant', 'quadraphony', 'quartering', + 'quarterstaff', 'quartz battery', 'quartz lamp', 'queen', 'queen', + 'queen post', 'quern', 'quill', 'quilt', 'quilted bedspread', 'quilting', + 'quipu', 'quirk molding', 'quirt', 'quiver', 'quoin', 'quoit', + 'qwerty keyboard', 'rabbet', 'rabbet joint', 'rabbit ears', 'rabbit hutch', + 'raceabout', 'racer', 'raceway', 'racing boat', 'racing gig', + 'racing skiff', 'rack', 'rack', 'rack', 'rack and pinion', 'racket', + 'racquetball', 'radar', 'radial', 'radial engine', 'radiation pyrometer', + 'radiator', 'radiator', 'radiator cap', 'radiator hose', 'radio', + 'radio antenna', 'radio chassis', 'radio compass', 'radiogram', + 'radio interferometer', 'radio link', 'radiometer', 'radiomicrometer', + 'radio-phonograph', 'radio receiver', 'radiotelegraph', 'radiotelephone', + 'radio telescope', 'radiotherapy equipment', 'radio transmitter', 'radome', + 'raft', 'rafter', 'raft foundation', 'rag', 'ragbag', 'raglan', + 'raglan sleeve', 'rail', 'rail fence', 'railhead', 'railing', 'railing', + 'railroad bed', 'railroad tunnel', 'rain barrel', 'raincoat', 'rain gauge', + 'rain stick', 'rake', 'rake handle', 'ram disk', 'ramekin', 'ramjet', + 'rammer', 'ramp', 'rampant arch', 'rampart', 'ramrod', 'ramrod', 'ranch', + 'ranch house', 'random-access memory', 'rangefinder', 'range hood', + 'range pole', 'rapier', 'rariora', 'rasp', 'ratchet', 'ratchet wheel', + 'rathskeller', 'ratline', 'rat-tail file', 'rattan', 'rattrap', 'rayon', + 'razor', 'razorblade', 'reaction-propulsion engine', 'reaction turbine', + 'reactor', 'reading lamp', 'reading room', 'read-only memory', + 'read-only memory chip', 'readout', 'read/write head', 'ready-to-wear', + 'real storage', 'reamer', 'reamer', 'rearview mirror', + 'reaumur thermometer', 'rebozo', 'receiver', 'receptacle', 'reception desk', + 'reception room', 'recess', 'reciprocating engine', 'recliner', + 'reconnaissance plane', 'reconnaissance vehicle', 'record changer', + 'recorder', 'recording', 'recording system', 'record player', + 'record sleeve', 'recovery room', 'recreational vehicle', 'recreation room', + 'recycling bin', 'recycling plant', 'redbrick university', 'red carpet', + 'redoubt', 'redoubt', 'reduction gear', 'reed pipe', 'reed stop', + 'reef knot', 'reel', 'reel', 'refectory', 'refectory table', 'refinery', + 'reflecting telescope', 'reflectometer', 'reflector', 'reflex camera', + 'reflux condenser', 'reformatory', 'reformer', 'refracting telescope', + 'refractometer', 'refrigeration system', 'refrigerator', 'refrigerator car', + 'refuge', 'regalia', 'regimentals', 'regulator', 'rein', 'relay', 'release', + 'religious residence', 'reliquary', 'remote control', 'remote terminal', + 'removable disk', 'rendering', 'rep', 'repair shop', 'repeater', + 'repeating firearm', 'repository', 'reproducer', 'rerebrace', + 'rescue equipment', 'research center', 'reseau', 'reservoir', 'reset', + 'reset button', 'residence', 'resistance pyrometer', 'resistor', + 'resonator', 'resonator', 'resort hotel', 'respirator', 'restaurant', + 'rest house', 'restraint', 'resuscitator', 'retainer', 'retaining wall', + 'reticle', 'reticulation', 'reticule', 'retort', 'retractor', 'return key', + 'reverberatory furnace', 'revers', 'reverse', 'reversible', 'revetment', + 'revetment', 'revolver', 'revolving door', 'rheometer', 'rheostat', + 'rhinoscope', 'rib', 'riband', 'ribbed vault', 'ribbing', + 'ribbon development', 'rib joint pliers', 'ricer', 'riddle', 'ride', + 'ridge', 'ridge rope', 'riding boot', 'riding crop', 'riding mower', + 'rifle', 'rifle ball', 'rifle grenade', 'rig', 'rigger', 'rigger', + 'rigging', 'rigout', 'ringlet', 'rings', 'rink', 'riot gun', 'ripcord', + 'ripcord', 'ripping bar', 'ripping chisel', 'ripsaw', 'riser', 'riser', + 'ritz', 'river boat', 'rivet', 'riveting machine', 'roach clip', 'road', + 'roadbed', 'roadblock', 'roadhouse', 'roadster', 'roadway', 'roaster', + 'robe', 'robotics equipment', 'rochon prism', 'rock bit', 'rocker', + 'rocker', 'rocker arm', 'rocket', 'rocket', 'rocking chair', 'rod', 'rodeo', + 'roll', 'roller', 'roller', 'roller bandage', 'in-line skate', + 'rollerblade', 'roller blind', 'roller coaster', 'roller skate', + 'roller towel', 'roll film', 'rolling hitch', 'rolling mill', 'rolling pin', + 'rolling stock', 'roll-on', 'roll-on', 'roll-on roll-off', 'rolodex', + 'roman arch', 'roman building', 'romper', 'rood screen', 'roof', 'roof', + 'roofing', 'room', 'roomette', 'room light', 'roost', 'rope', 'rope bridge', + 'rope tow', 'rose water', 'rose window', 'rosin bag', 'rotary actuator', + 'rotary engine', 'rotary press', 'rotating mechanism', 'rotating shaft', + 'rotisserie', 'rotisserie', 'rotor', 'rotor', 'rotor', 'rotor blade', + 'rotor head', 'rotunda', 'rotunda', 'rouge', 'roughcast', 'rouleau', + 'roulette', 'roulette ball', 'roulette wheel', 'round', 'round arch', + 'round-bottom flask', 'roundel', 'round file', 'roundhouse', 'router', + 'router', 'router plane', 'rowel', 'row house', 'rowing boat', + 'rowlock arch', 'royal', 'royal mast', 'rubber band', 'rubber boot', + 'rubber bullet', 'rubber eraser', 'rudder', 'rudder', 'rudder blade', 'rug', + 'rugby ball', 'ruin', 'rule', 'rumble', 'rumble seat', 'rummer', + 'rumpus room', 'runcible spoon', 'rundle', 'running shoe', 'running suit', + 'runway', 'rushlight', 'russet', 'rya', 'saber', 'saber saw', 'sable', + 'sable', 'sable coat', 'sabot', 'sachet', 'sack', 'sack', 'sackbut', + 'sackcloth', 'sackcloth', 'sack coat', 'sacking', 'saddle', 'saddlebag', + 'saddle blanket', 'saddle oxford', 'saddlery', 'saddle seat', + 'saddle stitch', 'safe', 'safe', 'safe-deposit', 'safe house', + 'safety arch', 'safety belt', 'safety bicycle', 'safety bolt', + 'safety curtain', 'safety fuse', 'safety lamp', 'safety match', + 'safety net', 'safety pin', 'safety rail', 'safety razor', 'safety valve', + 'sail', 'sail', 'sailboat', 'sailcloth', 'sailing vessel', + 'sailing warship', 'sailor cap', 'sailor suit', 'salad bar', 'salad bowl', + 'salinometer', 'sallet', 'salon', 'salon', 'salon', 'saltbox', 'saltcellar', + 'saltshaker', 'saltworks', 'salver', 'salwar', 'sam browne belt', 'samisen', + 'samite', 'samovar', 'sampan', 'sandal', 'sandbag', 'sandblaster', + 'sandbox', 'sandglass', 'sand wedge', 'sandwich board', 'sanitary napkin', + 'cling film', 'sarcenet', 'sarcophagus', 'sari', 'sarong', 'sash', + 'sash fastener', 'sash window', 'satchel', 'sateen', 'satellite', + 'satellite receiver', 'satellite television', 'satellite transmitter', + 'satin', 'saturday night special', 'saucepan', 'saucepot', 'sauna', + 'savings bank', 'saw', 'sawed-off shotgun', 'sawhorse', 'sawmill', + 'saw set', 'sax', 'saxhorn', 'scabbard', 'scaffolding', 'scale', 'scale', + 'scaler', 'scaling ladder', 'scalpel', 'scanner', 'scanner', 'scanner', + 'scantling', 'scarf', 'scarf joint', 'scatter rug', 'scauper', + 'schmidt telescope', 'school', 'schoolbag', 'school bell', 'school bus', + 'school ship', 'school system', 'schooner', 'schooner', + 'scientific instrument', 'scimitar', 'scintillation counter', 'scissors', + 'sclerometer', 'scoinson arch', 'sconce', 'sconce', 'scoop', 'scooter', + 'scoreboard', 'scouring pad', 'scow', 'scow', 'scraper', 'scratcher', + 'screen', 'screen', 'screen', 'screen', 'screen door', 'screening', 'screw', + 'screw', 'screw', 'screwdriver', 'screw eye', 'screw key', 'screw thread', + 'screwtop', 'screw wrench', 'scriber', 'scrim', 'scrimshaw', 'scriptorium', + 'scrubber', 'scrub brush', 'scrub plane', 'scuffer', 'scuffle', 'scull', + 'scull', 'scullery', 'sculpture', 'scuttle', 'scyphus', 'scythe', 'seabag', + 'sea boat', 'sea chest', 'sealing wax', 'sealskin', 'seam', 'seaplane', + 'searchlight', 'searing iron', 'seat', 'seat', 'seat', 'seat belt', + 'secateurs', 'secondary coil', 'second balcony', 'second base', + 'second hand', 'secretary', 'sectional', 'security blanket', + 'security system', 'security system', 'sedan', 'sedan', 'seeder', 'seeker', + 'seersucker', 'segmental arch', 'segway', 'seidel', 'seine', 'seismograph', + 'selector', 'selenium cell', 'self-propelled vehicle', + 'self-registering thermometer', 'self-starter', 'selsyn', 'selvage', + 'semaphore', 'semiautomatic firearm', 'semiautomatic pistol', + 'semiconductor device', 'semi-detached house', 'semigloss', 'semitrailer', + 'sennit', 'sensitometer', 'sentry box', 'separate', 'septic tank', + 'sequence', 'sequencer', 'serape', 'serge', 'serger', 'serial port', + 'serpent', 'serration', 'server', 'server', 'service club', 'serving cart', + 'serving dish', 'servo', 'set', 'set gun', 'setscrew', 'setscrew', + 'set square', 'settee', 'settle', 'settlement house', 'seventy-eight', + 'seven wonders of the ancient world', 'sewage disposal plant', 'sewer', + 'sewing basket', 'sewing kit', 'sewing machine', 'sewing needle', + 'sewing room', 'sextant', 'sgraffito', 'shackle', 'shackle', 'shade', + 'shadow box', 'shaft', 'shag rug', 'shaker', 'shank', 'shank', 'shantung', + 'shaper', 'shaping tool', 'sharkskin', 'sharpener', 'sharpie', 'shaver', + 'shaving brush', 'shaving cream', 'shaving foam', 'shawl', 'shawm', + 'shears', 'sheath', 'sheathing', 'shed', 'sheep bell', 'sheepshank', + 'sheepskin coat', 'sheepwalk', 'sheet', 'sheet bend', 'sheeting', + 'sheet pile', 'sheetrock', 'shelf', 'shelf bracket', 'shell', 'shell', + 'shell', 'shellac', 'shelter', 'shelter', 'shelter', 'sheltered workshop', + 'sheraton', 'shield', 'shield', 'shielding', 'shift key', 'shillelagh', + 'shim', 'shingle', 'shin guard', 'ship', 'shipboard system', 'shipping', + 'shipping room', 'ship-towed long-range acoustic detection system', + 'shipwreck', 'shirt', 'shirt button', 'shirtdress', 'shirtfront', + 'shirting', 'shirtsleeve', 'shirttail', 'shirtwaist', 'shiv', + 'shock absorber', 'shoe', 'shoe', 'shoebox', 'shoehorn', 'shoe shop', + 'shoetree', 'shofar', 'shoji', 'shooting brake', 'shooting lodge', + 'shooting stick', 'shop', 'shop bell', 'shopping bag', 'shopping basket', + 'shopping cart', 'short circuit', 'short iron', 'short pants', + 'short sleeve', 'shortwave diathermy machine', 'shot', 'shot glass', + 'shotgun', 'shotgun shell', 'shot tower', 'shoulder', 'shoulder bag', + 'shouldered arch', 'shoulder holster', 'shoulder pad', 'shoulder patch', + 'shovel', 'shovel', 'shovel hat', 'showboat', 'shower', 'shower cap', + 'shower curtain', 'shower room', 'shower stall', 'showroom', 'shrapnel', + 'shredder', 'shrimper', 'shrine', 'shrink-wrap', 'shunt', 'shunt', + 'shunter', 'shutter', 'shutter', 'shuttle', 'shuttle', 'shuttle bus', + 'shuttlecock', 'shuttle helicopter', 'sibley tent', 'sickbay', 'sickbed', + 'sickle', 'sickroom', 'sideboard', 'sidecar', 'side chapel', 'sidelight', + 'sidesaddle', 'sidewalk', 'sidewall', 'side-wheeler', 'sidewinder', 'sieve', + 'sifter', 'sights', 'sigmoidoscope', 'signal box', 'signaling device', + 'signboard', 'silencer', 'silent butler', 'silex', 'silk', 'silks', 'silo', + 'silver plate', 'silverpoint', 'simple pendulum', 'simulator', 'single bed', + 'single-breasted jacket', 'single-breasted suit', 'single prop', + 'single-reed instrument', 'single-rotor helicopter', 'singlestick', + 'singlet', 'siren', 'sister ship', 'sitar', 'sitz bath', 'six-pack', + 'skate', 'skateboard', 'skeg', 'skein', 'skeleton', 'skeleton key', 'skep', + 'skep', 'sketch', 'sketcher', 'skew arch', 'skewer', 'ski', 'ski binding', + 'skibob', 'ski boot', 'ski cap', 'skidder', 'skid lid', 'skiff', 'ski jump', + 'ski lodge', 'ski mask', 'skimmer', 'ski parka', 'ski-plane', 'ski pole', + 'ski rack', 'skirt', 'skirt', 'ski tow', 'skivvies', 'skullcap', 'skybox', + 'skyhook', 'skylight', 'skysail', 'skyscraper', 'skywalk', 'slacks', + 'slack suit', 'slasher', 'slash pocket', 'slat', 'slate', 'slate pencil', + 'slate roof', 'sled', 'sleeper', 'sleeper', 'sleeping bag', 'sleeping car', + 'sleeve', 'sleeve', 'sleigh bed', 'sleigh bell', 'slice bar', 'slicer', + 'slicer', 'slide', 'slide fastener', 'slide projector', 'slide rule', + 'slide valve', 'sliding door', 'sliding seat', 'sliding window', 'sling', + 'sling', 'slingback', 'slinger ring', 'slip clutch', 'slipcover', + 'slip-joint pliers', 'slipknot', 'slip-on', 'slipper', 'slip ring', + 'slit lamp', 'slit trench', 'sloop', 'sloop of war', 'slop basin', + 'slop pail', 'slops', 'slopshop', 'slot', 'slot machine', 'sluice', 'smack', + 'small boat', 'small computer system interface', 'small ship', + 'small stores', 'smart bomb', 'smelling bottle', 'smocking', 'smoke bomb', + 'smokehouse', 'smoker', 'smoke screen', 'smoking room', 'smoothbore', + 'smooth plane', 'snack bar', 'snaffle', 'snap', 'snap brim', + 'snap-brim hat', 'snare', 'snare drum', 'snatch block', 'snifter', + 'sniper rifle', 'snips', 'sno-cat', 'snood', 'snorkel', 'snorkel', + 'snowbank', 'snowboard', 'snowmobile', 'snowplow', 'snowshoe', 'snowsuit', + 'snow thrower', 'snuffbox', 'snuffer', 'snuffers', 'soapbox', 'soap dish', + 'soap dispenser', 'soap pad', 'soccer ball', 'sock', 'socket', + 'socket wrench', 'socle', 'soda can', 'soda fountain', 'soda fountain', + 'sod house', 'sodium-vapor lamp', 'sofa', 'soffit', 'softball', + 'soft pedal', 'soil pipe', 'solar array', 'solar cell', 'solar dish', + 'solar heater', 'solar house', 'solar telescope', 'solar thermal system', + 'soldering iron', 'solenoid', 'solleret', 'sombrero', 'sonic depth finder', + 'sonogram', 'sonograph', 'sorter', 'souk', 'sound bow', 'soundbox', + 'sound camera', 'sounder', 'sound film', 'sounding board', + 'sounding rocket', 'sound recording', 'sound spectrograph', 'soup bowl', + 'soup ladle', 'soupspoon', 'source of illumination', 'sourdine', 'soutache', + 'soutane', 'sou\'wester', 'soybean future', 'space bar', 'space capsule', + 'spacecraft', 'space heater', 'space helmet', 'space rocket', + 'space shuttle', 'space station', 'spacesuit', 'spade', 'spade bit', + 'spaghetti junction', 'spandau', 'spandex', 'spandrel', 'spanker', 'spar', + 'sparge pipe', 'spark arrester', 'spark arrester', 'spark chamber', + 'spark coil', 'spark gap', 'spark lever', 'spark plug', 'sparkplug wrench', + 'spark transmitter', 'spat', 'spatula', 'spatula', 'speakerphone', + 'speaking trumpet', 'spear', 'spear', 'specialty store', 'specimen bottle', + 'spectacle', 'spectacles', 'spectator pump', 'spectrograph', + 'spectrophotometer', 'spectroscope', 'speculum', 'speedboat', 'speed bump', + 'speedometer', 'speed skate', 'spherometer', 'sphygmomanometer', + 'spicemill', 'spice rack', 'spider', 'spider web', 'spike', 'spike', + 'spindle', 'spindle', 'spindle', 'spin dryer', 'spinet', 'spinet', + 'spinnaker', 'spinner', 'spinning frame', 'spinning jenny', + 'spinning machine', 'spinning rod', 'spinning wheel', 'spiral bandage', + 'spiral ratchet screwdriver', 'spiral spring', 'spirit lamp', + 'spirit stove', 'spirometer', 'spit', 'spittoon', 'splashboard', 'splasher', + 'splice', 'splicer', 'splint', 'split rail', 'spode', 'spoiler', 'spoiler', + 'spoke', 'spokeshave', 'sponge cloth', 'sponge mop', 'spoon', 'spoon', + 'spork', 'sporran', 'sport kite', 'sports car', 'sports equipment', + 'sports implement', 'sportswear', 'sport utility', 'spot', 'spotlight', + 'spot weld', 'spouter', 'sprag', 'spray gun', 'spray paint', 'spreader', + 'sprig', 'spring', 'spring balance', 'springboard', 'sprinkler', + 'sprinkler system', 'sprit', 'spritsail', 'sprocket', 'sprocket', + 'spun yarn', 'spur', 'spur gear', 'sputnik', 'spy satellite', 'squad room', + 'square', 'square knot', 'square-rigger', 'square sail', 'squash ball', + 'squash racket', 'squawk box', 'squeegee', 'squeezer', 'squelch circuit', + 'squinch', 'stabilizer', 'stabilizer', 'stabilizer bar', 'stable', + 'stable gear', 'stabling', 'stacks', 'staddle', 'stadium', 'stage', + 'stagecoach', 'stained-glass window', 'stair-carpet', 'stair-rod', + 'stairwell', 'stake', 'stall', 'stall', 'stamp', 'stamp mill', + 'stamping machine', 'stanchion', 'stand', 'standard', 'standard cell', + 'standard transmission', 'standing press', 'stanhope', 'stanley steamer', + 'staple', 'staple', 'staple gun', 'stapler', 'starship', 'starter', + 'starting gate', 'stassano furnace', 'statehouse', 'stately home', + 'state prison', 'stateroom', 'static tube', 'station', 'stator', 'statue', + 'stay', 'staysail', 'steakhouse', 'steak knife', 'stealth aircraft', + 'stealth bomber', 'stealth fighter', 'steam bath', 'steamboat', + 'steam chest', 'steam engine', 'steamer', 'steamer', 'steam iron', + 'steam locomotive', 'steamroller', 'steam shovel', 'steam turbine', + 'steam whistle', 'steel', 'steel arch bridge', 'steel drum', 'steel mill', + 'steel-wool pad', 'steelyard', 'steeple', 'steerage', 'steering gear', + 'steering linkage', 'steering system', 'steering wheel', 'stele', + 'stem-winder', 'stencil', 'sten gun', 'stenograph', 'step', + 'step-down transformer', 'step stool', 'step-up transformer', 'stereo', + 'stereoscope', 'stern chaser', 'sternpost', 'sternwheeler', 'stethoscope', + 'stewing pan', 'stick', 'stick', 'stick', 'stick', 'stile', 'stiletto', + 'still', 'stillroom', 'stillson wrench', 'stilt', 'stinger', 'stink bomb', + 'stirrer', 'stirrup', 'stirrup pump', 'stob', 'stock', 'stockade', + 'stockcar', 'stock car', 'stockinet', 'stocking', 'stock-in-trade', + 'stockpot', 'stockroom', 'stocks', 'stock saddle', 'stockyard', 'stole', + 'stomacher', 'stomach pump', 'stone wall', 'stoneware', 'stonework', + 'stool', 'stoop', 'stop bath', 'stopcock', 'stopper knot', 'stopwatch', + 'storage battery', 'storage cell', 'storage ring', 'storage space', + 'storeroom', 'storm cellar', 'storm door', 'storm window', 'stoup', 'stoup', + 'stove', 'stove', 'stove bolt', 'stovepipe', 'stovepipe iron', + 'stradavarius', 'straight chair', 'straightedge', 'straightener', + 'straight flute', 'straight pin', 'straight razor', 'strainer', + 'straitjacket', 'strap', 'strap', 'strap hinge', 'strapless', + 'streamer fly', 'streamliner', 'street', 'street', 'streetcar', + 'street clothes', 'streetlight', 'stretcher', 'stretcher', 'stretch pants', + 'strickle', 'strickle', 'stringed instrument', 'stringer', 'stringer', + 'string tie', 'strip', 'strip lighting', 'strip mall', 'stroboscope', + 'strongbox', 'stronghold', 'strongroom', 'strop', 'structural member', + 'structure', 'student center', 'student lamp', 'student union', + 'stud finder', 'studio apartment', 'studio couch', 'study', 'study hall', + 'stuffing nut', 'stump', 'stun gun', 'stupa', 'sty', 'stylus', 'stylus', + 'sub-assembly', 'subcompact', 'submachine gun', 'submarine', + 'submarine torpedo', 'submersible', 'submersible', 'subtracter', + 'subway token', 'subway train', 'subwoofer', 'suction cup', 'suction pump', + 'sudatorium', 'suede cloth', 'sugar bowl', 'sugar refinery', 'sugar spoon', + 'suit', 'suite', 'suiting', 'sulky', 'summer house', 'sumo ring', 'sump', + 'sump pump', 'sunbonnet', 'sunday best', 'sun deck', 'sundial', 'sundress', + 'sundries', 'sun gear', 'sunglass', 'sunglasses', 'sunhat', 'sunlamp', + 'sun parlor', 'sunroof', 'sunscreen', 'sunsuit', 'supercharger', + 'supercomputer', 'superconducting supercollider', 'superhighway', + 'supermarket', 'superstructure', 'supertanker', 'supper club', 'supplejack', + 'supply chamber', 'supply closet', 'support', 'support', 'support column', + 'support hose', 'supporting structure', 'supporting tower', 'surcoat', + 'surface gauge', 'surface lift', 'surface search radar', 'surface ship', + 'surface-to-air missile', 'surface-to-air missile system', 'surfboat', + 'surcoat', 'surgeon\'s knot', 'surgery', 'surge suppressor', + 'surgical dressing', 'surgical instrument', 'surgical knife', 'surplice', + 'surrey', 'surtout', 'surveillance system', 'surveying instrument', + 'surveyor\'s level', 'sushi bar', 'suspension', 'suspension bridge', + 'suspensory', 'sustaining pedal', 'suture', 'swab', 'swab', + 'swaddling clothes', 'swag', 'swage block', 'swagger stick', + 'swallow-tailed coat', 'swamp buggy', 'swan\'s down', 'swathe', 'swatter', + 'sweat bag', 'sweatband', 'sweater', 'sweat pants', 'sweatshirt', + 'sweatshop', 'sweat suit', 'sweep', 'sweep hand', 'swimming trunks', + 'swimsuit', 'swing', 'swing door', 'switch', 'switchblade', 'switch engine', + 'swivel', 'swivel chair', 'swizzle stick', 'sword', 'sword cane', + 's wrench', 'synagogue', 'synchrocyclotron', 'synchroflash', 'synchromesh', + 'synchronous converter', 'synchronous motor', 'synchrotron', 'synchroscope', + 'synthesizer', 'syringe', 'system', 'tabard', 'tabernacle', 'tabi', + 'tab key', 'table', 'table', 'tablefork', 'table knife', 'table lamp', + 'table saw', 'tablespoon', 'tablet-armed chair', 'table-tennis table', + 'table-tennis racquet', 'tabletop', 'tableware', 'tabor', 'taboret', + 'tachistoscope', 'tachograph', 'tachometer', 'tachymeter', 'tack', + 'tack hammer', 'taffeta', 'taffrail', 'tailgate', 'taillight', + 'tailor-made', 'tailor\'s chalk', 'tailpipe', 'tail rotor', 'tailstock', + 'take-up', 'talaria', 'talcum', 'tam', 'tambour', 'tambour', 'tambourine', + 'tammy', 'tamp', 'tampax', 'tampion', 'tampon', 'tandoor', 'tangram', + 'tank', 'tank', 'tankard', 'tank car', 'tank destroyer', 'tank engine', + 'tanker plane', 'tank shell', 'tank top', 'tannoy', 'tap', 'tapa', 'tape', + 'tape', 'tape deck', 'tape drive', 'tape player', 'tape recorder', + 'taper file', 'tapestry', 'tappet', 'tap wrench', 'tare', 'target', + 'target acquisition system', 'tarmacadam', 'tarpaulin', 'tartan', 'tasset', + 'tattoo', 'tavern', 'tawse', 'taximeter', 't-bar lift', 'tea bag', + 'tea ball', 'tea cart', 'tea chest', 'teaching aid', 'teacup', 'tea gown', + 'teakettle', 'tea maker', 'teapot', 'teashop', 'teaspoon', 'tea-strainer', + 'tea table', 'tea tray', 'tea urn', 'tee', 'tee hinge', 'telecom hotel', + 'telecommunication system', 'telegraph', 'telegraph key', 'telemeter', + 'telephone', 'telephone bell', 'telephone booth', 'telephone cord', + 'telephone jack', 'telephone line', 'telephone plug', 'telephone pole', + 'telephone receiver', 'telephone system', 'telephone wire', + 'telephoto lens', 'teleprompter', 'telescope', 'telescopic sight', + 'telethermometer', 'teletypewriter', 'television', 'television antenna', + 'television camera', 'television equipment', 'television monitor', + 'television receiver', 'television room', 'television transmitter', + 'telpher', 'telpherage', 'tempera', 'temple', 'temple', 'temporary hookup', + 'tender', 'tender', 'tender', 'tenement', 'tennis ball', 'tennis camp', + 'tennis racket', 'tenon', 'tenor drum', 'tenoroon', 'tenpenny nail', + 'tenpin', 'tensimeter', 'tensiometer', 'tensiometer', 'tensiometer', 'tent', + 'tenter', 'tenterhook', 'tent-fly', 'tent peg', 'tepee', 'terminal', + 'terminal', 'terraced house', 'terra cotta', 'terrarium', 'terra sigillata', + 'terry', 'tesla coil', 'tessera', 'test equipment', 'test rocket', + 'test room', 'testudo', 'tetraskelion', 'tetrode', 'textile machine', + 'textile mill', 'thatch', 'theater', 'theater curtain', 'theater light', + 'theodolite', 'theremin', 'thermal printer', 'thermal reactor', + 'thermocouple', 'thermoelectric thermometer', 'thermograph', 'thermograph', + 'thermohydrometer', 'thermojunction', 'thermometer', + 'thermonuclear reactor', 'thermopile', 'thermos', 'thermostat', 'thigh pad', + 'thill', 'thimble', 'thinning shears', 'third base', 'third gear', + 'third rail', 'thong', 'thong', 'three-centered arch', 'three-decker', + 'three-dimensional radar', 'three-piece suit', 'three-quarter binding', + 'three-way switch', 'thresher', 'threshing floor', 'thriftshop', + 'throat protector', 'throne', 'thrust bearing', 'thruster', 'thumb', + 'thumbhole', 'thumbscrew', 'thumbstall', 'thumbtack', 'thunderer', 'thwart', + 'tiara', 'ticking', 'tickler coil', 'tie', 'tie', 'tie rack', 'tie rod', + 'tights', 'tile', 'tile cutter', 'tile roof', 'tiller', 'tilter', + 'tilt-top table', 'timber', 'timber', 'timber hitch', 'timbrel', + 'time bomb', 'time capsule', 'time clock', + 'time-delay measuring instrument', 'time-fuse', 'timepiece', 'timer', + 'timer', 'time-switch', 'tin', 'tinderbox', 'tine', 'tinfoil', 'tippet', + 'tire chain', 'tire iron', 'titfer', 'tithe barn', 'titrator', 'toaster', + 'toaster oven', 'toasting fork', 'toastrack', 'tobacco pouch', + 'tobacco shop', 'toboggan', 'toby', 'tocsin', 'toe', 'toecap', 'toehold', + 'toga', 'toga virilis', 'toggle', 'toggle bolt', 'toggle joint', + 'toggle switch', 'togs', 'toilet', 'toilet bag', 'toilet bowl', + 'toilet kit', 'toilet powder', 'toiletry', 'toilet seat', 'toilet water', + 'tokamak', 'token', 'tollbooth', 'toll bridge', 'tollgate', 'toll line', + 'tomahawk', 'tommy gun', 'tomograph', 'tone arm', 'toner', 'tongs', + 'tongue', 'tongue and groove joint', 'tongue depressor', 'tonometer', + 'tool', 'tool bag', 'toolbox', 'toolshed', 'tooth', 'tooth', 'toothbrush', + 'toothpick', 'top', 'top', 'topgallant', 'topgallant', 'topiary', 'topknot', + 'topmast', 'topper', 'topsail', 'toque', 'torch', 'torpedo', 'torpedo', + 'torpedo', 'torpedo boat', 'torpedo-boat destroyer', 'torpedo tube', + 'torque converter', 'torque wrench', 'torture chamber', 'totem pole', + 'touch screen', 'toupee', 'touring car', 'tourist class', 'towel', + 'toweling', 'towel rack', 'towel rail', 'tower', 'town hall', 'towpath', + 'tow truck', 'toy', 'toy box', 'toyshop', 'trace detector', 'track', + 'track', 'trackball', 'tracked vehicle', 'tract house', 'tract housing', + 'traction engine', 'tractor', 'tractor', 'trail bike', 'trailer', 'trailer', + 'trailer camp', 'trailer truck', 'trailing edge', 'train', 'tramline', + 'trammel', 'trampoline', 'tramp steamer', 'tramway', 'transdermal patch', + 'transept', 'transformer', 'transistor', 'transit instrument', + 'transmission', 'transmission shaft', 'transmitter', 'transom', 'transom', + 'transponder', 'transporter', 'transporter', 'transport ship', 'trap', + 'trap door', 'trapeze', 'trave', 'travel iron', 'trawl', 'trawl', 'trawler', + 'tray', 'tray cloth', 'tread', 'tread', 'treadmill', 'treadmill', + 'treasure chest', 'treasure ship', 'treenail', 'trefoil arch', 'trellis', + 'trench', 'trench coat', 'trench knife', 'trepan', 'trepan', 'trestle', + 'trestle', 'trestle bridge', 'trestle table', 'trestlework', 'trews', + 'trial balloon', 'triangle', 'triangle', 'triclinium', 'triclinium', + 'tricorn', 'tricot', 'tricycle', 'trident', 'trigger', 'trimaran', + 'trimmer', 'trimmer arch', 'triode', 'tripod', 'triptych', 'trip wire', + 'trireme', 'triskelion', 'triumphal arch', 'trivet', 'trivet', 'troika', + 'troll', 'trolleybus', 'trombone', 'troop carrier', 'troopship', + 'trophy case', 'trough', 'trouser', 'trouser cuff', 'trouser press', + 'trouser', 'trousseau', 'trowel', 'truck', 'trumpet arch', 'truncheon', + 'trundle bed', 'trunk', 'trunk hose', 'trunk lid', 'trunk line', 'truss', + 'truss bridge', 'try square', 't-square', 'tub', 'tube', 'tuck box', + 'tucker', 'tucker-bag', 'tuck shop', 'tudor arch', 'tudung', 'tugboat', + 'tulle', 'tumble-dryer', 'tumbler', 'tumbrel', 'tun', 'tunic', + 'tuning fork', 'tupik', 'turban', 'turbine', 'turbogenerator', 'tureen', + 'turkish bath', 'turkish towel', 'turk\'s head', 'turnbuckle', 'turner', + 'turnery', 'turnpike', 'turnspit', 'turnstile', 'turntable', 'turntable', + 'turret', 'turret clock', 'turtleneck', 'tweed', 'tweeter', 'twenty-two', + 'twenty-two pistol', 'twenty-two rifle', 'twill', 'twill', 'twin bed', + 'twinjet', 'twist bit', 'two-by-four', 'two-man tent', 'two-piece', + 'typesetting machine', 'typewriter', 'typewriter carriage', + 'typewriter keyboard', 'tyrolean', 'uke', 'ulster', 'ultracentrifuge', + 'ultramicroscope', 'ultrasuede', 'ultraviolet lamp', 'umbrella', + 'umbrella tent', 'undercarriage', 'undercoat', 'undergarment', 'underpants', + 'underwear', 'undies', 'uneven parallel bars', 'unicycle', 'uniform', + 'universal joint', 'university', 'upholstery', 'upholstery material', + 'upholstery needle', 'uplift', 'upper berth', 'upright', 'upset', + 'upstairs', 'urceole', 'urn', 'urn', 'used-car', 'utensil', 'uzi', + 'vacation home', 'vacuum', 'vacuum chamber', 'vacuum flask', 'vacuum gauge', + 'valenciennes', 'valise', 'valve', 'valve', 'valve-in-head engine', + 'vambrace', 'van', 'van', 'vane', 'vaporizer', 'variable-pitch propeller', + 'variometer', 'varnish', 'vase', 'vault', 'vault', 'vaulting horse', + 'vehicle', 'velcro', 'velocipede', 'velour', 'velvet', 'velveteen', + 'vending machine', 'veneer', 'venetian blind', 'venn diagram', + 'ventilation', 'ventilation shaft', 'ventilator', 'veranda', 'verdigris', + 'vernier caliper', 'vernier scale', 'vertical file', 'vertical stabilizer', + 'vertical tail', 'very pistol', 'vessel', 'vessel', 'vest', 'vestiture', + 'vestment', 'vest pocket', 'vestry', 'viaduct', 'vibraphone', 'vibrator', + 'vibrator', 'victrola', 'vicuna', 'videocassette', 'videocassette recorder', + 'videodisk', 'video recording', 'videotape', 'videotape', 'vigil light', + 'villa', 'villa', 'villa', 'viol', 'viola', 'viola da braccio', + 'viola da gamba', 'viola d\'amore', 'violin', 'virginal', 'viscometer', + 'viscose rayon', 'vise', 'visor', 'visual display unit', 'vivarium', + 'viyella', 'voile', 'volleyball', 'volleyball net', 'voltage regulator', + 'voltaic cell', 'voltaic pile', 'voltmeter', 'vomitory', + 'von neumann machine', 'voting booth', 'voting machine', 'voussoir', + 'vox angelica', 'vox humana', 'waders', 'wading pool', 'waffle iron', + 'wagon', 'wagon', 'wagon tire', 'wagon wheel', 'wain', 'wainscot', + 'wainscoting', 'waist pack', 'walker', 'walker', 'walker', 'walkie-talkie', + 'walk-in', 'walking shoe', 'walking stick', 'walkman', 'walk-up apartment', + 'wall', 'wall', 'wall clock', 'wallet', 'wall tent', 'wall unit', 'wand', + 'wankel engine', 'ward', 'wardrobe', 'wardroom', 'warehouse', 'warming pan', + 'war paint', 'warplane', 'war room', 'warship', 'wash', 'wash-and-wear', + 'washbasin', 'washboard', 'washboard', 'washer', 'washer', 'washhouse', + 'washroom', 'washstand', 'washtub', 'wastepaper basket', 'watch', + 'watch cap', 'watch case', 'watch glass', 'watchtower', 'water-base paint', + 'water bed', 'water bottle', 'water butt', 'water cart', 'water chute', + 'water closet', 'watercolor', 'water-cooled reactor', 'water cooler', + 'water faucet', 'water filter', 'water gauge', 'water glass', + 'water hazard', 'water heater', 'watering can', 'watering cart', + 'water jacket', 'water jug', 'water jump', 'water level', 'water meter', + 'water mill', 'waterproof', 'waterproofing', 'water pump', 'water scooter', + 'water ski', 'waterspout', 'water tower', 'water wagon', 'waterwheel', + 'waterwheel', 'water wings', 'waterworks', 'wattmeter', 'waxwork', 'ways', + 'weapon', 'weaponry', 'weapons carrier', 'weathercock', 'weatherglass', + 'weather satellite', 'weather ship', 'weathervane', 'web', 'web', 'webbing', + 'webcam', 'wedge', 'wedge', 'wedgie', 'wedgwood', 'weeder', 'weeds', + 'weekender', 'weighbridge', 'weight', 'weir', 'weir', 'welcome wagon', + 'weld', 'welder\'s mask', 'weldment', 'well', 'wellhead', 'welt', + 'weston cell', 'wet bar', 'wet-bulb thermometer', 'wet cell', 'wet fly', + 'wet suit', 'whaleboat', 'whaler', 'whaling gun', 'wheel', 'wheel', + 'wheel and axle', 'wheelchair', 'wheeled vehicle', 'wheelwork', 'wherry', + 'wherry', 'whetstone', 'whiffletree', 'whip', 'whipcord', 'whipping post', + 'whipstitch', 'whirler', 'whisk', 'whisk', 'whiskey bottle', 'whiskey jug', + 'whispering gallery', 'whistle', 'whistle', 'white', 'white goods', + 'whitewash', 'whorehouse', 'wick', 'wicker', 'wicker basket', 'wicket', + 'wicket', 'wickiup', 'wide-angle lens', 'widebody aircraft', 'wide wale', + 'widow\'s walk', 'wiffle', 'wig', 'wigwam', 'wilton', 'wimple', 'wincey', + 'winceyette', 'winch', 'winchester', 'windbreak', 'winder', + 'wind instrument', 'windjammer', 'windmill', 'windmill', 'window', 'window', + 'window blind', 'window box', 'window envelope', 'window frame', + 'window screen', 'window seat', 'window shade', 'windowsill', 'windshield', + 'windshield wiper', 'windsor chair', 'windsor knot', 'windsor tie', + 'wind tee', 'wind tunnel', 'wind turbine', 'wine bar', 'wine bottle', + 'wine bucket', 'wine cask', 'wineglass', 'winepress', 'winery', 'wineskin', + 'wing', 'wing chair', 'wing nut', 'wing tip', 'wing tip', 'winker', 'wiper', + 'wiper motor', 'wire', 'wire', 'wire cloth', 'wire cutter', 'wire gauge', + 'wireless local area network', 'wire matrix printer', 'wire recorder', + 'wire stripper', 'wirework', 'wiring', 'wishing cap', 'witness box', 'wok', + 'woman\'s clothing', 'wood', 'woodcarving', 'wood chisel', 'woodenware', + 'wooden spoon', 'woodscrew', 'woodshed', 'wood vise', 'woodwind', 'woof', + 'woofer', 'wool', 'workbasket', 'workbench', 'work-clothing', 'workhouse', + 'workhouse', 'workpiece', 'workroom', 'works', 'work-shirt', 'workstation', + 'worktable', 'workwear', 'world wide web', 'worm fence', 'worm gear', + 'worm wheel', 'worsted', 'worsted', 'wrap', 'wraparound', 'wrapping', + 'wreck', 'wrench', 'wrestling mat', 'wringer', 'wrist pad', 'wrist pin', + 'wristwatch', 'writing arm', 'writing desk', 'writing desk', + 'writing implement', 'xerographic printer', 'xerox', 'x-ray film', + 'x-ray machine', 'x-ray tube', 'yacht', 'yacht chair', 'yagi', 'yard', + 'yard', 'yardarm', 'yard marker', 'yardstick', 'yarmulke', 'yashmak', + 'yataghan', 'yawl', 'yawl', 'yoke', 'yoke', 'yoke', 'yurt', 'zamboni', + 'zero', 'ziggurat', 'zill', 'zip gun', 'zither', 'zoot suit', 'shading', + 'grain', 'wood grain', 'graining', 'marbleization', 'light', 'aura', + 'sunniness', 'glint', 'opalescence', 'polish', 'primary color for pigments', + 'primary color for light', 'colorlessness', 'mottle', 'achromia', 'shade', + 'chromatic color', 'black', 'coal black', 'alabaster', 'bone', 'gray', + 'ash grey', 'charcoal', 'sanguine', 'turkey red', 'crimson', 'dark red', + 'claret', 'fuschia', 'maroon', 'orange', 'reddish orange', 'yellow', + 'gamboge', 'pale yellow', 'green', 'greenishness', 'sea green', + 'sage green', 'bottle green', 'emerald', 'olive green', 'jade green', + 'blue', 'azure', 'steel blue', 'greenish blue', 'purplish blue', 'purple', + 'tyrian purple', 'indigo', 'lavender', 'reddish purple', 'pink', + 'carnation', 'rose', 'chestnut', 'chocolate', 'light brown', 'tan', 'beige', + 'reddish brown', 'brick red', 'copper', 'indian red', 'puce', 'olive', + 'ultramarine', 'complementary color', 'pigmentation', 'complexion', + 'ruddiness', 'nonsolid color', 'aposematic coloration', + 'cryptic coloration', 'ring', 'center of curvature', 'cadaver', + 'mandibular notch', 'rib', 'skin', 'skin graft', 'epidermal cell', + 'melanocyte', 'prickle cell', 'columnar cell', 'spongioblast', + 'squamous cell', 'amyloid plaque', 'dental plaque', 'macule', 'freckle', + 'bouffant', 'sausage curl', 'forelock', 'spit curl', 'pigtail', 'pageboy', + 'pompadour', 'thatch', 'soup-strainer', 'mustachio', 'walrus mustache', + 'stubble', 'vandyke beard', 'soul patch', 'esophageal smear', + 'paraduodenal smear', 'specimen', 'punctum', 'glenoid fossa', 'diastema', + 'marrow', 'mouth', 'canthus', 'milk', 'mother\'s milk', 'colostrum', 'vein', + 'ganglion cell', 'x chromosome', 'embryonic cell', 'myeloblast', + 'sideroblast', 'osteocyte', 'megalocyte', 'leukocyte', 'histiocyte', + 'fixed phagocyte', 'lymphocyte', 'monoblast', 'neutrophil', 'microphage', + 'sickle cell', 'siderocyte', 'spherocyte', 'ootid', 'oocyte', 'spermatid', + 'leydig cell', 'striated muscle cell', 'smooth muscle cell', + 'ranvier\'s nodes', 'neuroglia', 'astrocyte', 'protoplasmic astrocyte', + 'oligodendrocyte', 'proprioceptor', 'dendrite', 'sensory fiber', + 'subarachnoid space', 'cerebral cortex', 'renal cortex', 'prepuce', 'head', + 'scalp', 'frontal eminence', 'suture', 'foramen magnum', + 'esophagogastric junction', 'heel', 'cuticle', 'hangnail', 'exoskeleton', + 'abdominal wall', 'lemon', 'coordinate axis', 'landscape', 'medium', + 'vehicle', 'paper', 'channel', 'film', 'silver screen', 'free press', + 'press', 'print media', 'storage medium', 'magnetic storage medium', + 'journalism', 'fleet street', 'photojournalism', 'news photography', + 'rotogravure', 'newspaper', 'daily', 'gazette', 'school newspaper', + 'tabloid', 'yellow journalism', 'telecommunication', 'telephone', + 'voice mail', 'call', 'call-back', 'collect call', 'call forwarding', + 'call-in', 'call waiting', 'crank call', 'local call', 'long distance', + 'toll call', 'wake-up call', 'three-way calling', 'telegraphy', 'cable', + 'wireless', 'radiotelegraph', 'radiotelephone', 'broadcasting', + 'rediffusion', 'multiplex', 'radio', 'television', 'cable television', + 'high-definition television', 'reception', 'signal detection', 'hakham', + 'web site', 'chat room', 'portal site', 'jotter', 'breviary', 'wordbook', + 'desk dictionary', 'reckoner', 'document', 'album', 'concept album', + 'rock opera', 'tribute album', 'magazine', 'colour supplement', + 'comic book', 'news magazine', 'pulp', 'slick', 'trade magazine', 'movie', + 'outtake', 'shoot-\'em-up', 'spaghetti western', 'encyclical', + 'crossword puzzle', 'sign', 'street sign', 'traffic light', 'swastika', + 'concert', 'artwork', 'lobe', 'book jacket', 'cairn', 'three-day event', + 'comfort food', 'comestible', 'tuck', 'course', 'dainty', 'dish', + 'fast food', 'finger food', 'ingesta', 'kosher', 'fare', 'diet', 'diet', + 'dietary', 'balanced diet', 'bland diet', 'clear liquid diet', + 'diabetic diet', 'dietary supplement', 'carbohydrate loading', 'fad diet', + 'gluten-free diet', 'high-protein diet', 'high-vitamin diet', 'light diet', + 'liquid diet', 'low-calorie diet', 'low-fat diet', 'low-sodium diet', + 'macrobiotic diet', 'reducing diet', 'soft diet', 'vegetarianism', 'menu', + 'chow', 'board', 'mess', 'ration', 'field ration', 'k ration', 'c-ration', + 'foodstuff', 'starches', 'breadstuff', 'coloring', 'concentrate', + 'tomato concentrate', 'meal', 'kibble', 'cornmeal', 'farina', 'matzo meal', + 'oatmeal', 'pea flour', 'roughage', 'bran', 'flour', 'plain flour', + 'wheat flour', 'whole wheat flour', 'soybean meal', 'semolina', + 'corn gluten feed', 'nutriment', 'commissariat', 'larder', 'frozen food', + 'canned food', 'canned meat', 'spam', 'dehydrated food', 'square meal', + 'meal', 'potluck', 'refection', 'refreshment', 'breakfast', + 'continental breakfast', 'brunch', 'lunch', 'business lunch', 'high tea', + 'tea', 'dinner', 'supper', 'buffet', 'picnic', 'cookout', 'barbecue', + 'clambake', 'fish fry', 'bite', 'nosh', 'nosh-up', 'ploughman\'s lunch', + 'coffee break', 'banquet', 'entree', 'piece de resistance', 'plate', + 'adobo', 'side dish', 'special', 'casserole', 'chicken casserole', + 'chicken cacciatore', 'antipasto', 'appetizer', 'canape', 'cocktail', + 'fruit cocktail', 'crab cocktail', 'shrimp cocktail', 'hors d\'oeuvre', + 'relish', 'dip', 'bean dip', 'cheese dip', 'clam dip', 'guacamole', 'soup', + 'soup du jour', 'alphabet soup', 'consomme', 'madrilene', 'bisque', + 'borsch', 'broth', 'barley water', 'bouillon', 'beef broth', + 'chicken broth', 'broth', 'stock cube', 'chicken soup', 'cock-a-leekie', + 'gazpacho', 'gumbo', 'julienne', 'marmite', 'mock turtle soup', + 'mulligatawny', 'oxtail soup', 'pea soup', 'pepper pot', 'petite marmite', + 'potage', 'pottage', 'turtle soup', 'eggdrop soup', 'chowder', + 'corn chowder', 'clam chowder', 'manhattan clam chowder', + 'new england clam chowder', 'fish chowder', 'won ton', 'split-pea soup', + 'green pea soup', 'lentil soup', 'scotch broth', 'vichyssoise', 'stew', + 'bigos', 'brunswick stew', 'burgoo', 'burgoo', 'olla podrida', + 'mulligan stew', 'purloo', 'goulash', 'hotchpotch', 'hot pot', + 'beef goulash', 'pork-and-veal goulash', 'porkholt', 'irish stew', + 'oyster stew', 'lobster stew', 'lobscouse', 'fish stew', 'bouillabaisse', + 'matelote', 'paella', 'fricassee', 'chicken stew', 'turkey stew', + 'beef stew', 'ragout', 'ratatouille', 'salmi', 'pot-au-feu', 'slumgullion', + 'smorgasbord', 'viand', 'ready-mix', 'brownie mix', 'cake mix', + 'lemonade mix', 'self-rising flour', 'choice morsel', 'savory', + 'calf\'s-foot jelly', 'caramel', 'lump sugar', 'cane sugar', 'castor sugar', + 'powdered sugar', 'granulated sugar', 'icing sugar', 'corn sugar', + 'brown sugar', 'demerara', 'sweet', 'confectionery', 'confiture', + 'sweetmeat', 'candy', 'candy bar', 'carob bar', 'hardbake', 'hard candy', + 'barley-sugar', 'brandyball', 'jawbreaker', 'lemon drop', 'sourball', + 'patty', 'peppermint patty', 'bonbon', 'brittle', 'peanut brittle', + 'chewing gum', 'gum ball', 'bubble gum', 'butterscotch', 'candied fruit', + 'candied apple', 'crystallized ginger', 'grapefruit peel', 'lemon peel', + 'orange peel', 'candied citrus peel', 'candy cane', 'candy corn', 'caramel', + 'center', 'comfit', 'cotton candy', 'dragee', 'dragee', 'fondant', 'fudge', + 'chocolate fudge', 'divinity', 'penuche', 'gumdrop', 'jujube', + 'honey crisp', 'mint', 'horehound', 'peppermint', 'jelly bean', 'kiss', + 'molasses kiss', 'meringue kiss', 'chocolate kiss', 'licorice', + 'life saver', 'lollipop', 'lozenge', 'cachou', 'cough drop', 'marshmallow', + 'marzipan', 'nougat', 'nougat bar', 'nut bar', 'peanut bar', 'popcorn ball', + 'praline', 'rock candy', 'rock candy', 'sugar candy', 'sugarplum', 'taffy', + 'molasses taffy', 'truffle', 'turkish delight', 'dessert', 'ambrosia', + 'ambrosia', 'baked alaska', 'blancmange', 'charlotte', 'compote', + 'dumpling', 'flan', 'frozen dessert', 'junket', 'mousse', 'mousse', + 'pavlova', 'peach melba', 'whip', 'prune whip', 'pudding', 'pudding', + 'syllabub', 'tiramisu', 'trifle', 'tipsy cake', 'jello', 'apple dumpling', + 'ice', 'water ice', 'ice cream', 'ice-cream cone', 'chocolate ice cream', + 'neapolitan ice cream', 'peach ice cream', 'sherbert', + 'strawberry ice cream', 'tutti-frutti', 'vanilla ice cream', 'ice lolly', + 'ice milk', 'frozen yogurt', 'snowball', 'snowball', 'parfait', + 'ice-cream sundae', 'split', 'banana split', 'frozen pudding', + 'frozen custard', 'pudding', 'flummery', 'fish mousse', 'chicken mousse', + 'chocolate mousse', 'plum pudding', 'carrot pudding', 'corn pudding', + 'steamed pudding', 'duff', 'vanilla pudding', 'chocolate pudding', + 'brown betty', 'nesselrode', 'pease pudding', 'custard', 'creme caramel', + 'creme anglais', 'creme brulee', 'fruit custard', 'tapioca', + 'tapioca pudding', 'roly-poly', 'suet pudding', 'bavarian cream', + 'maraschino', 'nonpareil', 'zabaglione', 'garnish', 'pastry', 'turnover', + 'apple turnover', 'knish', 'pirogi', 'samosa', 'timbale', 'puff paste', + 'phyllo', 'puff batter', 'ice-cream cake', 'doughnut', 'fish cake', + 'fish stick', 'conserve', 'apple butter', 'chowchow', 'jam', 'lemon curd', + 'strawberry jam', 'jelly', 'apple jelly', 'crabapple jelly', 'grape jelly', + 'marmalade', 'orange marmalade', 'gelatin', 'gelatin dessert', + 'buffalo wing', 'barbecued wing', 'mess', 'mince', 'puree', 'barbecue', + 'biryani', 'escalope de veau orloff', 'saute', 'patty', 'veal parmesan', + 'veal cordon bleu', 'margarine', 'mincemeat', 'stuffing', 'turkey stuffing', + 'oyster stuffing', 'forcemeat', 'bread', 'anadama bread', 'bap', + 'barmbrack', 'breadstick', 'grissino', 'brown bread', 'bun', 'tea bread', + 'caraway seed bread', 'challah', 'cinnamon bread', 'cracked-wheat bread', + 'cracker', 'crouton', 'dark bread', 'english muffin', 'flatbread', + 'garlic bread', 'gluten bread', 'graham bread', 'host', 'flatbrod', + 'bannock', 'chapatti', 'pita', 'loaf of bread', 'french loaf', 'matzo', + 'nan', 'onion bread', 'raisin bread', 'quick bread', 'banana bread', + 'date bread', 'date-nut bread', 'nut bread', 'oatcake', 'irish soda bread', + 'skillet bread', 'rye bread', 'black bread', 'jewish rye bread', 'limpa', + 'swedish rye bread', 'salt-rising bread', 'simnel', 'sour bread', 'toast', + 'wafer', 'white bread', 'baguet', 'french bread', 'italian bread', + 'cornbread', 'corn cake', 'skillet corn bread', 'ashcake', 'hoecake', + 'cornpone', 'corn dab', 'hush puppy', 'johnnycake', 'shawnee cake', + 'spoon bread', 'cinnamon toast', 'orange toast', 'melba toast', 'zwieback', + 'frankfurter bun', 'hamburger bun', 'muffin', 'bran muffin', 'corn muffin', + 'yorkshire pudding', 'popover', 'scone', 'drop scone', 'cross bun', + 'brioche', 'crescent roll', 'hard roll', 'soft roll', 'kaiser roll', + 'parker house roll', 'clover-leaf roll', 'onion roll', 'bialy', + 'sweet roll', 'bear claw', 'cinnamon roll', 'honey bun', 'pinwheel roll', + 'danish', 'bagel', 'onion bagel', 'biscuit', 'rolled biscuit', + 'baking-powder biscuit', 'buttermilk biscuit', 'shortcake', 'hardtack', + 'saltine', 'soda cracker', 'oyster cracker', 'water biscuit', + 'graham cracker', 'pretzel', 'soft pretzel', 'sandwich', 'sandwich plate', + 'butty', 'ham sandwich', 'chicken sandwich', 'club sandwich', + 'open-face sandwich', 'hamburger', 'cheeseburger', 'tunaburger', 'hotdog', + 'sloppy joe', 'bomber', 'gyro', 'bacon-lettuce-tomato sandwich', 'reuben', + 'western', 'wrap', 'spaghetti', 'hasty pudding', 'gruel', 'congee', + 'skilly', 'edible fruit', 'vegetable', 'julienne', 'raw vegetable', + 'crudites', 'celery stick', 'legume', 'pulse', 'potherb', 'greens', + 'chop-suey greens', 'bean curd', 'solanaceous vegetable', 'root vegetable', + 'potato', 'baked potato', 'french fries', 'home fries', 'jacket potato', + 'mashed potato', 'potato skin', 'uruguay potato', 'yam', 'sweet potato', + 'yam', 'snack food', 'chip', 'corn chip', 'tortilla chip', 'nacho', + 'eggplant', 'pieplant', 'cruciferous vegetable', 'mustard', 'cabbage', + 'kale', 'collards', 'chinese cabbage', 'bok choy', 'head cabbage', + 'red cabbage', 'savoy cabbage', 'broccoli', 'cauliflower', + 'brussels sprouts', 'broccoli rabe', 'squash', 'summer squash', + 'yellow squash', 'crookneck', 'zucchini', 'marrow', 'cocozelle', + 'pattypan squash', 'spaghetti squash', 'winter squash', 'acorn squash', + 'butternut squash', 'hubbard squash', 'turban squash', 'buttercup squash', + 'cushaw', 'winter crookneck squash', 'cucumber', 'gherkin', 'artichoke', + 'artichoke heart', 'jerusalem artichoke', 'asparagus', 'bamboo shoot', + 'sprout', 'bean sprout', 'alfalfa sprout', 'beet', 'beet green', + 'sugar beet', 'mangel-wurzel', 'chard', 'pepper', 'sweet pepper', + 'bell pepper', 'green pepper', 'globe pepper', 'pimento', 'hot pepper', + 'chili', 'jalapeno', 'chipotle', 'cayenne', 'tabasco', 'onion', + 'bermuda onion', 'green onion', 'vidalia onion', 'spanish onion', + 'purple onion', 'leek', 'shallot', 'salad green', 'lettuce', + 'butterhead lettuce', 'buttercrunch', 'bibb lettuce', 'boston lettuce', + 'crisphead lettuce', 'cos', 'leaf lettuce', 'celtuce', 'bean', 'goa bean', + 'lentil', 'pea', 'green pea', 'marrowfat pea', 'snow pea', 'sugar snap pea', + 'split-pea', 'chickpea', 'cajan pea', 'field pea', 'mushy peas', + 'black-eyed pea', 'common bean', 'kidney bean', 'navy bean', 'pinto bean', + 'frijole', 'black bean', 'fresh bean', 'flageolet', 'green bean', + 'snap bean', 'string bean', 'kentucky wonder', 'scarlet runner', + 'haricot vert', 'wax bean', 'shell bean', 'lima bean', 'fordhooks', + 'sieva bean', 'fava bean', 'soy', 'green soybean', 'field soybean', + 'cardoon', 'carrot', 'carrot stick', 'celery', 'pascal celery', 'celeriac', + 'chicory', 'radicchio', 'coffee substitute', 'chicory', 'postum', + 'chicory escarole', 'belgian endive', 'corn', 'sweet corn', 'hominy', + 'lye hominy', 'pearl hominy', 'popcorn', 'cress', 'watercress', + 'garden cress', 'winter cress', 'dandelion green', 'gumbo', 'kohlrabi', + 'lamb\'s-quarter', 'wild spinach', 'tomato', 'beefsteak tomato', + 'cherry tomato', 'plum tomato', 'tomatillo', 'mushroom', 'stuffed mushroom', + 'salsify', 'oyster plant', 'scorzonera', 'parsnip', 'pumpkin', 'radish', + 'turnip', 'white turnip', 'rutabaga', 'turnip greens', 'sorrel', + 'french sorrel', 'spinach', 'taro', 'truffle', 'edible nut', 'bunya bunya', + 'peanut', 'freestone', 'cling', 'windfall', 'apple', 'crab apple', + 'eating apple', 'baldwin', 'cortland', 'cox\'s orange pippin', 'delicious', + 'golden delicious', 'red delicious', 'empire', 'grimes\' golden', + 'jonathan', 'mcintosh', 'macoun', 'northern spy', 'pearmain', 'pippin', + 'prima', 'stayman', 'winesap', 'stayman winesap', 'cooking apple', + 'bramley\'s seedling', 'granny smith', 'lane\'s prince albert', + 'newtown wonder', 'rome beauty', 'berry', 'bilberry', 'huckleberry', + 'blueberry', 'wintergreen', 'cranberry', 'lingonberry', 'currant', + 'gooseberry', 'black currant', 'red currant', 'blackberry', 'boysenberry', + 'dewberry', 'loganberry', 'raspberry', 'saskatoon', 'strawberry', + 'sugarberry', 'persimmon', 'acerola', 'carambola', 'ceriman', + 'carissa plum', 'citrus', 'orange', 'temple orange', 'mandarin', + 'clementine', 'satsuma', 'tangerine', 'tangelo', 'bitter orange', + 'sweet orange', 'jaffa orange', 'navel orange', 'valencia orange', + 'kumquat', 'lemon', 'lime', 'key lime', 'grapefruit', 'pomelo', 'citrange', + 'citron', 'almond', 'jordan almond', 'apricot', 'peach', 'nectarine', + 'pitahaya', 'plum', 'damson', 'greengage', 'beach plum', 'sloe', + 'victoria plum', 'dried fruit', 'dried apricot', 'prune', 'raisin', + 'seedless raisin', 'seeded raisin', 'currant', 'fig', 'pineapple', + 'anchovy pear', 'banana', 'passion fruit', 'granadilla', 'sweet calabash', + 'bell apple', 'breadfruit', 'jackfruit', 'cacao bean', 'cocoa', 'canistel', + 'melon', 'melon ball', 'muskmelon', 'cantaloup', 'winter melon', 'honeydew', + 'persian melon', 'net melon', 'casaba', 'watermelon', 'cherry', + 'sweet cherry', 'bing cherry', 'heart cherry', 'blackheart', 'capulin', + 'sour cherry', 'amarelle', 'morello', 'cocoa plum', 'gherkin', 'grape', + 'fox grape', 'concord grape', 'catawba', 'muscadine', 'scuppernong', + 'slipskin grape', 'vinifera grape', 'emperor', 'muscat', 'ribier', + 'sultana', 'tokay', 'flame tokay', 'thompson seedless', 'custard apple', + 'cherimoya', 'soursop', 'sweetsop', 'ilama', 'pond apple', 'papaw', + 'papaya', 'kai apple', 'ketembilla', 'ackee', 'durian', 'feijoa', 'genip', + 'genipap', 'kiwi', 'loquat', 'mangosteen', 'mango', 'sapodilla', 'sapote', + 'tamarind', 'avocado', 'date', 'elderberry', 'guava', 'mombin', 'hog plum', + 'hog plum', 'jaboticaba', 'jujube', 'litchi', 'longanberry', 'mamey', + 'marang', 'medlar', 'medlar', 'mulberry', 'olive', 'black olive', + 'green olive', 'pear', 'bosc', 'anjou', 'bartlett', 'seckel', 'plantain', + 'plumcot', 'pomegranate', 'prickly pear', 'barbados gooseberry', 'quandong', + 'quandong nut', 'quince', 'rambutan', 'pulasan', 'rose apple', 'sorb', + 'sour gourd', 'edible seed', 'pumpkin seed', 'betel nut', 'beechnut', + 'walnut', 'black walnut', 'english walnut', 'brazil nut', 'butternut', + 'souari nut', 'cashew', 'chestnut', 'chincapin', 'hazelnut', 'coconut', + 'coconut milk', 'grugru nut', 'hickory nut', 'cola extract', + 'macadamia nut', 'pecan', 'pine nut', 'pistachio', 'sunflower seed', + 'anchovy paste', 'rollmops', 'feed', 'cattle cake', 'creep feed', 'fodder', + 'feed grain', 'eatage', 'silage', 'oil cake', 'oil meal', 'alfalfa', + 'broad bean', 'hay', 'timothy', 'stover', 'grain', 'grist', 'groats', + 'millet', 'barley', 'pearl barley', 'buckwheat', 'bulgur', 'wheat', + 'cracked wheat', 'stodge', 'wheat germ', 'oat', 'rice', 'brown rice', + 'white rice', 'wild rice', 'paddy', 'slop', 'mash', 'chicken feed', 'cud', + 'bird feed', 'petfood', 'dog food', 'cat food', 'canary seed', 'salad', + 'tossed salad', 'green salad', 'caesar salad', 'salmagundi', + 'salad nicoise', 'combination salad', 'chef\'s salad', 'potato salad', + 'pasta salad', 'macaroni salad', 'fruit salad', 'waldorf salad', + 'crab louis', 'herring salad', 'tuna fish salad', 'chicken salad', + 'coleslaw', 'aspic', 'molded salad', 'tabbouleh', 'ingredient', 'flavorer', + 'bouillon cube', 'condiment', 'herb', 'fines herbes', 'spice', + 'spearmint oil', 'lemon oil', 'wintergreen oil', 'salt', 'celery salt', + 'onion salt', 'seasoned salt', 'sour salt', 'five spice powder', 'allspice', + 'cinnamon', 'stick cinnamon', 'clove', 'cumin', 'fennel', 'ginger', + 'ginger', 'mace', 'nutmeg', 'pepper', 'black pepper', 'white pepper', + 'sassafras', 'basil', 'bay leaf', 'borage', 'hyssop', 'caraway', 'chervil', + 'chives', 'comfrey', 'coriander', 'coriander', 'costmary', 'fennel', + 'fennel', 'fennel seed', 'fenugreek', 'garlic', 'clove', 'garlic chive', + 'lemon balm', 'lovage', 'marjoram', 'mint', 'mustard seed', 'mustard', + 'chinese mustard', 'nasturtium', 'parsley', 'salad burnet', 'rosemary', + 'rue', 'sage', 'clary sage', 'savory', 'summer savory', 'winter savory', + 'sweet woodruff', 'sweet cicely', 'tarragon', 'thyme', 'turmeric', 'caper', + 'catsup', 'cardamom', 'cayenne', 'chili powder', 'chili sauce', 'chutney', + 'steak sauce', 'taco sauce', 'salsa', 'mint sauce', 'cranberry sauce', + 'curry powder', 'curry', 'lamb curry', 'duck sauce', 'horseradish', + 'marinade', 'paprika', 'spanish paprika', 'pickle', 'dill pickle', + 'bread and butter pickle', 'pickle relish', 'piccalilli', 'sweet pickle', + 'applesauce', 'soy sauce', 'tabasco', 'tomato paste', 'angelica', + 'angelica', 'almond extract', 'anise', 'chinese anise', 'juniper berries', + 'saffron', 'sesame seed', 'caraway seed', 'poppy seed', 'dill', 'dill seed', + 'celery seed', 'lemon extract', 'monosodium glutamate', 'vanilla bean', + 'vinegar', 'cider vinegar', 'wine vinegar', 'sauce', 'anchovy sauce', + 'hot sauce', 'hard sauce', 'horseradish sauce', 'bolognese pasta sauce', + 'carbonara', 'tomato sauce', 'tartare sauce', 'wine sauce', + 'marchand de vin', 'bread sauce', 'plum sauce', 'peach sauce', + 'apricot sauce', 'pesto', 'ravigote', 'remoulade sauce', 'dressing', + 'sauce louis', 'bleu cheese dressing', 'blue cheese dressing', + 'french dressing', 'lorenzo dressing', 'anchovy dressing', + 'italian dressing', 'half-and-half dressing', 'mayonnaise', + 'green mayonnaise', 'aioli', 'russian dressing', 'salad cream', + 'thousand island dressing', 'barbecue sauce', 'hollandaise', 'bearnaise', + 'bercy', 'bordelaise', 'bourguignon', 'brown sauce', 'espagnole', + 'chinese brown sauce', 'blanc', 'cheese sauce', 'chocolate sauce', + 'hot-fudge sauce', 'cocktail sauce', 'colbert', 'white sauce', + 'cream sauce', 'mornay sauce', 'demiglace', 'gravy', 'gravy', + 'spaghetti sauce', 'marinara', 'mole', 'hunter\'s sauce', 'mushroom sauce', + 'mustard sauce', 'nantua', 'hungarian sauce', 'pepper sauce', 'roux', + 'smitane', 'soubise', 'lyonnaise sauce', 'veloute', 'allemande', + 'caper sauce', 'poulette', 'curry sauce', 'worcester sauce', 'coconut milk', + 'egg', 'egg white', 'egg yolk', 'boiled egg', 'hard-boiled egg', + 'easter egg', 'easter egg', 'chocolate egg', 'candy egg', 'poached egg', + 'scrambled eggs', 'deviled egg', 'shirred egg', 'omelet', 'firm omelet', + 'french omelet', 'fluffy omelet', 'western omelet', 'souffle', 'fried egg', + 'dairy product', 'milk', 'milk', 'sour milk', 'soya milk', 'formula', + 'pasteurized milk', 'cows\' milk', 'yak\'s milk', 'goats\' milk', + 'acidophilus milk', 'raw milk', 'scalded milk', 'homogenized milk', + 'certified milk', 'powdered milk', 'nonfat dry milk', 'evaporated milk', + 'condensed milk', 'skim milk', 'semi-skimmed milk', 'whole milk', + 'low-fat milk', 'buttermilk', 'cream', 'clotted cream', 'double creme', + 'half-and-half', 'heavy cream', 'light cream', 'sour cream', + 'whipping cream', 'butter', 'clarified butter', 'ghee', 'brown butter', + 'meuniere butter', 'yogurt', 'blueberry yogurt', 'raita', 'whey', 'curd', + 'curd', 'clabber', 'cheese', 'paring', 'cream cheese', 'double cream', + 'mascarpone', 'triple cream', 'cottage cheese', 'process cheese', 'bleu', + 'stilton', 'roquefort', 'gorgonzola', 'danish blue', 'bavarian blue', + 'brie', 'brick cheese', 'camembert', 'cheddar', 'rat cheese', + 'cheshire cheese', 'double gloucester', 'edam', 'goat cheese', 'gouda', + 'grated cheese', 'hand cheese', 'liederkranz', 'limburger', 'mozzarella', + 'muenster', 'parmesan', 'quark cheese', 'ricotta', 'string cheese', + 'swiss cheese', 'emmenthal', 'gruyere', 'sapsago', 'velveeta', 'nut butter', + 'peanut butter', 'marshmallow fluff', 'onion butter', 'pimento butter', + 'shrimp butter', 'lobster butter', 'yak butter', 'spread', 'cheese spread', + 'anchovy butter', 'fishpaste', 'garlic butter', 'miso', 'wasabi', + 'snail butter', 'hummus', 'pate', 'duck pate', 'foie gras', 'tapenade', + 'tahini', 'sweetening', 'aspartame', 'honey', 'saccharin', 'sugar', 'syrup', + 'sugar syrup', 'molasses', 'sorghum', 'treacle', 'grenadine', 'maple syrup', + 'corn syrup', 'miraculous food', 'batter', 'dough', 'bread dough', + 'pancake batter', 'fritter batter', 'coq au vin', 'chicken provencale', + 'chicken and rice', 'moo goo gai pan', 'arroz con pollo', 'bacon and eggs', + 'barbecued spareribs', 'beef bourguignonne', 'beef wellington', 'bitok', + 'boiled dinner', 'boston baked beans', 'bubble and squeak', 'pasta', + 'cannelloni', 'carbonnade flamande', 'cheese souffle', 'chicken marengo', + 'chicken cordon bleu', 'maryland chicken', 'chicken paprika', + 'chicken tetrazzini', 'tetrazzini', 'chicken kiev', 'chili', 'chili dog', + 'chop suey', 'chow mein', 'codfish ball', 'coquille', + 'coquilles saint-jacques', 'croquette', 'cottage pie', 'rissole', 'dolmas', + 'egg foo yong', 'egg roll', 'eggs benedict', 'enchilada', 'falafel', + 'fish and chips', 'fondue', 'cheese fondue', 'chocolate fondue', 'fondue', + 'beef fondue', 'french toast', 'fried rice', 'frittata', 'frog legs', + 'galantine', 'gefilte fish', 'haggis', 'ham and eggs', 'hash', + 'corned beef hash', 'jambalaya', 'kabob', 'kedgeree', 'souvlaki', 'lasagna', + 'seafood newburg', 'lobster newburg', 'shrimp newburg', 'newburg sauce', + 'lobster thermidor', 'lutefisk', 'macaroni and cheese', 'macedoine', + 'meatball', 'porcupine ball', 'swedish meatball', 'meat loaf', 'moussaka', + 'osso buco', 'marrow', 'pheasant under glass', 'pigs in blankets', 'pilaf', + 'bulgur pilaf', 'pizza', 'sausage pizza', 'pepperoni pizza', 'cheese pizza', + 'anchovy pizza', 'sicilian pizza', 'poi', 'pork and beans', 'porridge', + 'oatmeal', 'loblolly', 'potpie', 'rijsttaffel', 'risotto', 'roulade', + 'fish loaf', 'salmon loaf', 'salisbury steak', 'sauerbraten', 'sauerkraut', + 'scallopine', 'veal scallopini', 'scampi', 'scotch egg', 'scotch woodcock', + 'scrapple', 'spaghetti and meatballs', 'spanish rice', 'steak tartare', + 'pepper steak', 'steak au poivre', 'beef stroganoff', 'stuffed cabbage', + 'kishke', 'stuffed peppers', 'stuffed tomato', 'stuffed tomato', + 'succotash', 'sukiyaki', 'sashimi', 'sushi', 'swiss steak', 'tamale', + 'tamale pie', 'tempura', 'teriyaki', 'terrine', 'welsh rarebit', + 'schnitzel', 'taco', 'chicken taco', 'burrito', 'beef burrito', + 'quesadilla', 'tostada', 'bean tostada', 'refried beans', 'beverage', + 'wish-wash', 'concoction', 'mix', 'filling', 'lekvar', 'potion', 'elixir', + 'elixir of life', 'philter', 'alcohol', 'proof spirit', 'home brew', + 'hooch', 'kava', 'aperitif', 'brew', 'beer', 'draft beer', 'suds', + 'munich beer', 'bock', 'lager', 'light beer', 'oktoberfest', 'pilsner', + 'shebeen', 'weissbier', 'weizenbock', 'malt', 'wort', 'malt', 'ale', + 'bitter', 'burton', 'pale ale', 'porter', 'stout', 'guinness', 'kvass', + 'mead', 'metheglin', 'hydromel', 'oenomel', 'near beer', 'ginger beer', + 'sake', 'wine', 'vintage', 'red wine', 'white wine', 'blush wine', + 'altar wine', 'sparkling wine', 'champagne', 'cold duck', 'burgundy', + 'beaujolais', 'medoc', 'canary wine', 'chablis', 'montrachet', 'chardonnay', + 'pinot noir', 'pinot blanc', 'bordeaux', 'claret', 'chianti', 'cabernet', + 'merlot', 'sauvignon blanc', 'california wine', 'cotes de provence', + 'dessert wine', 'dubonnet', 'jug wine', 'macon', 'moselle', 'muscadet', + 'plonk', 'retsina', 'rhine wine', 'riesling', 'liebfraumilch', 'rhone wine', + 'rioja', 'sack', 'saint emilion', 'soave', 'zinfandel', 'sauterne', + 'straw wine', 'table wine', 'tokay', 'vin ordinaire', 'vermouth', + 'sweet vermouth', 'dry vermouth', 'chenin blanc', 'verdicchio', 'vouvray', + 'yquem', 'generic', 'varietal', 'fortified wine', 'madeira', 'malmsey', + 'port', 'sherry', 'marsala', 'muscat', 'liquor', 'neutral spirits', + 'aqua vitae', 'eau de vie', 'moonshine', 'bathtub gin', 'aquavit', 'arrack', + 'bitters', 'brandy', 'applejack', 'calvados', 'armagnac', 'cognac', + 'grappa', 'kirsch', 'slivovitz', 'gin', 'sloe gin', 'geneva', 'grog', + 'ouzo', 'rum', 'demerara', 'jamaica rum', 'schnapps', 'pulque', 'mescal', + 'tequila', 'vodka', 'whiskey', 'blended whiskey', 'bourbon', 'corn whiskey', + 'firewater', 'irish', 'poteen', 'rye', 'scotch', 'sour mash', 'liqueur', + 'absinth', 'amaretto', 'anisette', 'benedictine', 'chartreuse', + 'coffee liqueur', 'creme de cacao', 'creme de menthe', 'creme de fraise', + 'drambuie', 'galliano', 'orange liqueur', 'curacao', 'triple sec', + 'grand marnier', 'kummel', 'maraschino', 'pastis', 'pernod', 'pousse-cafe', + 'kahlua', 'ratafia', 'sambuca', 'mixed drink', 'cocktail', 'dom pedro', + 'highball', 'mixer', 'bishop', 'bloody mary', 'virgin mary', 'bullshot', + 'cobbler', 'collins', 'cooler', 'refresher', 'smoothie', 'daiquiri', + 'strawberry daiquiri', 'nada daiquiri', 'spritzer', 'flip', 'gimlet', + 'gin and tonic', 'grasshopper', 'harvey wallbanger', 'julep', 'manhattan', + 'rob roy', 'margarita', 'martini', 'gin and it', 'vodka martini', + 'old fashioned', 'pink lady', 'sazerac', 'screwdriver', 'sidecar', + 'scotch and soda', 'sling', 'brandy sling', 'gin sling', 'rum sling', + 'sour', 'whiskey sour', 'stinger', 'swizzle', 'hot toddy', 'zombie', 'fizz', + 'irish coffee', 'cafe au lait', 'cafe noir', 'decaffeinated coffee', + 'drip coffee', 'espresso', 'caffe latte', 'cappuccino', 'iced coffee', + 'instant coffee', 'mocha', 'mocha', 'cassareep', 'turkish coffee', + 'chocolate milk', 'cider', 'hard cider', 'scrumpy', 'sweet cider', + 'mulled cider', 'perry', 'rotgut', 'slug', 'cocoa', 'criollo', 'juice', + 'fruit juice', 'nectar', 'apple juice', 'cranberry juice', 'grape juice', + 'must', 'grapefruit juice', 'orange juice', 'frozen orange juice', + 'pineapple juice', 'lemon juice', 'lime juice', 'papaya juice', + 'tomato juice', 'carrot juice', 'v-8 juice', 'koumiss', 'fruit drink', + 'lemonade', 'limeade', 'orangeade', 'malted milk', 'mate', 'mulled wine', + 'negus', 'soft drink', 'pop', 'birch beer', 'bitter lemon', 'cola', + 'cream soda', 'egg cream', 'ginger ale', 'orange soda', 'phosphate', + 'coca cola', 'pepsi', 'root beer', 'sarsaparilla', 'tonic', 'coffee bean', + 'coffee', 'cafe royale', 'fruit punch', 'milk punch', 'mimosa', + 'pina colada', 'punch', 'cup', 'champagne cup', 'claret cup', 'wassail', + 'planter\'s punch', 'white russian', 'fish house punch', 'may wine', + 'eggnog', 'cassiri', 'spruce beer', 'rickey', 'gin rickey', 'tea', + 'tea bag', 'tea', 'tea-like drink', 'cambric tea', 'cuppa', 'herb tea', + 'tisane', 'camomile tea', 'ice tea', 'sun tea', 'black tea', 'congou', + 'darjeeling', 'orange pekoe', 'souchong', 'green tea', 'hyson', 'oolong', + 'water', 'bottled water', 'branch water', 'spring water', 'sugar water', + 'drinking water', 'ice water', 'soda water', 'mineral water', 'seltzer', + 'vichy water', 'perishable', 'couscous', 'ramekin', 'multivitamin', + 'vitamin pill', 'soul food', 'mold', 'people', 'collection', 'book', + 'library', 'baseball club', 'crowd', 'class', 'core', 'concert band', + 'dance', 'wedding', 'chain', 'power breakfast', 'aerie', 'agora', + 'amusement park', 'aphelion', 'apron', 'interplanetary space', + 'interstellar space', 'intergalactic space', 'bush', 'semidesert', + 'beam-ends', 'bridgehead', 'bus stop', 'campsite', 'detention basin', + 'cemetery', 'trichion', 'city', 'business district', 'outskirts', 'borough', + 'cow pasture', 'crest', 'eparchy', 'suburb', 'stockbroker belt', + 'crawlspace', 'sheikdom', 'residence', 'domicile', 'dude ranch', 'farmland', + 'midfield', 'firebreak', 'flea market', 'battlefront', 'garbage heap', + 'benthos', 'goldfield', 'grainfield', 'half-mast', 'hemline', 'heronry', + 'hipline', 'hipline', 'hole-in-the-wall', 'junkyard', 'isoclinic line', + 'littoral', 'magnetic pole', 'grassland', 'mecca', 'observer\'s meridian', + 'prime meridian', 'nombril', 'no-parking zone', 'outdoors', 'fairground', + 'pasture', 'perihelion', 'periselene', 'locus of infection', 'kasbah', + 'waterfront', 'resort', 'resort area', 'rough', 'ashram', 'harborage', + 'scrubland', 'weald', 'wold', 'schoolyard', 'showplace', 'bedside', + 'sideline', 'ski resort', 'soil horizon', 'geological horizon', 'coal seam', + 'coalface', 'field', 'oilfield', 'temperate zone', 'terreplein', + 'three-mile limit', 'desktop', 'top', 'kampong', 'subtropics', 'barrio', + 'veld', 'vertex', 'waterline', 'high-water mark', 'low-water mark', + 'continental divide', 'zodiac', 'aegean island', 'sultanate', + 'swiss canton', 'abyssal zone', 'aerie', 'air bubble', 'alluvial flat', + 'alp', 'alpine glacier', 'anthill', 'aquifer', 'archipelago', 'arete', + 'arroyo', 'ascent', 'asterism', 'asthenosphere', 'atoll', 'bank', 'bank', + 'bar', 'barbecue pit', 'barrier reef', 'baryon', 'basin', 'beach', + 'honeycomb', 'belay', 'ben', 'berm', 'bladder stone', 'bluff', 'borrow pit', + 'brae', 'bubble', 'burrow', 'butte', 'caldera', 'canyon', 'canyonside', + 'cave', 'cavern', 'chasm', 'cirque', 'cliff', 'cloud', 'coast', 'coastland', + 'col', 'collector', 'comet', 'continental glacier', 'coral reef', 'cove', + 'crag', 'crater', 'cultivated land', 'dale', 'defile', 'delta', 'descent', + 'diapir', 'divot', 'divot', 'down', 'downhill', 'draw', 'drey', 'drumlin', + 'dune', 'escarpment', 'esker', 'fireball', 'flare star', 'floor', 'fomite', + 'foothill', 'footwall', 'foreland', 'foreshore', 'gauge boson', + 'geological formation', 'geyser', 'glacier', 'glen', 'gopher hole', 'gorge', + 'grotto', 'growler', 'gulch', 'gully', 'hail', 'highland', 'hill', + 'hillside', 'hole', 'hollow', 'hot spring', 'iceberg', 'icecap', + 'ice field', 'ice floe', 'ice mass', 'inclined fault', 'ion', 'isthmus', + 'kidney stone', 'knoll', 'kopje', 'kuiper belt', 'lake bed', 'lakefront', + 'lakeside', 'landfall', 'landfill', 'lather', 'leak', 'ledge', 'lepton', + 'lithosphere', 'lowland', 'lunar crater', 'maar', 'massif', 'meander', + 'mesa', 'meteorite', 'microfossil', 'midstream', 'molehill', 'monocline', + 'mountain', 'mountainside', 'mouth', 'mull', 'natural depression', + 'natural elevation', 'nullah', 'ocean', 'ocean floor', 'oceanfront', + 'outcrop', 'oxbow', 'pallasite', 'perforation', 'photosphere', 'piedmont', + 'piedmont glacier', 'pinetum', 'plage', 'plain', 'point', 'polar glacier', + 'pothole', 'precipice', 'promontory', 'ptyalith', 'pulsar', 'quicksand', + 'rabbit burrow', 'radiator', 'rainbow', 'range', 'rangeland', 'ravine', + 'reef', 'ridge', 'ridge', 'rift valley', 'riparian forest', 'ripple mark', + 'riverbank', 'riverbed', 'rock', 'roof', 'saltpan', 'sandbank', 'sandbar', + 'sandpit', 'sanitary landfill', 'sawpit', 'scablands', 'seashore', + 'seaside', 'seif dune', 'shell', 'shiner', 'shoal', 'shore', 'shoreline', + 'sinkhole', 'ski slope', 'sky', 'slope', 'snowcap', 'snowdrift', + 'snowfield', 'soapsuds', 'spit', 'spoor', 'spume', 'star', 'steep', + 'steppe', 'strand', 'streambed', 'sun', 'supernova', 'swale', 'swamp', + 'swell', 'tableland', 'talus', 'tangle', 'tar pit', 'terrace', + 'tidal basin', 'tideland', 'tor', 'tor', 'trapezium', 'troposphere', + 'tundra', 'twinkler', 'uphill', 'urolith', 'valley', + 'vehicle-borne transmission', 'vein', 'volcanic crater', 'volcano', 'wadi', + 'wall', 'warren', 'wasp\'s nest', 'watercourse', 'waterside', 'water table', + 'whinstone', 'wormcast', 'xenolith', 'circe', 'gryphon', 'spiritual leader', + 'messiah', 'rhea silvia', 'number one', 'adventurer', 'anomaly', + 'appointee', 'argonaut', 'ashkenazi', 'benefactor', 'color-blind person', + 'commoner', 'conservator', 'contrarian', 'contadino', 'contestant', + 'cosigner', 'discussant', 'enologist', 'entertainer', 'eulogist', + 'ex-gambler', 'experimenter', 'experimenter', 'exponent', 'ex-president', + 'face', 'female', 'finisher', 'inhabitant', 'native', 'native', 'juvenile', + 'lover', 'male', 'mediator', 'mediatrix', 'national', 'peer', + 'prize winner', 'recipient', 'religionist', 'sensualist', 'traveler', + 'unwelcome person', 'unskilled person', 'worker', 'wrongdoer', + 'black african', 'afrikaner', 'aryan', 'black', 'black woman', 'mulatto', + 'white', 'circassian', 'semite', 'chaldean', 'elamite', 'white man', 'wasp', + 'gook', 'mongol', 'tatar', 'nahuatl', 'aztec', 'olmec', 'biloxi', + 'blackfoot', 'brule', 'caddo', 'cheyenne', 'chickasaw', 'cocopa', + 'comanche', 'creek', 'delaware', 'diegueno', 'esselen', 'eyeish', + 'havasupai', 'hunkpapa', 'iowa', 'kalapooia', 'kamia', 'kekchi', 'kichai', + 'kickapoo', 'kiliwa', 'malecite', 'maricopa', 'mohican', 'muskhogean', + 'navaho', 'nootka', 'oglala', 'osage', 'oneida', 'paiute', 'passamaquody', + 'penobscot', 'penutian', 'potawatomi', 'powhatan', 'kachina', 'salish', + 'shahaptian', 'shasta', 'shawnee', 'sihasapa', 'teton', 'taracahitian', + 'tarahumara', 'tuscarora', 'tutelo', 'yana', 'yavapai', 'yokuts', 'yuma', + 'gadaba', 'kolam', 'kui', 'toda', 'tulu', 'gujarati', 'kashmiri', 'punjabi', + 'slav', 'anabaptist', 'adventist', 'gentile', 'gentile', 'catholic', + 'old catholic', 'uniat', 'copt', 'jewess', 'jihadist', 'buddhist', + 'zen buddhist', 'mahayanist', 'swami', 'hare krishna', 'shintoist', + 'eurafrican', 'eurasian', 'gael', 'frank', 'afghan', 'albanian', 'algerian', + 'altaic', 'andorran', 'angolan', 'anguillan', 'austrian', 'bahamian', + 'bahraini', 'basotho', 'herero', 'luba', 'barbadian', 'bolivian', 'bornean', + 'carioca', 'tupi', 'bruneian', 'bulgarian', 'byelorussian', 'cameroonian', + 'canadian', 'french canadian', 'central american', 'chilean', 'congolese', + 'cypriot', 'dane', 'djiboutian', 'britisher', 'english person', + 'englishwoman', 'anglo-saxon', 'angle', 'west saxon', 'lombard', 'limey', + 'cantabrigian', 'cornishman', 'cornishwoman', 'lancastrian', 'lancastrian', + 'geordie', 'oxonian', 'ethiopian', 'amhara', 'eritrean', 'finn', 'komi', + 'livonian', 'lithuanian', 'selkup', 'parisian', 'parisienne', 'creole', + 'creole', 'gabonese', 'greek', 'dorian', 'athenian', 'laconian', 'guyanese', + 'haitian', 'malay', 'moro', 'netherlander', 'icelander', 'iraqi', + 'irishman', 'irishwoman', 'dubliner', 'italian', 'roman', 'sabine', + 'japanese', 'jordanian', 'korean', 'kenyan', 'lao', 'lapp', + 'latin american', 'lebanese', 'levantine', 'liberian', 'luxemburger', + 'macedonian', 'sabahan', 'mexican', 'chicano', 'mexican-american', + 'namibian', 'nauruan', 'gurkha', 'new zealander', 'nicaraguan', 'nigerian', + 'hausa', 'north american', 'nova scotian', 'omani', 'pakistani', 'brahui', + 'south american indian', 'carib', 'filipino', 'polynesian', 'qatari', + 'romanian', 'muscovite', 'georgian', 'sarawakian', 'scandinavian', + 'senegalese', 'slovene', 'south african', 'south american', 'sudanese', + 'syrian', 'tahitian', 'tanzanian', 'tibetan', 'togolese', 'tuareg', 'turki', + 'chuvash', 'turkoman', 'uzbek', 'ugandan', 'ukranian', 'yakut', 'tungus', + 'igbo', 'american', 'anglo-american', 'alaska native', 'arkansan', + 'carolinian', 'coloradan', 'connecticuter', 'delawarean', 'floridian', + 'german american', 'illinoisan', 'mainer', 'marylander', 'minnesotan', + 'nebraskan', 'new hampshirite', 'new jerseyan', 'new yorker', + 'north carolinian', 'oregonian', 'pennsylvanian', 'texan', 'utahan', + 'uruguayan', 'vietnamese', 'gambian', 'east german', 'berliner', 'prussian', + 'ghanian', 'guinean', 'papuan', 'walloon', 'yemeni', 'yugoslav', 'serbian', + 'xhosa', 'zairese', 'zimbabwean', 'zulu', 'gemini', 'sagittarius', 'pisces', + 'abbe', 'abbess', 'abnegator', 'abridger', 'abstractor', 'absconder', + 'absolver', 'abecedarian', 'aberrant', 'abettor', 'abhorrer', 'abomination', + 'abseiler', 'abstainer', 'academic administrator', 'academician', + 'accessory before the fact', 'companion', 'accompanist', 'accomplice', + 'account executive', 'accused', 'accuser', 'acid head', 'acquaintance', + 'acquirer', 'aerialist', 'action officer', 'active', 'active citizen', + 'actor', 'actor', 'addict', 'adducer', 'adjuster', 'adjutant', + 'adjutant general', 'admirer', 'adoptee', 'adulterer', 'adulteress', + 'advertiser', 'advisee', 'advocate', 'aeronautical engineer', 'affiliate', + 'affluent', 'aficionado', 'buck sergeant', 'agent-in-place', 'aggravator', + 'agitator', 'agnostic', 'agnostic', 'agonist', 'agony aunt', + 'agriculturist', 'air attache', 'air force officer', 'airhead', + 'air traveler', 'alarmist', 'albino', 'alcoholic', 'alderman', 'alexic', + 'alienee', 'alienor', 'aliterate', 'algebraist', 'allegorizer', + 'alliterator', 'almoner', 'alpinist', 'altar boy', 'alto', 'ambassador', + 'ambassador', 'ambusher', 'amicus curiae', 'amoralist', 'amputee', + 'analogist', 'analphabet', 'analyst', 'industry analyst', + 'market strategist', 'anarchist', 'anathema', 'ancestor', 'anchor', + 'ancient', 'anecdotist', 'angler', 'animator', 'animist', 'annotator', + 'announcer', 'announcer', 'anti', 'anti-american', 'anti-semite', 'anzac', + 'ape-man', 'aphakic', 'appellant', 'appointee', 'apprehender', 'april fool', + 'aspirant', 'appreciator', 'appropriator', 'arabist', 'archaist', + 'archbishop', 'archer', 'architect', 'archivist', 'archpriest', + 'aristotelian', 'armiger', 'army attache', 'army engineer', 'army officer', + 'arranger', 'arrival', 'arthritic', 'articulator', 'artilleryman', + 'artist\'s model', 'assayer', 'assemblyman', 'assemblywoman', 'assenter', + 'asserter', 'assignee', 'assistant', 'assistant professor', 'associate', + 'associate', 'associate professor', 'astronaut', 'cosmographer', 'atheist', + 'athlete', 'attendant', 'attorney general', 'auditor', 'augur', 'aunt', + 'au pair girl', 'authoritarian', 'authority', 'authorizer', + 'automobile mechanic', 'aviator', 'aviatrix', 'ayah', 'babu', 'baby', + 'baby', 'baby boomer', 'baby farmer', 'back', 'backbencher', 'backpacker', + 'backroom boy', 'backscratcher', 'bad person', 'baggage', 'bag lady', + 'bailee', 'bailiff', 'bailor', 'bairn', 'baker', 'balancer', 'balker', + 'ball-buster', 'ball carrier', 'ballet dancer', 'ballet master', + 'ballet mistress', 'balletomane', 'ball hawk', 'balloonist', 'ballplayer', + 'bullfighter', 'banderillero', 'matador', 'picador', 'bandsman', 'banker', + 'bank robber', 'bankrupt', 'bantamweight', 'barmaid', 'baron', 'baron', + 'baron', 'bartender', 'baseball coach', 'base runner', 'basketball player', + 'basketweaver', 'basket maker', 'bass', 'bastard', 'bat boy', 'bather', + 'batman', 'baton twirler', 'bavarian', 'beadsman', 'beard', 'beatnik', + 'beauty consultant', 'bedouin', 'bedwetter', 'beekeeper', 'beer drinker', + 'beggarman', 'beggarwoman', 'beldam', 'theist', 'believer', 'bell founder', + 'benedick', 'berserker', 'besieger', 'best', 'betrothed', 'big brother', + 'bigot', 'big shot', 'big sister', 'billiard player', 'biochemist', + 'biographer', 'bird fancier', 'birth', 'birth-control campaigner', + 'bisexual', 'black belt', 'blackmailer', 'black muslim', 'blacksmith', + 'blade', 'bleacher', 'blind date', 'bluecoat', 'bluestocking', + 'boatbuilder', 'boatman', 'boatswain', 'bobby', 'bodyguard', 'boffin', + 'bolshevik', 'bolshevik', 'bombshell', 'bondman', 'bondwoman', 'bondwoman', + 'bond servant', 'book agent', 'bookbinder', 'bookkeeper', 'bookmaker', + 'bookworm', 'booster', 'bootblack', 'bootlegger', 'bootmaker', 'borderer', + 'border patrolman', 'botanist', 'bottom feeder', 'boulevardier', + 'bounty hunter', 'bounty hunter', 'bourbon', 'bowler', 'slugger', 'cub', + 'boy scout', 'boy scout', 'boy wonder', 'bragger', 'brahman', 'brawler', + 'breadwinner', 'breaststroker', 'breeder', 'brick', 'bride', 'bridesmaid', + 'bridge agent', 'broadcast journalist', 'brother', 'brother-in-law', + 'browser', 'brummie', 'buddy', 'bull', 'bully', 'bunny', 'burglar', + 'bursar', 'busboy', 'business editor', 'business traveler', 'buster', + 'busybody', 'buttinsky', 'cabinetmaker', 'caddie', 'cadet', 'caller', + 'call girl', 'calligrapher', 'campaigner', 'camper', 'camp follower', + 'candidate', 'canonist', 'capitalist', 'captain', 'captain', 'captain', + 'captain', 'captive', 'captive', 'cardinal', 'cardiologist', 'card player', + 'cardsharp', 'careerist', 'career man', 'caregiver', 'caretaker', + 'caretaker', 'caricaturist', 'carillonneur', 'caroler', 'carpenter', + 'carper', 'cartesian', 'cashier', 'casualty', 'casualty', 'casuist', + 'catechist', 'catechumen', 'caterer', 'catholicos', 'cat fancier', + 'cavalier', 'cavalryman', 'caveman', 'celebrant', 'celebrant', 'celebrity', + 'cellist', 'censor', 'censor', 'centenarian', 'centrist', 'centurion', + 'certified public accountant', 'chachka', 'chambermaid', 'chameleon', + 'champion', 'chandler', 'prison chaplain', 'charcoal burner', + 'charge d\'affaires', 'charioteer', 'charmer', 'chartered accountant', + 'chartist', 'charwoman', 'male chauvinist', 'cheapskate', 'chechen', + 'checker', 'cheerer', 'cheerleader', 'cheerleader', 'cheops', + 'chess master', 'chief executive officer', 'chief of staff', + 'chief petty officer', 'chief secretary', 'child', 'child', 'child', + 'child prodigy', 'chimneysweeper', 'chiropractor', 'chit', 'choker', + 'choragus', 'choreographer', 'chorus girl', 'chosen', 'cicerone', + 'cigar smoker', 'cipher', 'circus acrobat', 'citizen', 'city editor', + 'city father', 'city man', 'city slicker', 'civic leader', + 'civil rights leader', 'cleaner', 'clergyman', 'cleric', 'clerk', + 'clever dick', 'climatologist', 'climber', 'clinician', 'closer', + 'closet queen', 'clown', 'clown', 'coach', 'coach', 'pitching coach', + 'coachman', 'coal miner', 'coastguardsman', 'cobber', 'cobbler', 'codger', + 'co-beneficiary', 'cog', 'cognitive neuroscientist', 'coiffeur', 'coiner', + 'collaborator', 'colleen', 'college student', 'collegian', 'colonial', + 'colonialist', 'colonizer', 'coloratura', 'color guard', 'colossus', + 'comedian', 'comedienne', 'comer', 'commander', 'commander in chief', + 'commanding officer', 'commissar', 'commissioned officer', + 'commissioned military officer', 'commissioner', 'commissioner', + 'committee member', 'committeewoman', 'commodore', 'communicant', + 'communist', 'communist', 'commuter', 'compere', 'complexifier', + 'compulsive', 'computational linguist', 'computer scientist', + 'computer user', 'comrade', 'concert-goer', 'conciliator', 'conductor', + 'confectioner', 'confederate', 'confessor', 'confidant', 'confucian', 'rep', + 'conqueror', 'conservative', 'nonconformist', 'anglican', 'consignee', + 'consigner', 'constable', 'constructivist', 'contractor', 'contralto', + 'contributor', 'control freak', 'convalescent', 'convener', 'convict', + 'copilot', 'copycat', 'coreligionist', 'cornerback', 'corporatist', + 'correspondent', 'cosmetician', 'cosmopolitan', 'cossack', + 'cost accountant', 'co-star', 'costumier', 'cotter', 'cotter', 'counselor', + 'counterterrorist', 'counterspy', 'countess', 'compromiser', 'countrywoman', + 'county agent', 'courtier', 'cousin', 'cover girl', 'cow', 'craftsman', + 'craftsman', 'crapshooter', 'crazy', 'creature', 'creditor', 'creep', + 'criminologist', 'critic', 'croesus', 'cross-examiner', 'crossover voter', + 'croupier', 'crown prince', 'crown princess', 'cryptanalyst', 'cub scout', + 'cuckold', 'cultist', 'curandera', 'curate', 'curator', 'customer agent', + 'cutter', 'cyberpunk', 'cyborg', 'cymbalist', 'cynic', 'cytogeneticist', + 'cytologist', 'czar', 'czar', 'dad', 'dairyman', 'dalai lama', 'dallier', + 'dancer', 'dancer', 'clog dancer', 'dancing-master', 'dark horse', + 'darling', 'date', 'daughter', 'dawdler', 'day boarder', 'day laborer', + 'deacon', 'deaconess', 'deadeye', 'deipnosophist', 'dropout', 'deadhead', + 'deaf person', 'debtor', 'deckhand', 'defamer', 'defense contractor', + 'deist', 'delegate', 'deliveryman', 'demagogue', 'demigod', 'demographer', + 'demonstrator', 'den mother', 'department head', 'depositor', 'deputy', + 'dermatologist', 'descender', 'designated hitter', 'designer', 'desk clerk', + 'desk officer', 'desk sergeant', 'detainee', 'detective', 'detective', + 'detractor', 'developer', 'deviationist', 'devisee', 'devisor', 'devourer', + 'dialectician', 'diarist', 'dietician', 'diocesan', 'director', 'director', + 'dirty old man', 'disbeliever', 'disk jockey', 'dispatcher', + 'distortionist', 'distributor', 'district attorney', 'district manager', + 'diver', 'divorcee', 'ex-wife', 'divorce lawyer', 'docent', 'doctor', + 'dodo', 'doge', 'dog in the manger', 'dogmatist', 'dolichocephalic', + 'domestic partner', 'dominican', 'dominus', 'don', 'donatist', 'donna', + 'dosser', 'double', 'double-crosser', 'down-and-out', 'doyenne', + 'draftsman', 'dramatist', 'dreamer', 'dressmaker', 'dressmaker\'s model', + 'dribbler', 'dribbler', 'drinker', 'drinker', 'drug addict', 'drug user', + 'druid', 'drum majorette', 'drummer', 'drunk', 'drunkard', 'druze', 'dry', + 'dry nurse', 'duchess', 'duke', 'duffer', 'dunker', 'dutch uncle', + 'dyspeptic', 'eager beaver', 'earl', 'earner', 'eavesdropper', 'eccentric', + 'eclectic', 'econometrician', 'economist', 'ectomorph', 'editor', + 'egocentric', 'egotist', 'ejaculator', 'elder', 'elder statesman', + 'elected official', 'electrician', 'elegist', 'elocutionist', 'emancipator', + 'embryologist', 'emeritus', 'emigrant', 'emissary', 'empress', 'employee', + 'employer', 'enchantress', 'enchantress', 'encyclopedist', 'endomorph', + 'enemy', 'energizer', 'end man', 'end man', 'endorser', 'enjoyer', + 'enlisted woman', 'enophile', 'entrant', 'entrant', 'entrepreneur', 'envoy', + 'enzymologist', 'eparch', 'epidemiologist', 'epigone', 'epileptic', + 'episcopalian', 'equerry', 'equerry', 'erotic', 'escapee', 'escapist', + 'eskimo', 'espionage agent', 'esthetician', 'etcher', 'ethnologist', + 'etonian', 'etymologist', 'evangelist', 'evangelist', 'event planner', + 'examiner', 'examiner', 'exarch', 'executant', 'executive secretary', + 'executive vice president', 'executrix', 'exegete', 'exhibitor', + 'exhibitionist', 'exile', 'existentialist', 'exorcist', 'ex-spouse', + 'extern', 'extremist', 'extrovert', 'eyewitness', 'facilitator', + 'fairy godmother', 'falangist', 'falconer', 'falsifier', 'familiar', 'fan', + 'fanatic', 'fancier', 'farm boy', 'farmer', 'farmhand', 'fascist', + 'fascista', 'fatalist', 'father', 'father', 'father-figure', + 'father-in-law', 'fauntleroy', 'fauve', 'favorite son', 'featherweight', + 'federalist', 'fellow traveler', 'female aristocrat', 'female offspring', + 'female child', 'fence', 'fiance', 'fielder', 'field judge', + 'fighter pilot', 'filer', 'film director', 'finder', 'fire chief', + 'fire-eater', 'fire-eater', 'fireman', 'fire marshall', 'fire walker', + 'first baseman', 'firstborn', 'first lady', 'first lieutenant', + 'first offender', 'first sergeant', 'fishmonger', 'flagellant', + 'flag officer', 'flak catcher', 'flanker back', 'flapper', 'flatmate', + 'flatterer', 'flibbertigibbet', 'flight surgeon', 'floorwalker', 'flop', + 'florentine', 'flower girl', 'flower girl', 'flutist', 'fly-by-night', + 'flyweight', 'flyweight', 'foe', 'folk dancer', 'folk poet', 'follower', + 'football hero', 'football player', 'footman', 'forefather', 'foremother', + 'foreign agent', 'foreigner', 'boss', 'foreman', 'forester', 'forewoman', + 'forger', 'forward', 'foster-brother', 'foster-father', 'foster-mother', + 'foster-sister', 'foster-son', 'founder', 'foundress', 'four-minute man', + 'framer', 'francophobe', 'freak', 'free agent', 'free agent', + 'freedom rider', 'free-liver', 'freeloader', 'free trader', 'freudian', + 'friar', 'monk', 'frontierswoman', 'front man', 'frotteur', 'fucker', + 'fucker', 'fuddy-duddy', 'fullback', 'funambulist', 'fundamentalist', + 'fundraiser', 'futurist', 'gadgeteer', 'gagman', 'gagman', 'gainer', 'gal', + 'galoot', 'gambist', 'gambler', 'gamine', 'garbage man', 'gardener', + 'garment cutter', 'garroter', 'gasman', 'gastroenterologist', 'gatherer', + 'gawker', 'gendarme', 'general', 'generator', 'geneticist', 'genitor', + 'gent', 'geologist', 'geophysicist', 'ghostwriter', 'gibson girl', 'girl', + 'girlfriend', 'girlfriend', 'girl wonder', 'girondist', 'gitano', + 'gladiator', 'glassblower', 'gleaner', 'goat herder', 'godchild', + 'godfather', 'godparent', 'godson', 'gofer', 'goffer', 'goldsmith', + 'golfer', 'gondolier', 'good guy', 'good old boy', 'good samaritan', + 'gossip columnist', 'gouger', 'governor general', 'grabber', 'grader', + 'graduate nurse', 'grammarian', 'granddaughter', 'grande dame', + 'grandfather', 'grand inquisitor', 'grandma', 'grandmaster', 'grandparent', + 'grantee', 'granter', 'grass widower', 'great-aunt', 'great grandchild', + 'great granddaughter', 'great grandmother', 'great grandparent', + 'great grandson', 'great-nephew', 'great-niece', 'green beret', 'grenadier', + 'greeter', 'gringo', 'grinner', 'grocer', 'groom', 'groom', 'grouch', + 'group captain', 'grunter', 'prison guard', 'guard', 'guesser', 'guest', + 'guest', 'guest of honor', 'guest worker', 'guide', 'guitarist', + 'gunnery sergeant', 'guru', 'guru', 'guvnor', 'guy', 'gymnast', 'gym rat', + 'gynecologist', 'gypsy', 'hack', 'hacker', 'haggler', 'hairdresser', + 'hakim', 'hakka', 'halberdier', 'halfback', 'half blood', 'hand', + 'animal trainer', 'handyman', 'hang glider', 'hardliner', 'harlequin', + 'harmonizer', 'hash head', 'hatchet man', 'hater', 'hatmaker', 'headman', + 'headmaster', 'head nurse', 'hearer', 'heartbreaker', 'heathen', + 'heavyweight', 'heavy', 'heckler', 'hedger', 'hedger', 'hedonist', 'heir', + 'heir apparent', 'heiress', 'heir presumptive', 'hellion', 'helmsman', + 'hire', 'hematologist', 'hemiplegic', 'herald', 'herbalist', 'herder', + 'hermaphrodite', 'heroine', 'heroin addict', 'hero worshiper', 'herr', + 'highbinder', 'highbrow', 'high commissioner', 'highflier', 'highlander', + 'high-muck-a-muck', 'high priest', 'highjacker', 'hireling', 'historian', + 'hitchhiker', 'hitter', 'hobbyist', 'holdout', 'holdover', 'holdup man', + 'homeboy', 'homeboy', 'home buyer', 'homegirl', 'homeless', 'homeopath', + 'honest woman', 'honor guard', 'hooker', 'hoper', 'hornist', 'horseman', + 'horse trader', 'horsewoman', 'horse wrangler', 'horticulturist', + 'hospital chaplain', 'host', 'host', 'hostess', 'hotelier', 'housekeeper', + 'housemaster', 'housemate', 'house physician', 'house sitter', + 'housing commissioner', 'huckster', 'hugger', 'humanist', 'humanitarian', + 'hunk', 'huntress', 'ex-husband', 'hydrologist', 'hyperope', 'hypertensive', + 'hypnotist', 'hypocrite', 'iceman', 'iconoclast', 'ideologist', 'idol', + 'idolizer', 'imam', 'imperialist', 'important person', 'inamorato', + 'incumbent', 'incurable', 'inductee', 'industrialist', 'infanticide', + 'inferior', 'infernal', 'infielder', 'infiltrator', 'informer', 'ingenue', + 'ingenue', 'polymath', 'in-law', 'inquiry agent', 'inspector', + 'inspector general', 'instigator', 'insurance broker', 'insurgent', + 'intelligence analyst', 'interior designer', 'interlocutor', 'interlocutor', + 'international grandmaster', 'internationalist', 'internist', 'interpreter', + 'interpreter', 'intervenor', 'introvert', 'invader', 'invalidator', + 'investigator', 'investor', 'invigilator', 'irreligionist', 'ivy leaguer', + 'jack of all trades', 'jacksonian', 'jane doe', 'janissary', 'jat', + 'javanese', 'jekyll and hyde', 'jester', 'jesuit', 'jezebel', 'jilt', + 'jobber', 'job candidate', 'job\'s comforter', 'jockey', 'john doe', + 'journalist', 'judge', 'judge advocate', 'juggler', 'jungian', 'junior', + 'junior', 'junior', 'junior lightweight', 'junior middleweight', 'jurist', + 'juror', 'justice of the peace', 'justiciar', 'kachina', 'keyboardist', + 'khedive', 'kingmaker', 'king', 'king\'s counsel', 'counsel to the crown', + 'kin', 'enate', 'kink', 'kinswoman', 'kisser', 'kitchen help', + 'kitchen police', 'klansman', 'kleptomaniac', 'kneeler', 'knight', + 'knocker', 'knower', 'know-it-all', 'kolkhoznik', 'kshatriya', + 'labor coach', 'laborer', 'labourite', 'lady', 'lady-in-waiting', + 'lady\'s maid', 'lama', 'lamb', 'lame duck', 'lamplighter', 'land agent', + 'landgrave', 'landlubber', 'landlubber', 'landowner', 'landscape architect', + 'langlaufer', 'languisher', 'lapidary', 'lass', 'latin', 'latin', + 'latitudinarian', 'jehovah\'s witness', 'law agent', 'lawgiver', 'lawman', + 'law student', 'lawyer', 'lay reader', 'lazybones', 'leaker', 'leaseholder', + 'lector', 'lector', 'lecturer', 'left-hander', 'legal representative', + 'legate', 'legatee', 'legionnaire', 'letterman', 'liberator', 'licenser', + 'licentiate', 'lieutenant', 'lieutenant colonel', 'lieutenant commander', + 'lieutenant junior grade', 'life', 'lifeguard', 'life tenant', + 'light flyweight', 'light heavyweight', 'light heavyweight', + 'light-o\'-love', 'lightweight', 'lightweight', 'lightweight', + 'lilliputian', 'limnologist', 'lineman', 'line officer', 'lion-hunter', + 'lisper', 'lister', 'literary critic', 'literate', 'litigant', 'litterer', + 'little brother', 'little sister', 'lobbyist', 'locksmith', 'locum tenens', + 'lord', 'loser', 'loser', 'failure', 'lothario', 'loudmouth', + 'lowerclassman', 'lowlander', 'loyalist', 'luddite', 'lumberman', 'lumper', + 'bedlamite', 'pyromaniac', 'lutist', 'lutheran', 'lyricist', 'macebearer', + 'machinist', 'madame', 'maenad', 'maestro', 'magdalen', 'magician', 'magus', + 'maharani', 'mahatma', 'maid', 'maid', 'major', 'major', 'major-domo', + 'maker', 'malahini', 'malcontent', 'malik', 'malingerer', 'malthusian', + 'adonis', 'man', 'man', 'manageress', 'mandarin', 'maneuverer', 'maniac', + 'manichaean', 'manicurist', 'manipulator', 'man-at-arms', 'man of action', + 'man of letters', 'manufacturer', 'marcher', 'marchioness', 'margrave', + 'margrave', 'marine', 'marquess', 'marquis', 'marshal', 'martinet', + 'mascot', 'masochist', 'mason', 'masquerader', 'masseur', 'masseuse', + 'master', 'master', 'master-at-arms', 'master of ceremonies', 'masturbator', + 'matchmaker', 'mate', 'mate', 'mate', 'mater', 'material', 'materialist', + 'matriarch', 'matriarch', 'matriculate', 'matron', 'mayor', 'mayoress', + 'mechanical engineer', 'medalist', 'medical officer', + 'medical practitioner', 'medical scientist', 'medium', 'megalomaniac', + 'melancholic', 'melkite', 'melter', 'nonmember', 'board member', 'clansman', + 'memorizer', 'mendelian', 'mender', 'mesoamerican', 'messmate', 'mestiza', + 'meteorologist', 'meter maid', 'methodist', 'metis', 'metropolitan', + 'mezzo-soprano', 'microeconomist', 'middle-aged man', 'middlebrow', + 'middleweight', 'midwife', 'mikado', 'milanese', 'miler', 'miles gloriosus', + 'military attache', 'military chaplain', 'military leader', + 'military officer', 'military policeman', 'mill agent', 'mill-hand', + 'millionairess', 'millwright', 'minder', 'mining engineer', 'minister', + 'ministrant', 'minor leaguer', 'minuteman', 'misanthrope', 'misfit', + 'mistress', 'mistress', 'mixed-blood', 'model', 'class act', 'modeler', + 'modifier', 'molecular biologist', 'monegasque', 'monetarist', + 'moneygrubber', 'moneymaker', 'mongoloid', 'monolingual', 'monologist', + 'moonlighter', 'moralist', 'morosoph', 'morris dancer', 'mortal enemy', + 'mortgagee', 'mortician', 'moss-trooper', 'mother', 'mother', 'mother', + 'mother figure', 'mother hen', 'mother-in-law', 'mother\'s boy', + 'mother\'s daughter', 'motorcycle cop', 'motorcyclist', 'mound builder', + 'mountebank', 'mourner', 'mouthpiece', 'mover', 'moviegoer', 'muffin man', + 'mugwump', 'mullah', 'muncher', 'murderess', 'murder suspect', 'musher', + 'musician', 'musicologist', 'music teacher', 'musketeer', 'muslimah', + 'mutilator', 'mutineer', 'mute', 'mutterer', 'muzzler', 'mycenaen', + 'mycologist', 'myope', 'myrmidon', 'mystic', 'mythologist', 'naif', + 'nailer', 'namby-pamby', 'name dropper', 'namer', 'nan', 'nanny', 'narc', + 'narcissist', 'nark', 'nationalist', 'nautch girl', 'naval commander', + 'navy seal', 'obstructionist', 'nazarene', 'nazarene', 'nazi', 'nebbish', + 'necker', 'neonate', 'nephew', 'neurobiologist', 'neurologist', + 'neurosurgeon', 'neutral', 'neutralist', 'newcomer', 'newcomer', + 'new dealer', 'newspaper editor', 'newsreader', 'newtonian', 'niece', + 'niggard', 'night porter', 'night rider', 'nimby', 'niqaabi', 'nitpicker', + 'nobelist', 'noc', 'noncandidate', 'noncommissioned officer', 'nondescript', + 'nondriver', 'nonparticipant', 'nonperson', 'nonresident', 'nonsmoker', + 'northern baptist', 'noticer', 'novelist', 'novitiate', 'nuclear chemist', + 'nudger', 'nullipara', 'number theorist', 'nurse', 'nursling', 'nymph', + 'nymphet', 'nympholept', 'nymphomaniac', 'oarswoman', 'oboist', + 'obscurantist', 'observer', 'obstetrician', 'occupier', 'occultist', + 'wine lover', 'offerer', 'office-bearer', 'office boy', 'officeholder', + 'officiant', 'federal', 'oilman', 'oil tycoon', 'old-age pensioner', + 'old boy', 'old lady', 'old man', 'oldster', 'old-timer', 'old woman', + 'oligarch', 'olympian', 'omnivore', 'oncologist', 'onlooker', 'onomancer', + 'operator', 'opportunist', 'optimist', 'orangeman', 'orator', 'orderly', + 'orderly', 'orderly sergeant', 'ordinand', 'ordinary', 'organ-grinder', + 'organist', 'organization man', 'organizer', 'organizer', 'originator', + 'ornithologist', 'orphan', 'orphan', 'osteopath', 'out-and-outer', + 'outdoorswoman', 'outfielder', 'outfielder', 'right fielder', + 'right-handed pitcher', 'outlier', 'owner-occupier', 'oyabun', 'packrat', + 'padrone', 'padrone', 'page', 'painter', 'paleo-american', 'paleontologist', + 'pallbearer', 'palmist', 'pamperer', 'panchen lama', 'panelist', + 'panhandler', 'paparazzo', 'paperboy', 'paperhanger', 'paperhanger', + 'papoose', 'pardoner', 'paretic', 'parishioner', 'park commissioner', + 'parliamentarian', 'parliamentary agent', 'parodist', 'parricide', 'parrot', + 'partaker', 'part-timer', 'party', 'party man', 'passenger', 'passer', + 'paster', 'pater', 'patient', 'patriarch', 'patriarch', 'patriarch', + 'patriot', 'patron', 'patternmaker', 'pawnbroker', 'payer', 'peacekeeper', + 'peasant', 'pedant', 'peddler', 'pederast', 'penologist', 'pentathlete', + 'pentecostal', 'percussionist', 'periodontist', 'peshmerga', 'personality', + 'personal representative', 'personage', 'persona grata', + 'persona non grata', 'personification', 'perspirer', 'pervert', 'pessimist', + 'pest', 'peter pan', 'petitioner', 'petit juror', 'pet sitter', 'petter', + 'pharaoh', 'pharmacist', 'philanthropist', 'philatelist', 'philosopher', + 'phonetician', 'phonologist', 'photojournalist', 'photometrist', + 'physical therapist', 'physicist', 'piano maker', 'picker', 'picnicker', + 'pilgrim', 'pill', 'pillar', 'pill head', 'pilot', 'piltdown man', 'pimp', + 'pipe smoker', 'pip-squeak', 'pisser', 'pitcher', 'pitchman', 'placeman', + 'placer miner', 'plagiarist', 'plainsman', 'planner', 'planter', + 'plasterer', 'platinum blond', 'platitudinarian', 'playboy', 'player', + 'playmate', 'pleaser', 'pledger', 'plenipotentiary', 'plier', 'plodder', + 'plodder', 'plotter', 'plumber', 'pluralist', 'pluralist', 'poet', + 'pointsman', 'point woman', 'policyholder', 'political prisoner', + 'political scientist', 'politician', 'politician', 'pollster', 'polluter', + 'pool player', 'portraitist', 'poseuse', 'positivist', 'postdoc', + 'poster girl', 'postulator', 'private citizen', 'problem solver', + 'pro-lifer', 'prosthetist', 'postulant', 'potboy', 'poultryman', + 'power user', 'power worker', 'practitioner', 'prayer', 'preceptor', + 'predecessor', 'preemptor', 'preemptor', 'premature baby', 'presbyter', + 'presenter', 'presentist', 'preserver', 'president', + 'president of the united states', 'president', 'press agent', + 'press photographer', 'priest', 'prima ballerina', 'prima donna', + 'prima donna', 'primigravida', 'primordial dwarf', 'prince charming', + 'prince consort', 'princeling', 'prince of wales', 'princess', + 'princess royal', 'principal', 'principal', 'print seller', 'prior', + 'private', 'probationer', 'processor', 'process-server', 'proconsul', + 'proconsul', 'proctologist', 'proctor', 'procurator', 'procurer', + 'profit taker', 'programmer', 'promiser', 'promoter', 'promulgator', + 'propagandist', 'propagator', 'property man', 'prophetess', 'prophet', + 'prosecutor', 'prospector', 'protectionist', 'protegee', 'protozoologist', + 'provost marshal', 'pruner', 'psalmist', 'psephologist', 'psychiatrist', + 'psychic', 'psycholinguist', 'psychophysicist', 'publican', 'pudge', + 'puerpera', 'punching bag', 'punter', 'punter', 'puppeteer', 'puppy', + 'purchasing agent', 'puritan', 'puritan', 'pursuer', 'pusher', 'pusher', + 'pusher', 'putz', 'pygmy', 'qadi', 'quadriplegic', 'quadruplet', 'quaker', + 'quarter', 'quarterback', 'quartermaster', 'quartermaster general', + 'quebecois', 'queen', 'queen of england', 'queen', 'queen', 'queen consort', + 'queen mother', 'queen\'s counsel', 'question master', 'quick study', + 'quietist', 'quitter', 'rabbi', 'racist', 'radiobiologist', + 'radiologic technologist', 'radiologist', 'rainmaker', 'raiser', 'raja', + 'rake', 'ramrod', 'ranch hand', 'ranker', 'ranter', 'rape suspect', + 'rapper', 'rapporteur', 'rare bird', 'ratepayer', 'raw recruit', 'reader', + 'reading teacher', 'realist', 'real estate broker', 'rear admiral', + 'receiver', 'reciter', 'recruit', 'recruit', 'recruiter', + 'recruiting-sergeant', 'redcap', 'redhead', 'redneck', 'reeler', + 'reenactor', 'referral', 'referee', 'refiner', 'reform jew', + 'registered nurse', 'registrar', 'regius professor', 'reliever', + 'anchorite', 'religious leader', 'remover', 'renaissance man', 'renegade', + 'rentier', 'repairman', 'reporter', 'newswoman', 'representative', + 'reprobate', 'rescuer', 'reservist', 'resident commissioner', 'respecter', + 'restaurateur', 'restrainer', 'retailer', 'retiree', 'returning officer', + 'revenant', 'revisionist', 'revolutionist', 'rheumatologist', + 'rhodesian man', 'rhymer', 'rich person', 'rider', 'riding master', + 'rifleman', 'right-hander', 'right-hand man', 'ringer', 'ringleader', + 'roadman', 'roarer', 'rocket engineer', 'rocket scientist', 'rock star', + 'romanov', 'romanticist', 'ropemaker', 'roper', 'roper', 'ropewalker', + 'rosebud', 'rosicrucian', 'mountie', 'rough rider', 'roundhead', + 'civil authority', 'runner', 'runner', 'runner', 'running back', 'rusher', + 'rustic', 'saboteur', 'sadist', 'sailing master', 'sailor', 'salesgirl', + 'salesman', 'salesperson', 'salvager', 'sandwichman', 'sangoma', 'sannup', + 'sapper', 'sassenach', 'satrap', 'saunterer', 'savoyard', 'sawyer', + 'scalper', 'scandalmonger', 'scapegrace', 'scene painter', 'schemer', + 'schizophrenic', 'schlemiel', 'schlockmeister', 'scholar', 'scholiast', + 'schoolchild', 'schoolfriend', 'schoolman', 'schoolmaster', 'schoolmate', + 'scientist', 'scion', 'scoffer', 'scofflaw', 'scorekeeper', 'scorer', + 'scourer', 'scout', 'scoutmaster', 'scrambler', 'scratcher', 'screen actor', + 'scrutineer', 'scuba diver', 'sculptor', 'sea scout', 'seasonal worker', + 'seasoner', 'second baseman', 'second cousin', 'seconder', 'second fiddle', + 'second-in-command', 'second lieutenant', 'second-rater', 'secretary', + 'secretary of agriculture', 'secretary of health and human services', + 'secretary of state', 'secretary of the interior', 'sectarian', + 'section hand', 'secularist', 'security consultant', 'seeded player', + 'seeder', 'seeker', 'segregate', 'segregator', 'selectman', 'selectwoman', + 'selfish person', 'self-starter', 'seller', 'selling agent', 'semanticist', + 'semifinalist', 'seminarian', 'senator', 'sendee', 'senior', + 'senior vice president', 'separatist', 'septuagenarian', 'serf', + 'spree killer', 'serjeant-at-law', 'server', 'serviceman', 'settler', + 'settler', 'sex symbol', 'sexton', 'shaheed', 'shakespearian', 'shanghaier', + 'sharecropper', 'shaver', 'shavian', 'sheep', 'sheik', 'shelver', + 'shepherd', 'ship-breaker', 'shipmate', 'shipowner', 'shipping agent', + 'shirtmaker', 'shogun', 'shopaholic', 'shop girl', 'shop steward', + 'shot putter', 'shrew', 'shuffler', 'shyster', 'sibling', 'sick person', + 'sightreader', 'signaler', 'signer', 'signor', 'signora', 'signore', + 'signorina', 'silent partner', 'addle-head', 'simperer', 'singer', + 'sinologist', 'sipper', 'sirrah', 'sister', 'sister', 'waverer', + 'sitar player', 'sixth-former', 'skateboarder', 'skeptic', 'sketcher', + 'skidder', 'skier', 'skinny-dipper', 'skin-diver', 'skinhead', 'slasher', + 'slattern', 'sleeper', 'sleeper', 'sleeping beauty', 'sleuth', 'slob', + 'sloganeer', 'slopseller', 'smasher', 'smirker', 'smith', 'smoothie', + 'smuggler', 'sneezer', 'snob', 'snoop', 'snorer', 'sob sister', + 'soccer player', 'social anthropologist', 'social climber', 'socialist', + 'socializer', 'social scientist', 'social secretary', 'socinian', + 'sociolinguist', 'sociologist', 'soda jerk', 'sodalist', 'sodomite', + 'soldier', 'son', 'songster', 'songstress', 'songwriter', 'sorcerer', + 'sorehead', 'soul mate', 'southern baptist', 'sovereign', 'spacewalker', + 'spanish american', 'sparring partner', 'spastic', 'speaker', + 'native speaker', 'speaker', 'speechwriter', 'specialist', 'specifier', + 'spectator', 'speech therapist', 'speedskater', 'spellbinder', 'sphinx', + 'spinster', 'split end', 'sport', 'sport', 'sporting man', + 'sports announcer', 'sports editor', 'sprog', 'square dancer', + 'square shooter', 'squatter', 'squire', 'squire', 'staff member', + 'staff sergeant', 'stage director', 'stainer', 'stakeholder', 'stalker', + 'stalking-horse', 'stammerer', 'stamper', 'standee', 'stand-in', 'star', + 'starlet', 'starter', 'statesman', 'state treasurer', 'stationer', + 'stenographer', 'stentor', 'stepbrother', 'stepmother', 'stepparent', + 'stevedore', 'steward', 'steward', 'steward', 'stickler', 'stiff', + 'stifler', 'stipendiary', 'stitcher', 'stockjobber', 'stock trader', + 'stockist', 'stoker', 'stooper', 'store detective', 'strafer', + 'straight man', 'stranger', 'stranger', 'strategist', 'straw boss', + 'streetwalker', 'stretcher-bearer', 'struggler', 'stud', 'student', + 'stumblebum', 'stylist', 'subaltern', 'subcontractor', 'subduer', 'subject', + 'subordinate', 'substitute', 'successor', 'successor', 'succorer', 'sufi', + 'suffragan', 'suffragette', 'sugar daddy', 'suicide bomber', 'suitor', + 'sumo wrestler', 'sunbather', 'sundowner', 'super heavyweight', 'superior', + 'supermom', 'supernumerary', 'supremo', 'surgeon', 'surgeon general', + 'surgeon general', 'surpriser', 'surveyor', 'surveyor', 'survivor', + 'sutler', 'sweeper', 'sweetheart', 'swinger', 'switcher', 'swot', + 'sycophant', 'sylph', 'sympathizer', 'symphonist', 'syncopator', 'syndic', + 'tactician', 'tagger', 'tailback', 'tallyman', 'tallyman', 'tanker', + 'tapper', 'tartuffe', 'tarzan', 'taster', 'tax assessor', 'taxer', + 'taxi dancer', 'taxonomist', 'teacher', 'teaching fellow', 'tearaway', + 'technical sergeant', 'technician', 'ted', 'teetotaler', + 'television reporter', 'temporizer', 'tempter', 'term infant', 'toiler', + 'tenant', 'tenant', 'tenderfoot', 'tennis player', 'tennis pro', + 'tenor saxophonist', 'termer', 'terror', 'tertigravida', 'testator', + 'testatrix', 'testee', 'test-tube baby', 'texas ranger', 'thane', + 'theatrical producer', 'theologian', 'theorist', 'theosophist', 'therapist', + 'thessalonian', 'thinker', 'thinker', 'thrower', 'thurifer', + 'ticket collector', 'tight end', 'tiler', 'timekeeper', 'timorese', + 'tinkerer', 'tinsmith', 'tinter', 'tippler', 'tipster', 't-man', + 'toastmaster', 'toast mistress', 'tobogganist', 'tomboy', 'toolmaker', + 'torchbearer', 'tory', 'tory', 'tosser', 'tosser', 'totalitarian', + 'tourist', 'tout', 'tout', 'tovarich', 'towhead', 'town clerk', + 'town crier', 'townsman', 'toxicologist', 'track star', 'trader', + 'trade unionist', 'traditionalist', 'traffic cop', 'tragedian', 'tragedian', + 'tragedienne', 'trail boss', 'trainer', 'traitor', 'traitress', + 'transactor', 'transcriber', 'transfer', 'transferee', 'translator', + 'transvestite', 'traveling salesman', 'traverser', 'trawler', 'treasury', + 'trencher', 'trend-setter', 'tribesman', 'trier', 'trifler', 'trooper', + 'trooper', 'trotskyite', 'truant', 'trumpeter', 'trusty', 'tudor', + 'tumbler', 'tutee', 'twin', 'two-timer', 'tyke', 'tympanist', 'typist', + 'tyrant', 'umpire', 'understudy', 'undesirable', 'unicyclist', + 'unilateralist', 'unitarian', 'arminian', 'universal donor', 'unix guru', + 'unknown soldier', 'upsetter', 'upstager', 'upstart', 'upstart', 'urchin', + 'urologist', 'usherette', 'usher', 'usurper', 'utility man', 'utilizer', + 'utopian', 'uxoricide', 'vacationer', 'valedictorian', 'valley girl', + 'vaulter', 'vegetarian', 'vegan', 'venerator', 'venture capitalist', + 'venturer', 'vermin', 'very important person', 'vibist', 'vicar', 'vicar', + 'vicar-general', 'vice chancellor', 'vicegerent', 'vice president', + 'vice-regent', 'victim', 'victorian', 'victualer', 'vigilante', 'villager', + 'vintager', 'vintner', 'violator', 'violator', 'violist', 'virago', + 'virologist', 'visayan', 'viscountess', 'viscount', 'visigoth', 'visionary', + 'visiting fireman', 'visiting professor', 'visualizer', 'vixen', 'vizier', + 'voicer', 'volunteer', 'volunteer', 'votary', 'votary', 'vouchee', 'vower', + 'voyager', 'voyeur', 'vulcanizer', 'waffler', 'wagnerian', 'waif', 'wailer', + 'waiter', 'waitress', 'walking delegate', 'walk-on', 'wallah', 'wally', + 'waltzer', 'wanderer', 'wandering jew', 'wanton', 'warrantee', 'warrantee', + 'washer', 'washerman', 'washwoman', 'wassailer', 'wastrel', 'wave', + 'weatherman', 'weekend warrior', 'weeder', 'welder', 'welfare case', + 'westerner', 'west-sider', 'wetter', 'whaler', 'whig', 'whiner', + 'whipper-in', 'whisperer', 'whiteface', 'carmelite', 'augustinian', + 'white hope', 'white supremacist', 'whoremaster', 'whoremaster', 'widow', + 'wife', 'wiggler', 'wimp', 'wing commander', 'winger', 'winner', 'winner', + 'window dresser', 'winker', 'wiper', 'wireman', 'wise guy', 'witch doctor', + 'withdrawer', 'withdrawer', 'woman', 'woman', 'wonder boy', 'wonderer', + 'working girl', 'workman', 'workmate', 'worldling', 'worshiper', 'worthy', + 'wrecker', 'wright', 'write-in candidate', 'writer', 'wykehamist', 'yakuza', + 'yard bird', 'yardie', 'yardman', 'yardmaster', 'yenta', 'yogi', + 'young buck', 'young turk', 'young turk', 'zionist', 'zoo keeper', 'genet', + 'kennan', 'munro', 'popper', 'stoker', 'townes', 'dust storm', 'parhelion', + 'snow', 'facula', 'wave', 'microflora', 'wilding', 'semi-climber', 'volva', + 'basidiocarp', 'domatium', 'apomict', 'aquatic', 'bryophyte', 'acrocarp', + 'sphagnum', 'liverwort', 'hepatica', 'pecopteris', 'pteridophyte', 'fern', + 'fern ally', 'spore', 'carpospore', 'chlamydospore', 'conidium', 'oospore', + 'tetraspore', 'zoospore', 'cryptogam', 'spermatophyte', 'seedling', + 'annual', 'biennial', 'perennial', 'hygrophyte', 'gymnosperm', 'gnetum', + 'catha edulis', 'ephedra', 'mahuang', 'welwitschia', 'cycad', 'sago palm', + 'false sago', 'zamia', 'coontie', 'ceratozamia', 'dioon', 'encephalartos', + 'kaffir bread', 'macrozamia', 'burrawong', 'pine', 'pinon', 'nut pine', + 'pinon pine', 'rocky mountain pinon', 'single-leaf', 'bishop pine', + 'california single-leaf pinyon', 'parry\'s pinyon', 'spruce pine', + 'black pine', 'pitch pine', 'pond pine', 'stone pine', 'swiss pine', + 'cembra nut', 'swiss mountain pine', 'ancient pine', 'white pine', + 'american white pine', 'western white pine', 'southwestern white pine', + 'limber pine', 'whitebark pine', 'yellow pine', 'ponderosa', 'jeffrey pine', + 'shore pine', 'sierra lodgepole pine', 'loblolly pine', 'jack pine', + 'swamp pine', 'longleaf pine', 'shortleaf pine', 'red pine', 'scotch pine', + 'scrub pine', 'monterey pine', 'bristlecone pine', 'table-mountain pine', + 'knobcone pine', 'japanese red pine', 'japanese black pine', 'torrey pine', + 'larch', 'american larch', 'western larch', 'subalpine larch', + 'european larch', 'siberian larch', 'golden larch', 'fir', 'silver fir', + 'amabilis fir', 'european silver fir', 'white fir', 'balsam fir', + 'fraser fir', 'lowland fir', 'alpine fir', 'santa lucia fir', 'cedar', + 'cedar of lebanon', 'deodar', 'atlas cedar', 'spruce', 'norway spruce', + 'weeping spruce', 'engelmann spruce', 'white spruce', 'black spruce', + 'siberian spruce', 'sitka spruce', 'oriental spruce', 'colorado spruce', + 'red spruce', 'hemlock', 'eastern hemlock', 'carolina hemlock', + 'mountain hemlock', 'western hemlock', 'douglas fir', 'green douglas fir', + 'big-cone spruce', 'cathaya', 'cedar', 'cypress', 'gowen cypress', + 'pygmy cypress', 'santa cruz cypress', 'arizona cypress', + 'guadalupe cypress', 'monterey cypress', 'mexican cypress', + 'italian cypress', 'king william pine', 'chilean cedar', 'incense cedar', + 'southern white cedar', 'oregon cedar', 'yellow cypress', 'japanese cedar', + 'juniper berry', 'incense cedar', 'kawaka', 'pahautea', 'metasequoia', + 'arborvitae', 'western red cedar', 'american arborvitae', + 'oriental arborvitae', 'hiba arborvitae', 'keteleeria', 'wollemi pine', + 'araucaria', 'monkey puzzle', 'norfolk island pine', 'new caledonian pine', + 'bunya bunya', 'hoop pine', 'kauri pine', 'kauri', 'amboina pine', + 'dundathu pine', 'red kauri', 'plum-yew', 'california nutmeg', + 'stinking cedar', 'celery pine', 'celery top pine', 'tanekaha', + 'alpine celery pine', 'yellowwood', 'gymnospermous yellowwood', 'podocarp', + 'yacca', 'brown pine', 'cape yellowwood', 'south-african yellowwood', + 'alpine totara', 'totara', 'common yellowwood', 'kahikatea', 'rimu', + 'tarwood', 'common sickle pine', 'yellow-leaf sickle pine', 'tarwood', + 'westland pine', 'huon pine', 'chilean rimu', 'mountain rimu', 'nagi', + 'miro', 'matai', 'plum-fruited yew', 'prince albert yew', + 'sundacarpus amara', 'japanese umbrella pine', 'yew', 'old world yew', + 'pacific yew', 'japanese yew', 'florida yew', 'new caledonian yew', + 'white-berry yew', 'ginkgo', 'angiosperm', 'dicot', 'monocot', 'floret', + 'flower', 'bloomer', 'wildflower', 'apetalous flower', 'inflorescence', + 'rosebud', 'gynostegium', 'pollinium', 'pistil', 'gynobase', 'gynophore', + 'stylopodium', 'carpophore', 'cornstalk', 'petiolule', 'mericarp', + 'micropyle', 'germ tube', 'pollen tube', 'gemma', 'galbulus', 'nectary', + 'pericarp', 'epicarp', 'mesocarp', 'pip', 'silique', 'cataphyll', + 'perisperm', 'monocarp', 'sporophyte', 'gametophyte', 'megasporangium', + 'microspore', 'microsporangium', 'microsporophyll', 'archespore', + 'bonduc nut', 'job\'s tears', 'oilseed', 'castor bean', 'cottonseed', + 'candlenut', 'peach pit', 'hypanthium', 'petal', 'corolla', 'lip', + 'perianth', 'thistledown', 'custard apple', 'cherimoya', 'ilama', 'soursop', + 'bullock\'s heart', 'sweetsop', 'pond apple', 'pawpaw', 'ilang-ilang', + 'lancewood', 'guinea pepper', 'barberry', 'american barberry', + 'common barberry', 'japanese barberry', 'oregon grape', 'oregon grape', + 'mayapple', 'may apple', 'allspice', 'carolina allspice', 'spicebush', + 'katsura tree', 'laurel', 'true laurel', 'camphor tree', 'cinnamon', + 'cassia', 'cassia bark', 'saigon cinnamon', 'cinnamon bark', 'spicebush', + 'avocado', 'laurel-tree', 'sassafras', 'california laurel', 'anise tree', + 'purple anise', 'star anise', 'star anise', 'magnolia', 'southern magnolia', + 'umbrella tree', 'earleaved umbrella tree', 'cucumber tree', + 'large-leaved magnolia', 'saucer magnolia', 'star magnolia', 'sweet bay', + 'manglietia', 'tulip tree', 'moonseed', 'common moonseed', + 'carolina moonseed', 'nutmeg', 'water nymph', 'european white lily', + 'southern spatterdock', 'lotus', 'water chinquapin', 'water-shield', + 'water-shield', 'peony', 'buttercup', 'meadow buttercup', 'water crowfoot', + 'lesser celandine', 'lesser spearwort', 'greater spearwort', + 'western buttercup', 'creeping buttercup', 'cursed crowfoot', 'aconite', + 'monkshood', 'wolfsbane', 'baneberry', 'baneberry', 'red baneberry', + 'pheasant\'s-eye', 'anemone', 'alpine anemone', 'canada anemone', + 'thimbleweed', 'wood anemone', 'wood anemone', 'longheaded thimbleweed', + 'snowdrop anemone', 'virginia thimbleweed', 'rue anemone', 'columbine', + 'meeting house', 'blue columbine', 'granny\'s bonnets', 'marsh marigold', + 'american bugbane', 'black cohosh', 'fetid bugbane', 'clematis', + 'pine hyacinth', 'blue jasmine', 'golden clematis', 'scarlet clematis', + 'leather flower', 'leather flower', 'virgin\'s bower', 'purple clematis', + 'goldthread', 'rocket larkspur', 'delphinium', 'larkspur', 'winter aconite', + 'lenten rose', 'green hellebore', 'hepatica', 'goldenseal', + 'false rue anemone', 'giant buttercup', 'nigella', 'love-in-a-mist', + 'fennel flower', 'black caraway', 'pasqueflower', 'meadow rue', + 'false bugbane', 'globeflower', 'winter\'s bark', 'pepper shrub', + 'sweet gale', 'wax myrtle', 'bay myrtle', 'bayberry', 'sweet fern', + 'corkwood', 'jointed rush', 'toad rush', 'slender rush', 'zebrawood', + 'connarus guianensis', 'legume', 'legume', 'peanut', 'granadilla tree', + 'arariba', 'tonka bean', 'courbaril', 'melilotus', 'darling pea', + 'smooth darling pea', 'clover', 'alpine clover', 'hop clover', + 'crimson clover', 'red clover', 'buffalo clover', 'white clover', 'mimosa', + 'acacia', 'shittah', 'wattle', 'black wattle', 'gidgee', 'catechu', + 'silver wattle', 'huisache', 'lightwood', 'golden wattle', 'fever tree', + 'coralwood', 'albizzia', 'silk tree', 'siris', 'rain tree', 'calliandra', + 'conacaste', 'inga', 'ice-cream bean', 'guama', 'lead tree', + 'wild tamarind', 'sabicu', 'nitta tree', 'parkia javanica', + 'manila tamarind', 'cat\'s-claw', 'honey mesquite', 'algarroba', + 'screw bean', 'screw bean', 'dogbane', 'indian hemp', 'bushman\'s poison', + 'impala lily', 'allamanda', 'common allamanda', 'dita', + 'nepal trumpet flower', 'carissa', 'hedge thorn', 'natal plum', + 'periwinkle', 'ivory tree', 'white dipladenia', 'chilean jasmine', + 'oleander', 'frangipani', 'west indian jasmine', 'rauwolfia', 'snakewood', + 'strophanthus kombe', 'yellow oleander', 'myrtle', 'large periwinkle', + 'arum', 'cuckoopint', 'black calla', 'calamus', 'alocasia', 'giant taro', + 'amorphophallus', 'pungapung', 'devil\'s tongue', 'anthurium', + 'flamingo flower', 'jack-in-the-pulpit', 'friar\'s-cowl', 'caladium', + 'caladium bicolor', 'wild calla', 'taro', 'taro', 'cryptocoryne', + 'dracontium', 'golden pothos', 'skunk cabbage', 'monstera', 'ceriman', + 'nephthytis', 'nephthytis afzelii', 'arrow arum', 'green arrow arum', + 'philodendron', 'pistia', 'pothos', 'spathiphyllum', 'skunk cabbage', + 'yautia', 'calla lily', 'pink calla', 'golden calla', 'duckweed', + 'common duckweed', 'star-duckweed', 'great duckweed', 'watermeal', + 'common wolffia', 'aralia', 'american angelica tree', 'american spikenard', + 'bristly sarsaparilla', 'japanese angelica tree', 'chinese angelica', 'ivy', + 'puka', 'ginseng', 'ginseng', 'umbrella tree', 'birthwort', + 'dutchman\'s-pipe', 'virginia snakeroot', 'canada ginger', 'heartleaf', + 'heartleaf', 'asarabacca', 'caryophyllaceous plant', 'corn cockle', + 'sandwort', 'mountain sandwort', 'pine-barren sandwort', + 'seabeach sandwort', 'rock sandwort', 'thyme-leaved sandwort', + 'mouse-ear chickweed', 'snow-in-summer', 'alpine mouse-ear', 'pink', + 'sweet william', 'carnation', 'china pink', 'japanese pink', 'maiden pink', + 'cheddar pink', 'button pink', 'cottage pink', 'fringed pink', 'drypis', + 'baby\'s breath', 'coral necklace', 'lychnis', 'ragged robin', + 'scarlet lychnis', 'mullein pink', 'sandwort', 'sandwort', 'soapwort', + 'knawel', 'silene', 'moss campion', 'wild pink', 'red campion', + 'white campion', 'fire pink', 'bladder campion', 'corn spurry', + 'sand spurry', 'chickweed', 'common chickweed', 'cowherb', 'hottentot fig', + 'livingstone daisy', 'fig marigold', 'ice plant', 'new zealand spinach', + 'amaranth', 'amaranth', 'tumbleweed', 'prince\'s-feather', 'pigweed', + 'thorny amaranth', 'alligator weed', 'cockscomb', 'cottonweed', + 'globe amaranth', 'bloodleaf', 'saltwort', 'lamb\'s-quarters', + 'good-king-henry', 'jerusalem oak', 'oak-leaved goosefoot', 'sowbane', + 'nettle-leaved goosefoot', 'red goosefoot', 'stinking goosefoot', 'orach', + 'saltbush', 'garden orache', 'desert holly', 'quail bush', 'beet', + 'beetroot', 'chard', 'mangel-wurzel', 'winged pigweed', 'halogeton', + 'glasswort', 'saltwort', 'russian thistle', 'greasewood', + 'scarlet musk flower', 'sand verbena', 'sweet sand verbena', + 'yellow sand verbena', 'beach pancake', 'beach sand verbena', + 'desert sand verbena', 'trailing four o\'clock', 'bougainvillea', + 'umbrellawort', 'four o\'clock', 'common four-o\'clock', + 'california four o\'clock', 'sweet four o\'clock', 'desert four o\'clock', + 'mountain four o\'clock', 'cockspur', 'rattail cactus', 'saguaro', + 'night-blooming cereus', 'echinocactus', 'hedgehog cactus', + 'golden barrel cactus', 'hedgehog cereus', 'rainbow cactus', 'epiphyllum', + 'barrel cactus', 'night-blooming cereus', 'chichipe', 'mescal', + 'mescal button', 'mammillaria', 'feather ball', 'garambulla', + 'knowlton\'s cactus', 'nopal', 'prickly pear', 'cholla', 'nopal', 'tuna', + 'barbados gooseberry', 'mistletoe cactus', 'christmas cactus', + 'night-blooming cereus', 'crab cactus', 'pokeweed', 'indian poke', 'poke', + 'ombu', 'bloodberry', 'portulaca', 'rose moss', 'common purslane', + 'rock purslane', 'red maids', 'carolina spring beauty', 'spring beauty', + 'virginia spring beauty', 'siskiyou lewisia', 'bitterroot', + 'broad-leaved montia', 'blinks', 'toad lily', 'winter purslane', + 'flame flower', 'pigmy talinum', 'jewels-of-opar', 'caper', + 'native pomegranate', 'caper tree', 'caper tree', 'common caper', + 'spiderflower', 'rocky mountain bee plant', 'clammyweed', 'crucifer', + 'cress', 'watercress', 'stonecress', 'garlic mustard', 'alyssum', + 'rose of jericho', 'arabidopsis thaliana', 'arabidopsis lyrata', + 'rock cress', 'sicklepod', 'tower mustard', 'horseradish', 'winter cress', + 'yellow rocket', 'hoary alison', 'buckler mustard', 'wild cabbage', + 'cabbage', 'head cabbage', 'savoy cabbage', 'brussels sprout', + 'cauliflower', 'broccoli', 'collard', 'kohlrabi', 'turnip plant', 'turnip', + 'rutabaga', 'broccoli raab', 'mustard', 'chinese mustard', 'bok choy', + 'rape', 'rapeseed', 'shepherd\'s purse', 'lady\'s smock', + 'coral-root bittercress', 'crinkleroot', 'american watercress', + 'spring cress', 'purple cress', 'wallflower', 'prairie rocket', + 'scurvy grass', 'sea kale', 'tansy mustard', 'draba', 'wallflower', + 'prairie rocket', 'siberian wall flower', 'western wall flower', + 'wormseed mustard', 'heliophila', 'damask violet', 'tansy-leaved rocket', + 'candytuft', 'woad', 'dyer\'s woad', 'bladderpod', 'sweet alyssum', + 'malcolm stock', 'virginian stock', 'stock', 'brompton stock', 'bladderpod', + 'chamois cress', 'radish plant', 'jointed charlock', 'radish', 'radish', + 'marsh cress', 'great yellowcress', 'schizopetalon', 'field mustard', + 'hedge mustard', 'desert plume', 'pennycress', 'field pennycress', + 'fringepod', 'bladderpod', 'wasabi', 'poppy', 'iceland poppy', + 'western poppy', 'prickly poppy', 'iceland poppy', 'oriental poppy', + 'corn poppy', 'opium poppy', 'prickly poppy', 'mexican poppy', 'bocconia', + 'celandine', 'corydalis', 'climbing corydalis', 'california poppy', + 'horn poppy', 'golden cup', 'plume poppy', 'blue poppy', 'welsh poppy', + 'creamcups', 'matilija poppy', 'wind poppy', 'celandine poppy', + 'climbing fumitory', 'bleeding heart', 'dutchman\'s breeches', + 'squirrel corn', 'composite', 'compass plant', 'everlasting', 'achillea', + 'yarrow', 'pink-and-white everlasting', 'white snakeroot', 'ageratum', + 'common ageratum', 'sweet sultan', 'ragweed', 'common ragweed', + 'great ragweed', 'western ragweed', 'ammobium', 'winged everlasting', + 'pellitory', 'pearly everlasting', 'andryala', 'plantain-leaved pussytoes', + 'field pussytoes', 'solitary pussytoes', 'mountain everlasting', 'mayweed', + 'yellow chamomile', 'corn chamomile', 'woolly daisy', 'burdock', + 'great burdock', 'african daisy', 'blue-eyed african daisy', 'marguerite', + 'silversword', 'arnica', 'heartleaf arnica', 'arnica montana', + 'lamb succory', 'artemisia', 'mugwort', 'sweet wormwood', 'field wormwood', + 'tarragon', 'sand sage', 'wormwood sage', 'western mugwort', + 'roman wormwood', 'bud brush', 'common mugwort', 'aster', 'wood aster', + 'whorled aster', 'heath aster', 'heart-leaved aster', 'white wood aster', + 'bushy aster', 'heath aster', 'white prairie aster', 'stiff aster', + 'goldilocks', 'large-leaved aster', 'new england aster', 'michaelmas daisy', + 'upland white aster', 'short\'s aster', 'sea aster', 'prairie aster', + 'annual salt-marsh aster', 'aromatic aster', 'arrow leaved aster', + 'azure aster', 'bog aster', 'crooked-stemmed aster', + 'eastern silvery aster', 'flat-topped white aster', 'late purple aster', + 'panicled aster', 'perennial salt marsh aster', 'purple-stemmed aster', + 'rough-leaved aster', 'rush aster', 'schreiber\'s aster', + 'small white aster', 'smooth aster', 'southern aster', 'starved aster', + 'tradescant\'s aster', 'wavy-leaved aster', 'western silvery aster', + 'willow aster', 'ayapana', 'mule fat', 'balsamroot', 'daisy', + 'common daisy', 'bur marigold', 'spanish needles', 'tickseed sunflower', + 'european beggar-ticks', 'slender knapweed', 'false chamomile', + 'swan river daisy', 'woodland oxeye', 'indian plantain', 'calendula', + 'common marigold', 'china aster', 'thistle', 'welted thistle', + 'musk thistle', 'carline thistle', 'stemless carline thistle', + 'common carline thistle', 'safflower', 'safflower seed', 'catananche', + 'blue succory', 'centaury', 'dusty miller', 'cornflower', 'star-thistle', + 'knapweed', 'sweet sultan', 'great knapweed', 'barnaby\'s thistle', + 'chamomile', 'chaenactis', 'chrysanthemum', 'corn marigold', 'crown daisy', + 'chop-suey greens', 'golden aster', 'maryland golden aster', 'goldenbush', + 'rabbit brush', 'chicory', 'endive', 'chicory', 'plume thistle', + 'canada thistle', 'field thistle', 'woolly thistle', + 'european woolly thistle', 'melancholy thistle', 'brook thistle', + 'bull thistle', 'blessed thistle', 'mistflower', 'horseweed', 'coreopsis', + 'giant coreopsis', 'sea dahlia', 'calliopsis', 'cosmos', 'brass buttons', + 'billy buttons', 'hawk\'s-beard', 'artichoke', 'cardoon', 'dahlia', + 'german ivy', 'florist\'s chrysanthemum', 'cape marigold', + 'leopard\'s-bane', 'coneflower', 'globe thistle', 'elephant\'s-foot', + 'tassel flower', 'brittlebush', 'sunray', 'engelmannia', 'fireweed', + 'fleabane', 'blue fleabane', 'daisy fleabane', 'orange daisy', + 'spreading fleabane', 'seaside daisy', 'philadelphia fleabane', + 'robin\'s plantain', 'showy daisy', 'woolly sunflower', 'golden yarrow', + 'dog fennel', 'joe-pye weed', 'boneset', 'joe-pye weed', 'blue daisy', + 'kingfisher daisy', 'cotton rose', 'herba impia', 'gaillardia', 'gazania', + 'treasure flower', 'african daisy', 'barberton daisy', 'desert sunflower', + 'cudweed', 'chafeweed', 'gumweed', 'grindelia robusta', 'curlycup gumweed', + 'little-head snakeweed', 'rabbitweed', 'broomweed', 'velvet plant', + 'goldenbush', 'camphor daisy', 'yellow spiny daisy', 'hoary golden bush', + 'sneezeweed', 'orange sneezeweed', 'rosilla', 'sunflower', + 'swamp sunflower', 'common sunflower', 'giant sunflower', 'showy sunflower', + 'maximilian\'s sunflower', 'prairie sunflower', 'jerusalem artichoke', + 'jerusalem artichoke', 'strawflower', 'heliopsis', 'strawflower', + 'hairy golden aster', 'hawkweed', 'rattlesnake weed', 'alpine coltsfoot', + 'alpine gold', 'dwarf hulsea', 'cat\'s-ear', 'inula', 'marsh elder', + 'burweed marsh elder', 'krigia', 'dwarf dandelion', 'garden lettuce', + 'cos lettuce', 'leaf lettuce', 'celtuce', 'prickly lettuce', 'goldfields', + 'tidytips', 'hawkbit', 'fall dandelion', 'edelweiss', 'oxeye daisy', + 'oxeye daisy', 'shasta daisy', 'pyrenees daisy', 'north island edelweiss', + 'blazing star', 'dotted gayfeather', 'dense blazing star', 'texas star', + 'african daisy', 'tahoka daisy', 'sticky aster', 'mojave aster', 'tarweed', + 'sweet false chamomile', 'pineapple weed', 'climbing hempweed', 'mutisia', + 'rattlesnake root', 'white lettuce', 'daisybush', 'new zealand daisybush', + 'cotton thistle', 'othonna', 'cascade everlasting', 'butterweed', + 'american feverfew', 'cineraria', 'florest\'s cineraria', 'butterbur', + 'winter heliotrope', 'sweet coltsfoot', 'oxtongue', 'hawkweed', + 'mouse-ear hawkweed', 'stevia', 'rattlesnake root', 'fleabane', + 'sheep plant', 'coneflower', 'mexican hat', 'long-head coneflower', + 'prairie coneflower', 'swan river everlasting', 'coneflower', + 'black-eyed susan', 'cutleaved coneflower', 'golden glow', + 'lavender cotton', 'creeping zinnia', 'golden thistle', + 'spanish oyster plant', 'nodding groundsel', 'dusty miller', 'butterweed', + 'ragwort', 'arrowleaf groundsel', 'black salsify', 'white-topped aster', + 'narrow-leaved white-topped aster', 'silver sage', 'sea wormwood', + 'sawwort', 'rosinweed', 'milk thistle', 'goldenrod', 'silverrod', + 'meadow goldenrod', 'missouri goldenrod', 'alpine goldenrod', + 'grey goldenrod', 'blue mountain tea', 'dyer\'s weed', 'seaside goldenrod', + 'narrow goldenrod', 'boott\'s goldenrod', 'elliott\'s goldenrod', + 'ohio goldenrod', 'rough-stemmed goldenrod', 'showy goldenrod', + 'tall goldenrod', 'zigzag goldenrod', 'sow thistle', 'milkweed', 'stevia', + 'stokes\' aster', 'marigold', 'african marigold', 'french marigold', + 'painted daisy', 'pyrethrum', 'northern dune tansy', 'feverfew', + 'dusty miller', 'tansy', 'dandelion', 'common dandelion', 'dandelion green', + 'russian dandelion', 'stemless hymenoxys', 'mexican sunflower', + 'easter daisy', 'yellow salsify', 'salsify', 'meadow salsify', + 'scentless camomile', 'turfing daisy', 'coltsfoot', 'ursinia', 'crownbeard', + 'wingstem', 'cowpen daisy', 'gravelweed', 'virginia crownbeard', 'ironweed', + 'mule\'s ears', 'white-rayed mule\'s ears', 'cocklebur', 'xeranthemum', + 'immortelle', 'zinnia', 'white zinnia', 'little golden zinnia', + 'blazing star', 'bartonia', 'achene', 'samara', 'campanula', + 'creeping bellflower', 'canterbury bell', 'tall bellflower', + 'marsh bellflower', 'clustered bellflower', 'peach bells', 'chimney plant', + 'rampion', 'tussock bellflower', 'orchid', 'orchis', 'male orchis', + 'butterfly orchid', 'showy orchis', 'aerides', 'angrecum', 'jewel orchid', + 'puttyroot', 'arethusa', 'bog rose', 'bletia', 'bletilla striata', + 'brassavola', 'spider orchid', 'spider orchid', 'caladenia', 'calanthe', + 'grass pink', 'calypso', 'cattleya', 'helleborine', 'red helleborine', + 'spreading pogonia', 'rosebud orchid', 'satyr orchid', 'frog orchid', + 'coelogyne', 'coral root', 'spotted coral root', 'striped coral root', + 'early coral root', 'swan orchid', 'cymbid', 'cypripedia', + 'lady\'s slipper', 'moccasin flower', 'common lady\'s-slipper', + 'ram\'s-head', 'yellow lady\'s slipper', 'large yellow lady\'s slipper', + 'california lady\'s slipper', 'clustered lady\'s slipper', + 'mountain lady\'s slipper', 'marsh orchid', 'common spotted orchid', + 'dendrobium', 'disa', 'phantom orchid', 'tulip orchid', 'butterfly orchid', + 'butterfly orchid', 'epidendron', 'helleborine', 'epipactis helleborine', + 'stream orchid', 'tongueflower', 'rattlesnake plantain', 'fragrant orchid', + 'short-spurred fragrant orchid', 'fringed orchis', 'frog orchid', + 'rein orchid', 'bog rein orchid', 'white fringed orchis', + 'elegant habenaria', 'purple-fringed orchid', 'coastal rein orchid', + 'hooker\'s orchid', 'ragged orchid', 'prairie orchid', 'snowy orchid', + 'round-leaved rein orchid', 'purple fringeless orchid', + 'purple-fringed orchid', 'alaska rein orchid', 'crested coral root', + 'texas purple spike', 'lizard orchid', 'laelia', 'liparis', 'twayblade', + 'fen orchid', 'broad-leaved twayblade', 'lesser twayblade', 'twayblade', + 'green adder\'s mouth', 'masdevallia', 'maxillaria', 'pansy orchid', + 'odontoglossum', 'oncidium', 'bee orchid', 'fly orchid', 'spider orchid', + 'early spider orchid', 'venus\' slipper', 'phaius', 'moth orchid', + 'butterfly plant', 'rattlesnake orchid', 'lesser butterfly orchid', + 'greater butterfly orchid', 'prairie white-fringed orchid', 'tangle orchid', + 'indian crocus', 'pleurothallis', 'pogonia', 'butterfly orchid', + 'psychopsis krameriana', 'psychopsis papilio', 'helmet orchid', + 'foxtail orchid', 'orange-blossom orchid', 'sobralia', 'ladies\' tresses', + 'screw augur', 'hooded ladies\' tresses', 'western ladies\' tresses', + 'european ladies\' tresses', 'stanhopea', 'stelis', 'fly orchid', 'vanda', + 'blue orchid', 'vanilla', 'vanilla orchid', 'yam', 'yam', 'white yam', + 'cinnamon vine', 'elephant\'s-foot', 'wild yam', 'cush-cush', + 'black bryony', 'primrose', 'english primrose', 'cowslip', 'oxlip', + 'chinese primrose', 'polyanthus', 'pimpernel', 'scarlet pimpernel', + 'bog pimpernel', 'chaffweed', 'cyclamen', 'sowbread', 'sea milkwort', + 'featherfoil', 'water gillyflower', 'water violet', 'loosestrife', + 'gooseneck loosestrife', 'yellow pimpernel', 'fringed loosestrife', + 'moneywort', 'swamp candles', 'whorled loosestrife', 'water pimpernel', + 'brookweed', 'brookweed', 'coralberry', 'marlberry', 'plumbago', 'leadwort', + 'thrift', 'sea lavender', 'barbasco', 'gramineous plant', 'grass', + 'midgrass', 'shortgrass', 'sword grass', 'tallgrass', 'herbage', + 'goat grass', 'wheatgrass', 'crested wheatgrass', 'bearded wheatgrass', + 'western wheatgrass', 'intermediate wheatgrass', 'slender wheatgrass', + 'velvet bent', 'cloud grass', 'meadow foxtail', 'foxtail', 'broom grass', + 'broom sedge', 'tall oat grass', 'toetoe', 'oat', 'cereal oat', 'wild oat', + 'slender wild oat', 'wild red oat', 'brome', 'chess', 'field brome', + 'grama', 'black grama', 'buffalo grass', 'reed grass', 'feather reed grass', + 'australian reed grass', 'burgrass', 'buffel grass', 'rhodes grass', + 'pampas grass', 'giant star grass', 'orchard grass', 'egyptian grass', + 'crabgrass', 'smooth crabgrass', 'large crabgrass', 'barnyard grass', + 'japanese millet', 'yardgrass', 'finger millet', 'lyme grass', 'wild rye', + 'giant ryegrass', 'sea lyme grass', 'canada wild rye', 'teff', + 'weeping love grass', 'plume grass', 'ravenna grass', 'fescue', + 'reed meadow grass', 'velvet grass', 'creeping soft grass', 'barleycorn', + 'barley grass', 'little barley', 'rye grass', 'perennial ryegrass', + 'italian ryegrass', 'darnel', 'nimblewill', 'cultivated rice', 'ricegrass', + 'smilo', 'switch grass', 'broomcorn millet', 'goose grass', 'dallisgrass', + 'bahia grass', 'knotgrass', 'fountain grass', 'reed canary grass', + 'canary grass', 'timothy', 'bluegrass', 'meadowgrass', 'wood meadowgrass', + 'noble cane', 'munj', 'broom beard grass', 'bluestem', 'rye', + 'bristlegrass', 'giant foxtail', 'yellow bristlegrass', + 'green bristlegrass', 'siberian millet', 'german millet', 'millet', + 'rattan', 'malacca', 'reed', 'sorghum', 'grain sorghum', 'durra', + 'feterita', 'hegari', 'kaoliang', 'milo', 'shallu', 'broomcorn', + 'cordgrass', 'salt reed grass', 'prairie cordgrass', 'smut grass', + 'sand dropseed', 'rush grass', 'st', 'grain', 'cereal', 'wheat', + 'wheat berry', 'durum', 'spelt', 'emmer', 'wild wheat', 'corn', 'mealie', + 'corn', 'dent corn', 'flint corn', 'popcorn', 'zoysia', 'manila grass', + 'korean lawn grass', 'bamboo', 'common bamboo', 'giant bamboo', + 'umbrella plant', 'chufa', 'galingale', 'nutgrass', 'sand sedge', + 'cypress sedge', 'cotton grass', 'common cotton grass', 'hardstem bulrush', + 'wool grass', 'spike rush', 'water chestnut', 'needle spike rush', + 'creeping spike rush', 'pandanus', 'textile screw pine', 'cattail', + 'cat\'s-tail', 'bur reed', 'grain', 'kernel', 'rye', 'gourd', 'gourd', + 'pumpkin', 'squash', 'summer squash', 'yellow squash', 'marrow', 'zucchini', + 'cocozelle', 'cymling', 'spaghetti squash', 'winter squash', 'acorn squash', + 'hubbard squash', 'turban squash', 'buttercup squash', 'butternut squash', + 'winter crookneck', 'cushaw', 'prairie gourd', 'prairie gourd', 'bryony', + 'white bryony', 'sweet melon', 'cantaloupe', 'winter melon', 'net melon', + 'cucumber', 'squirting cucumber', 'bottle gourd', 'luffa', 'loofah', + 'angled loofah', 'loofa', 'balsam apple', 'balsam pear', 'lobelia', + 'water lobelia', 'mallow', 'musk mallow', 'common mallow', 'okra', 'okra', + 'abelmosk', 'flowering maple', 'velvetleaf', 'hollyhock', 'rose mallow', + 'althea', 'marsh mallow', 'poppy mallow', 'fringed poppy mallow', + 'purple poppy mallow', 'clustered poppy mallow', 'sea island cotton', + 'levant cotton', 'upland cotton', 'peruvian cotton', 'wild cotton', 'kenaf', + 'sorrel tree', 'rose mallow', 'cotton rose', 'roselle', 'mahoe', + 'flower-of-an-hour', 'lacebark', 'wild hollyhock', 'mountain hollyhock', + 'seashore mallow', 'salt marsh mallow', 'chaparral mallow', 'malope', + 'false mallow', 'waxmallow', 'glade mallow', 'pavonia', 'ribbon tree', + 'bush hibiscus', 'virginia mallow', 'queensland hemp', 'indian mallow', + 'checkerbloom', 'globe mallow', 'prairie mallow', 'tulipwood tree', + 'portia tree', 'red silk-cotton tree', 'cream-of-tartar tree', 'baobab', + 'kapok', 'durian', 'montezuma', 'shaving-brush tree', 'quandong', + 'quandong', 'makomako', 'jamaican cherry', 'breakax', 'sterculia', + 'panama tree', 'kalumpang', 'bottle-tree', 'flame tree', 'flame tree', + 'kurrajong', 'queensland bottletree', 'kola', 'kola nut', + 'chinese parasol tree', 'flannelbush', 'screw tree', + 'nut-leaved screw tree', 'red beech', 'looking glass tree', + 'looking-glass plant', 'honey bell', 'mayeng', 'silver tree', 'cacao', + 'obeche', 'linden', 'american basswood', 'small-leaved linden', + 'white basswood', 'japanese linden', 'silver lime', 'corchorus', + 'african hemp', 'herb', 'protea', 'honeypot', 'honeyflower', 'banksia', + 'honeysuckle', 'smoke bush', 'chilean firebush', 'chilean nut', 'grevillea', + 'red-flowered silky oak', 'silky oak', 'beefwood', 'cushion flower', + 'rewa-rewa', 'honeyflower', 'silver tree', 'lomatia', 'macadamia', + 'macadamia integrifolia', 'macadamia nut', 'queensland nut', 'prickly ash', + 'geebung', 'wheel tree', 'scrub beefwood', 'waratah', 'waratah', + 'casuarina', 'she-oak', 'beefwood', 'australian pine', 'heath', + 'tree heath', 'briarroot', 'winter heath', 'bell heather', 'cornish heath', + 'spanish heath', 'prince-of-wales\'-heath', 'bog rosemary', + 'marsh andromeda', 'madrona', 'strawberry tree', 'bearberry', + 'alpine bearberry', 'heartleaf manzanita', 'parry manzanita', 'spike heath', + 'bryanthus', 'leatherleaf', 'connemara heath', 'trailing arbutus', + 'creeping snowberry', 'salal', 'huckleberry', 'black huckleberry', + 'dangleberry', 'box huckleberry', 'kalmia', 'mountain laurel', + 'swamp laurel', 'trapper\'s tea', 'wild rosemary', 'sand myrtle', + 'leucothoe', 'dog laurel', 'sweet bells', 'alpine azalea', 'staggerbush', + 'maleberry', 'fetterbush', 'false azalea', 'minniebush', 'sorrel tree', + 'mountain heath', 'purple heather', 'fetterbush', 'rhododendron', + 'coast rhododendron', 'rosebay', 'swamp azalea', 'azalea', 'cranberry', + 'american cranberry', 'european cranberry', 'blueberry', 'farkleberry', + 'low-bush blueberry', 'rabbiteye blueberry', 'dwarf bilberry', + 'evergreen blueberry', 'evergreen huckleberry', 'bilberry', 'bilberry', + 'bog bilberry', 'dryland blueberry', 'grouseberry', 'deerberry', 'cowberry', + 'diapensia', 'galax', 'pyxie', 'shortia', 'oconee bells', + 'australian heath', 'epacris', 'common heath', 'common heath', + 'port jackson heath', 'native cranberry', 'pink fivecorner', 'wintergreen', + 'false wintergreen', 'lesser wintergreen', 'wild lily of the valley', + 'wild lily of the valley', 'pipsissewa', 'love-in-winter', + 'one-flowered wintergreen', 'indian pipe', 'pinesap', 'beech', + 'common beech', 'copper beech', 'american beech', 'weeping beech', + 'japanese beech', 'chestnut', 'american chestnut', 'european chestnut', + 'chinese chestnut', 'japanese chestnut', 'allegheny chinkapin', + 'ozark chinkapin', 'oak chestnut', 'giant chinkapin', + 'dwarf golden chinkapin', 'tanbark oak', 'japanese oak', 'southern beech', + 'myrtle beech', 'coigue', 'new zealand beech', 'silver beech', + 'roble beech', 'rauli beech', 'black beech', 'hard beech', 'acorn', + 'cupule', 'oak', 'live oak', 'coast live oak', 'white oak', + 'american white oak', 'arizona white oak', 'swamp white oak', + 'european turkey oak', 'canyon oak', 'scarlet oak', 'jack oak', 'red oak', + 'southern red oak', 'oregon white oak', 'holm oak', 'bear oak', + 'shingle oak', 'bluejack oak', 'california black oak', + 'american turkey oak', 'laurel oak', 'california white oak', 'overcup oak', + 'bur oak', 'scrub oak', 'blackjack oak', 'swamp chestnut oak', + 'japanese oak', 'chestnut oak', 'chinquapin oak', 'myrtle oak', 'water oak', + 'nuttall oak', 'durmast', 'basket oak', 'pin oak', 'willow oak', + 'dwarf chinkapin oak', 'common oak', 'northern red oak', 'shumard oak', + 'post oak', 'cork oak', 'spanish oak', 'huckleberry oak', + 'chinese cork oak', 'black oak', 'southern live oak', 'interior live oak', + 'mast', 'birch', 'yellow birch', 'american white birch', 'grey birch', + 'silver birch', 'downy birch', 'black birch', 'sweet birch', + 'yukon white birch', 'swamp birch', 'newfoundland dwarf birch', 'alder', + 'common alder', 'grey alder', 'seaside alder', 'white alder', 'red alder', + 'speckled alder', 'smooth alder', 'green alder', 'green alder', 'hornbeam', + 'european hornbeam', 'american hornbeam', 'hop hornbeam', + 'old world hop hornbeam', 'eastern hop hornbeam', 'hazelnut', + 'american hazel', 'cobnut', 'beaked hazelnut', 'centaury', 'rosita', + 'lesser centaury', 'seaside centaury', 'slender centaury', + 'prairie gentian', 'persian violet', 'columbo', 'gentian', 'gentianella', + 'closed gentian', 'explorer\'s gentian', 'closed gentian', + 'great yellow gentian', 'marsh gentian', 'soapwort gentian', + 'striped gentian', 'agueweed', 'felwort', 'fringed gentian', + 'gentianopsis crinita', 'gentianopsis detonsa', 'gentianopsid procera', + 'gentianopsis thermalis', 'tufted gentian', 'spurred gentian', 'sabbatia', + 'toothbrush tree', 'olive tree', 'olive', 'olive', 'black maire', + 'white maire', 'fringe tree', 'fringe bush', 'forestiera', 'forsythia', + 'ash', 'white ash', 'swamp ash', 'flowering ash', 'european ash', + 'oregon ash', 'black ash', 'manna ash', 'red ash', 'green ash', 'blue ash', + 'mountain ash', 'pumpkin ash', 'arizona ash', 'jasmine', 'primrose jasmine', + 'winter jasmine', 'common jasmine', 'privet', 'amur privet', + 'japanese privet', 'ligustrum obtusifolium', 'common privet', 'devilwood', + 'mock privet', 'lilac', 'himalayan lilac', 'persian lilac', + 'japanese tree lilac', 'japanese lilac', 'common lilac', 'bloodwort', + 'kangaroo paw', 'virginian witch hazel', 'vernal witch hazel', + 'winter hazel', 'fothergilla', 'liquidambar', 'sweet gum', 'iron tree', + 'walnut', 'california black walnut', 'butternut', 'black walnut', + 'english walnut', 'hickory', 'water hickory', 'pignut', 'bitternut', + 'pecan', 'big shellbark', 'nutmeg hickory', 'shagbark', 'mockernut', + 'wing nut', 'caucasian walnut', 'dhawa', 'combretum', 'hiccup nut', + 'bush willow', 'bush willow', 'button tree', 'white mangrove', 'oleaster', + 'water milfoil', 'anchovy pear', 'brazil nut', 'loosestrife', + 'purple loosestrife', 'grass poly', 'crape myrtle', 'queen\'s crape myrtle', + 'myrtaceous tree', 'myrtle', 'common myrtle', 'bayberry', 'allspice', + 'allspice tree', 'sour cherry', 'nakedwood', 'surinam cherry', 'rose apple', + 'feijoa', 'jaboticaba', 'guava', 'guava', 'cattley guava', + 'brazilian guava', 'gum tree', 'eucalyptus', 'flooded gum', 'mallee', + 'stringybark', 'smoothbark', 'red gum', 'red gum', 'river red gum', + 'mountain swamp gum', 'snow gum', 'alpine ash', 'white mallee', + 'white stringybark', 'white mountain ash', 'blue gum', 'rose gum', + 'cider gum', 'swamp gum', 'spotted gum', 'lemon-scented gum', + 'black mallee', 'forest red gum', 'mountain ash', 'manna gum', 'clove', + 'clove', 'tupelo', 'water gum', 'sour gum', 'enchanter\'s nightshade', + 'circaea lutetiana', 'willowherb', 'fireweed', 'california fuchsia', + 'fuchsia', 'lady\'s-eardrop', 'evening primrose', 'common evening primrose', + 'sundrops', 'missouri primrose', 'pomegranate', 'mangrove', 'daphne', + 'garland flower', 'spurge laurel', 'mezereon', 'indian rhododendron', + 'medinilla magnifica', 'deer grass', 'canna', 'achira', 'arrowroot', + 'banana', 'dwarf banana', 'japanese banana', 'plantain', 'edible banana', + 'abaca', 'abyssinian banana', 'ginger', 'common ginger', 'turmeric', + 'galangal', 'shellflower', 'grains of paradise', 'cardamom', 'begonia', + 'fibrous-rooted begonia', 'tuberous begonia', 'rhizomatous begonia', + 'christmas begonia', 'angel-wing begonia', 'beefsteak begonia', + 'star begonia', 'rex begonia', 'wax begonia', 'socotra begonia', + 'hybrid tuberous begonia', 'dillenia', 'guinea gold vine', 'poon', 'calaba', + 'maria', 'laurelwood', 'alexandrian laurel', 'clusia', 'wild fig', + 'waxflower', 'pitch apple', 'mangosteen', 'gamboge tree', 'st john\'s wort', + 'common st john\'s wort', 'great st john\'s wort', + 'creeping st john\'s wort', 'low st andrew\'s cross', 'klammath weed', + 'shrubby st john\'s wort', 'st peter\'s wort', 'marsh st-john\'s wort', + 'mammee apple', 'rose chestnut', 'bower actinidia', 'chinese gooseberry', + 'silvervine', 'wild cinnamon', 'papaya', 'souari', 'rockrose', + 'white-leaved rockrose', 'common gum cistus', 'frostweed', 'dipterocarp', + 'red lauan', 'governor\'s plum', 'kei apple', 'ketembilla', 'chaulmoogra', + 'wild peach', 'candlewood', 'boojum tree', 'bird\'s-eye bush', 'granadilla', + 'granadilla', 'granadilla', 'maypop', 'jamaica honeysuckle', + 'banana passion fruit', 'sweet calabash', 'love-in-a-mist', 'reseda', + 'mignonette', 'dyer\'s rocket', 'false tamarisk', 'halophyte', 'viola', + 'violet', 'field pansy', 'american dog violet', 'dog violet', + 'horned violet', 'two-eyed violet', 'bird\'s-foot violet', + 'downy yellow violet', 'long-spurred violet', 'pale violet', 'hedge violet', + 'nettle', 'stinging nettle', 'roman nettle', 'ramie', 'wood nettle', + 'australian nettle', 'pellitory-of-the-wall', 'richweed', 'artillery plant', + 'friendship plant', 'queensland grass-cloth plant', 'pipturus albidus', + 'cannabis', 'indian hemp', 'mulberry', 'white mulberry', 'black mulberry', + 'red mulberry', 'osage orange', 'breadfruit', 'jackfruit', 'marang', + 'fig tree', 'fig', 'caprifig', 'golden fig', 'banyan', 'pipal', + 'india-rubber tree', 'mistletoe fig', 'port jackson fig', 'sycamore', + 'paper mulberry', 'trumpetwood', 'elm', 'winged elm', 'american elm', + 'smooth-leaved elm', 'cedar elm', 'witch elm', 'dutch elm', + 'huntingdon elm', 'water elm', 'chinese elm', 'english elm', 'siberian elm', + 'slippery elm', 'jersey elm', 'september elm', 'rock elm', 'hackberry', + 'european hackberry', 'american hackberry', 'sugarberry', + 'iridaceous plant', 'bearded iris', 'beardless iris', 'orrisroot', + 'dwarf iris', 'dutch iris', 'florentine iris', 'stinking iris', + 'german iris', 'japanese iris', 'german iris', 'dalmatian iris', + 'persian iris', 'dutch iris', 'dwarf iris', 'spanish iris', + 'blackberry-lily', 'crocus', 'saffron', 'corn lily', 'blue-eyed grass', + 'wandflower', 'amaryllis', 'salsilla', 'salsilla', 'blood lily', + 'cape tulip', 'hippeastrum', 'narcissus', 'daffodil', 'jonquil', 'jonquil', + 'jacobean lily', 'liliaceous plant', 'mountain lily', 'canada lily', + 'tiger lily', 'columbia tiger lily', 'tiger lily', 'easter lily', + 'coast lily', 'turk\'s-cap', 'michigan lily', 'leopard lily', 'turk\'s-cap', + 'african lily', 'colicroot', 'ague root', 'yellow colicroot', + 'alliaceous plant', 'hooker\'s onion', 'wild leek', 'canada garlic', + 'keeled garlic', 'onion', 'shallot', 'nodding onion', 'welsh onion', + 'red-skinned onion', 'daffodil garlic', 'few-flowered leek', 'garlic', + 'sand leek', 'chives', 'crow garlic', 'wild garlic', 'garlic chive', + 'round-headed leek', 'three-cornered leek', 'cape aloe', 'kniphofia', + 'poker plant', 'red-hot poker', 'fly poison', 'amber lily', 'asparagus', + 'asparagus fern', 'smilax', 'asphodel', 'jacob\'s rod', 'aspidistra', + 'coral drops', 'christmas bells', 'climbing onion', 'mariposa', + 'globe lily', 'cat\'s-ear', 'white globe lily', 'yellow globe lily', + 'rose globe lily', 'star tulip', 'desert mariposa tulip', + 'yellow mariposa tulip', 'sagebrush mariposa tulip', 'sego lily', 'camas', + 'common camas', 'leichtlin\'s camas', 'wild hyacinth', 'dogtooth violet', + 'white dogtooth violet', 'yellow adder\'s tongue', 'european dogtooth', + 'fawn lily', 'glacier lily', 'avalanche lily', 'fritillary', + 'mission bells', 'mission bells', 'stink bell', 'crown imperial', + 'white fritillary', 'snake\'s head fritillary', 'adobe lily', + 'scarlet fritillary', 'tulip', 'dwarf tulip', 'lady tulip', + 'tulipa gesneriana', 'cottage tulip', 'darwin tulip', 'gloriosa', + 'lemon lily', 'common hyacinth', 'roman hyacinth', 'summer hyacinth', + 'star-of-bethlehem', 'bath asparagus', 'grape hyacinth', + 'common grape hyacinth', 'tassel hyacinth', 'scilla', 'spring squill', + 'false asphodel', 'scotch asphodel', 'sea squill', 'squill', + 'butcher\'s broom', 'bog asphodel', 'european bog asphodel', + 'american bog asphodel', 'hellebore', 'white hellebore', 'squaw grass', + 'death camas', 'alkali grass', 'white camas', 'poison camas', + 'grassy death camas', 'prairie wake-robin', 'dwarf-white trillium', + 'herb paris', 'sarsaparilla', 'bullbrier', 'rough bindweed', 'clintonia', + 'false lily of the valley', 'false lily of the valley', 'solomon\'s-seal', + 'great solomon\'s-seal', 'bellwort', 'strawflower', 'pia', 'agave', + 'american agave', 'sisal', 'maguey', 'maguey', 'agave tequilana', + 'cabbage tree', 'dracaena', 'tuberose', 'sansevieria', + 'african bowstring hemp', 'ceylon bowstring hemp', + 'mother-in-law\'s tongue', 'spanish bayonet', 'spanish bayonet', + 'joshua tree', 'soapweed', 'adam\'s needle', 'bear grass', 'spanish dagger', + 'our lord\'s candle', 'water shamrock', 'butterfly bush', 'yellow jasmine', + 'flax', 'calabar bean', 'bonduc', 'divi-divi', 'mysore thorn', + 'brazilian ironwood', 'bird of paradise', 'shingle tree', 'mountain ebony', + 'msasa', 'cassia', 'golden shower tree', 'pink shower', 'rainbow shower', + 'horse cassia', 'carob', 'carob', 'paloverde', 'royal poinciana', + 'locust tree', 'water locust', 'honey locust', 'kentucky coffee tree', + 'logwood', 'jerusalem thorn', 'palo verde', 'dalmatian laburnum', 'senna', + 'avaram', 'alexandria senna', 'wild senna', 'sicklepod', 'coffee senna', + 'tamarind', 'false indigo', 'false indigo', 'hog peanut', 'angelim', + 'cabbage bark', 'kidney vetch', 'groundnut', 'rooibos', 'milk vetch', + 'alpine milk vetch', 'purple milk vetch', 'camwood', 'wild indigo', + 'blue false indigo', 'white false indigo', 'indigo broom', 'dhak', + 'pigeon pea', 'sword bean', 'pea tree', 'siberian pea tree', + 'chinese pea tree', 'moreton bay chestnut', 'butterfly pea', 'judas tree', + 'redbud', 'western redbud', 'tagasaste', 'weeping tree broom', 'flame pea', + 'chickpea', 'chickpea', 'kentucky yellowwood', 'glory pea', 'desert pea', + 'parrot\'s beak', 'butterfly pea', 'blue pea', 'telegraph plant', + 'bladder senna', 'axseed', 'crotalaria', 'guar', 'white broom', + 'common broom', 'rosewood', 'indian blackwood', 'sissoo', 'kingwood', + 'brazilian rosewood', 'cocobolo', 'blackwood', 'bitter pea', 'derris', + 'derris root', 'prairie mimosa', 'tick trefoil', 'beggarweed', + 'australian pea', 'coral tree', 'kaffir boom', 'coral bean tree', 'ceibo', + 'kaffir boom', 'indian coral tree', 'cork tree', 'goat\'s rue', + 'poison bush', 'spanish broom', 'woodwaxen', 'chanar', 'gliricidia', 'soy', + 'licorice', 'wild licorice', 'licorice root', 'western australia coral pea', + 'sweet vetch', 'french honeysuckle', 'anil', 'scarlet runner', + 'hyacinth bean', 'scotch laburnum', 'vetchling', 'wild pea', + 'everlasting pea', 'beach pea', 'grass vetch', 'marsh pea', + 'common vetchling', 'grass pea', 'tangier pea', 'heath pea', + 'bicolor lespediza', 'japanese clover', 'korean lespedeza', + 'sericea lespedeza', 'lentil', 'lentil', 'prairie bird\'s-foot trefoil', + 'bird\'s foot trefoil', 'winged pea', 'lupine', 'white lupine', + 'tree lupine', 'wild lupine', 'bluebonnet', 'texas bluebonnet', 'medic', + 'moon trefoil', 'sickle alfalfa', 'calvary clover', 'black medick', + 'alfalfa', 'millettia', 'mucuna', 'cowage', 'tolu tree', 'peruvian balsam', + 'sainfoin', 'restharrow', 'bead tree', 'jumby bead', 'locoweed', + 'purple locoweed', 'tumbleweed', 'yam bean', 'shamrock pea', 'pole bean', + 'kidney bean', 'haricot', 'wax bean', 'scarlet runner', 'lima bean', + 'sieva bean', 'tepary bean', 'chaparral pea', 'jamaica dogwood', 'pea', + 'garden pea', 'edible-pod pea', 'sugar snap pea', 'field pea', 'field pea', + 'common flat pea', 'quira', 'roble', 'panama redwood tree', 'indian beech', + 'winged bean', 'breadroot', 'bloodwood tree', 'kino', 'red sandalwood', + 'kudzu', 'bristly locust', 'black locust', 'clammy locust', 'carib wood', + 'colorado river hemp', 'scarlet wisteria tree', 'japanese pagoda tree', + 'mescal bean', 'kowhai', 'jade vine', 'hoary pea', 'bastard indigo', + 'catgut', 'bush pea', 'false lupine', 'carolina lupine', 'tipu', + 'bird\'s foot trefoil', 'fenugreek', 'gorse', 'vetch', 'tufted vetch', + 'broad bean', 'bitter betch', 'bush vetch', 'moth bean', 'snailflower', + 'mung', 'cowpea', 'cowpea', 'asparagus bean', 'swamp oak', 'keurboom', + 'keurboom', 'japanese wistaria', 'chinese wistaria', 'american wistaria', + 'silky wisteria', 'palm', 'sago palm', 'feather palm', 'fan palm', + 'palmetto', 'coyol', 'grugru', 'areca', 'betel palm', 'sugar palm', + 'piassava palm', 'coquilla nut', 'palmyra', 'calamus', 'rattan', + 'lawyer cane', 'fishtail palm', 'wine palm', 'wax palm', 'coconut', + 'carnauba', 'caranday', 'corozo', 'gebang palm', 'latanier', 'talipot', + 'oil palm', 'african oil palm', 'american oil palm', 'palm nut', + 'cabbage palm', 'cabbage palm', 'true sago palm', 'nipa palm', 'babassu', + 'babassu nut', 'cohune palm', 'cohune nut', 'date palm', 'ivory palm', + 'raffia palm', 'bamboo palm', 'lady palm', 'miniature fan palm', + 'reed rhapis', 'royal palm', 'cabbage palm', 'cabbage palmetto', + 'saw palmetto', 'thatch palm', 'key palm', 'english plantain', + 'broad-leaved plantain', 'hoary plantain', 'fleawort', 'rugel\'s plantain', + 'hoary plantain', 'buckwheat', 'prince\'s-feather', 'eriogonum', + 'umbrella plant', 'wild buckwheat', 'rhubarb', 'himalayan rhubarb', + 'pie plant', 'chinese rhubarb', 'sour dock', 'sheep sorrel', 'bitter dock', + 'french sorrel', 'yellow-eyed grass', 'commelina', 'spiderwort', + 'pineapple', 'pipewort', 'water hyacinth', 'water star grass', 'naiad', + 'water plantain', 'narrow-leaved water plantain', 'hydrilla', + 'american frogbit', 'waterweed', 'canadian pondweed', 'tape grass', + 'pondweed', 'curled leaf pondweed', 'loddon pondweed', 'frog\'s lettuce', + 'arrow grass', 'horned pondweed', 'eelgrass', 'rose', 'hip', 'banksia rose', + 'damask rose', 'sweetbrier', 'cherokee rose', 'musk rose', 'agrimonia', + 'harvest-lice', 'fragrant agrimony', 'alderleaf juneberry', + 'flowering quince', 'japonica', 'coco plum', 'cotoneaster', + 'cotoneaster dammeri', 'cotoneaster horizontalis', 'parsley haw', + 'scarlet haw', 'blackthorn', 'cockspur thorn', 'mayhaw', 'red haw', + 'red haw', 'quince', 'mountain avens', 'loquat', 'beach strawberry', + 'virginia strawberry', 'avens', 'yellow avens', 'yellow avens', + 'prairie smoke', 'bennet', 'toyon', 'apple tree', 'apple', 'wild apple', + 'crab apple', 'siberian crab', 'wild crab', 'american crab apple', + 'oregon crab apple', 'southern crab apple', 'iowa crab', 'bechtel crab', + 'medlar', 'cinquefoil', 'silverweed', 'salad burnet', 'plum', 'wild plum', + 'allegheny plum', 'american red plum', 'chickasaw plum', 'beach plum', + 'common plum', 'bullace', 'damson plum', 'big-tree plum', 'canada plum', + 'plumcot', 'apricot', 'japanese apricot', 'common apricot', + 'purple apricot', 'cherry', 'wild cherry', 'wild cherry', 'sweet cherry', + 'heart cherry', 'gean', 'capulin', 'cherry laurel', 'cherry plum', + 'sour cherry', 'amarelle', 'morello', 'marasca', 'almond tree', 'almond', + 'bitter almond', 'jordan almond', 'dwarf flowering almond', + 'holly-leaved cherry', 'fuji', 'flowering almond', 'cherry laurel', + 'catalina cherry', 'bird cherry', 'hagberry tree', 'hagberry', 'pin cherry', + 'peach', 'nectarine', 'sand cherry', 'japanese plum', 'black cherry', + 'flowering cherry', 'oriental cherry', 'japanese flowering cherry', + 'sierra plum', 'rosebud cherry', 'russian almond', 'flowering almond', + 'chokecherry', 'chokecherry', 'western chokecherry', 'pyracantha', 'pear', + 'fruit tree', 'bramble bush', 'lawyerbush', 'stone bramble', + 'sand blackberry', 'boysenberry', 'loganberry', 'american dewberry', + 'northern dewberry', 'southern dewberry', 'swamp dewberry', + 'european dewberry', 'raspberry', 'wild raspberry', 'american raspberry', + 'black raspberry', 'salmonberry', 'salmonberry', 'wineberry', + 'mountain ash', 'rowan', 'rowanberry', 'american mountain ash', + 'western mountain ash', 'service tree', 'wild service tree', 'spirea', + 'bridal wreath', 'madderwort', 'indian madder', 'madder', 'woodruff', + 'dagame', 'blolly', 'coffee', 'arabian coffee', 'liberian coffee', + 'robusta coffee', 'cinchona', 'cartagena bark', 'calisaya', 'cinchona tree', + 'cinchona', 'bedstraw', 'sweet woodruff', 'northern bedstraw', + 'yellow bedstraw', 'wild licorice', 'cleavers', 'wild madder', + 'cape jasmine', 'genipa', 'genipap fruit', 'hamelia', 'scarlet bush', + 'lemonwood', 'negro peach', 'wild medlar', 'spanish tamarind', 'abelia', + 'bush honeysuckle', 'american twinflower', 'honeysuckle', + 'american fly honeysuckle', 'italian honeysuckle', 'yellow honeysuckle', + 'hairy honeysuckle', 'japanese honeysuckle', 'hall\'s honeysuckle', + 'morrow\'s honeysuckle', 'woodbine', 'trumpet honeysuckle', + 'european fly honeysuckle', 'swamp fly honeysuckle', 'snowberry', + 'coralberry', 'blue elder', 'dwarf elder', 'american red elder', + 'european red elder', 'feverroot', 'cranberry bush', 'wayfaring tree', + 'guelder rose', 'arrow wood', 'black haw', 'weigela', 'teasel', + 'common teasel', 'fuller\'s teasel', 'wild teasel', 'scabious', + 'sweet scabious', 'field scabious', 'jewelweed', 'geranium', 'cranesbill', + 'wild geranium', 'meadow cranesbill', 'richardson\'s geranium', + 'herb robert', 'sticky geranium', 'dove\'s foot geranium', 'rose geranium', + 'fish geranium', 'ivy geranium', 'apple geranium', 'lemon geranium', + 'storksbill', 'musk clover', 'incense tree', 'elephant tree', 'gumbo-limbo', + 'boswellia carteri', 'salai', 'balm of gilead', 'myrrh tree', + 'protium heptaphyllum', 'protium guianense', 'water starwort', + 'barbados cherry', 'mahogany', 'chinaberry', 'neem', 'neem seed', + 'spanish cedar', 'satinwood', 'african scented mahogany', 'silver ash', + 'native beech', 'bunji-bunji', 'african mahogany', 'lanseh tree', + 'true mahogany', 'honduras mahogany', 'philippine mahogany', 'caracolito', + 'common wood sorrel', 'bermuda buttercup', 'creeping oxalis', 'goatsfoot', + 'violet wood sorrel', 'oca', 'carambola', 'bilimbi', 'milkwort', 'senega', + 'orange milkwort', 'flowering wintergreen', 'seneca snakeroot', + 'common milkwort', 'rue', 'citrus', 'orange', 'sour orange', 'bergamot', + 'pomelo', 'citron', 'grapefruit', 'mandarin', 'tangerine', 'clementine', + 'satsuma', 'sweet orange', 'temple orange', 'tangelo', 'rangpur', 'lemon', + 'sweet lemon', 'lime', 'citrange', 'fraxinella', 'kumquat', 'marumi', + 'nagami', 'cork tree', 'trifoliate orange', 'prickly ash', 'toothache tree', + 'hercules\'-club', 'bitterwood tree', 'marupa', 'paradise tree', + 'ailanthus', 'tree of heaven', 'wild mango', 'pepper tree', + 'jamaica quassia', 'quassia', 'nasturtium', 'garden nasturtium', + 'bush nasturtium', 'canarybird flower', 'bean caper', 'palo santo', + 'lignum vitae', 'creosote bush', 'caltrop', 'willow', 'osier', + 'white willow', 'silver willow', 'golden willow', 'cricket-bat willow', + 'arctic willow', 'weeping willow', 'wisconsin weeping willow', + 'pussy willow', 'sallow', 'goat willow', 'peachleaf willow', + 'almond willow', 'hoary willow', 'crack willow', 'prairie willow', + 'dwarf willow', 'grey willow', 'arroyo willow', 'shining willow', + 'swamp willow', 'bay willow', 'purple willow', 'balsam willow', + 'creeping willow', 'sitka willow', 'dwarf grey willow', 'bearberry willow', + 'common osier', 'poplar', 'balsam poplar', 'white poplar', 'grey poplar', + 'black poplar', 'lombardy poplar', 'cottonwood', 'eastern cottonwood', + 'black cottonwood', 'swamp cottonwood', 'aspen', 'quaking aspen', + 'american quaking aspen', 'canadian aspen', 'sandalwood tree', 'quandong', + 'rabbitwood', 'loranthaceae', 'mistletoe', 'american mistletoe', + 'mistletoe', 'american mistletoe', 'aalii', 'soapberry', 'wild china tree', + 'china tree', 'akee', 'soapberry vine', 'heartseed', 'balloon vine', + 'longan', 'harpullia', 'harpulla', 'moreton bay tulipwood', 'litchi', + 'spanish lime', 'rambutan', 'pulasan', 'pachysandra', 'allegheny spurge', + 'bittersweet', 'spindle tree', 'winged spindle tree', 'wahoo', + 'strawberry bush', 'evergreen bittersweet', 'cyrilla', 'titi', 'crowberry', + 'maple', 'silver maple', 'sugar maple', 'red maple', 'moosewood', + 'oregon maple', 'dwarf maple', 'mountain maple', 'vine maple', + 'hedge maple', 'norway maple', 'sycamore', 'box elder', + 'california box elder', 'pointed-leaf maple', 'japanese maple', + 'japanese maple', 'holly', 'chinese holly', 'bearberry', 'inkberry', 'mate', + 'american holly', 'low gallberry holly', 'tall gallberry holly', + 'yaupon holly', 'deciduous holly', 'juneberry holly', 'largeleaf holly', + 'geogia holly', 'common winterberry holly', 'smooth winterberry holly', + 'cashew', 'goncalo alves', 'venetian sumac', 'laurel sumac', 'mango', + 'pistachio', 'terebinth', 'mastic', 'australian sumac', 'sumac', + 'smooth sumac', 'sugar-bush', 'staghorn sumac', 'squawbush', + 'aroeira blanca', 'pepper tree', 'brazilian pepper tree', 'hog plum', + 'mombin', 'poison ash', 'poison ivy', 'western poison oak', + 'eastern poison oak', 'varnish tree', 'horse chestnut', 'buckeye', + 'sweet buckeye', 'ohio buckeye', 'dwarf buckeye', 'red buckeye', + 'particolored buckeye', 'ebony', 'marblewood', 'marblewood', 'persimmon', + 'japanese persimmon', 'american persimmon', 'date plum', 'buckthorn', + 'southern buckthorn', 'false buckthorn', 'star apple', 'satinleaf', + 'balata', 'sapodilla', 'gutta-percha tree', 'gutta-percha tree', 'canistel', + 'marmalade tree', 'sweetleaf', 'asiatic sweetleaf', 'styrax', 'snowbell', + 'japanese snowbell', 'texas snowbell', 'silver-bell tree', + 'carnivorous plant', 'pitcher plant', 'common pitcher plant', + 'hooded pitcher plant', 'huntsman\'s horn', 'tropical pitcher plant', + 'sundew', 'venus\'s flytrap', 'waterwheel plant', + 'drosophyllum lusitanicum', 'roridula', 'australian pitcher plant', 'sedum', + 'stonecrop', 'rose-root', 'orpine', 'pinwheel', 'christmas bush', + 'hortensia', 'fall-blooming hydrangea', 'carpenteria', 'decumary', + 'deutzia', 'philadelphus', 'mock orange', 'saxifrage', + 'yellow mountain saxifrage', 'meadow saxifrage', 'mossy saxifrage', + 'western saxifrage', 'purple saxifrage', 'star saxifrage', + 'strawberry geranium', 'astilbe', 'false goatsbeard', 'dwarf astilbe', + 'spirea', 'bergenia', 'coast boykinia', 'golden saxifrage', + 'umbrella plant', 'bridal wreath', 'alumroot', 'coralbells', + 'leatherleaf saxifrage', 'woodland star', 'prairie star', 'miterwort', + 'five-point bishop\'s cap', 'parnassia', 'bog star', + 'fringed grass of parnassus', 'false alumroot', 'foamflower', + 'false miterwort', 'pickaback plant', 'currant', 'black currant', + 'white currant', 'gooseberry', 'plane tree', 'london plane', + 'american sycamore', 'oriental plane', 'california sycamore', + 'arizona sycamore', 'greek valerian', 'northern jacob\'s ladder', + 'skunkweed', 'phlox', 'moss pink', 'evening-snow', 'acanthus', + 'bear\'s breech', 'caricature plant', 'black-eyed susan', 'catalpa', + 'catalpa bignioides', 'catalpa speciosa', 'desert willow', 'calabash', + 'calabash', 'borage', 'common amsinckia', 'anchusa', 'bugloss', + 'cape forget-me-not', 'cape forget-me-not', 'spanish elm', 'princewood', + 'chinese forget-me-not', 'hound\'s-tongue', 'hound\'s-tongue', 'blueweed', + 'beggar\'s lice', 'gromwell', 'puccoon', 'virginia bluebell', + 'garden forget-me-not', 'forget-me-not', 'false gromwell', 'comfrey', + 'common comfrey', 'convolvulus', 'bindweed', 'field bindweed', 'scammony', + 'silverweed', 'dodder', 'dichondra', 'cypress vine', 'moonflower', + 'wild potato vine', 'red morning-glory', 'man-of-the-earth', 'scammony', + 'japanese morning glory', 'imperial japanese morning glory', 'gesneriad', + 'gesneria', 'achimenes', 'aeschynanthus', 'lace-flower vine', 'columnea', + 'episcia', 'gloxinia', 'canterbury bell', 'kohleria', 'african violet', + 'streptocarpus', 'cape primrose', 'waterleaf', 'virginia waterleaf', + 'yellow bells', 'yerba santa', 'nemophila', 'baby blue-eyes', 'five-spot', + 'scorpionweed', 'california bluebell', 'california bluebell', 'fiddleneck', + 'fiesta flower', 'basil thyme', 'giant hyssop', 'yellow giant hyssop', + 'anise hyssop', 'mexican hyssop', 'bugle', 'creeping bugle', 'erect bugle', + 'pyramid bugle', 'wood mint', 'hairy wood mint', 'downy wood mint', + 'calamint', 'common calamint', 'large-flowered calamint', 'lesser calamint', + 'wild basil', 'horse balm', 'coleus', 'country borage', 'painted nettle', + 'apalachicola rosemary', 'dragonhead', 'elsholtzia', 'hemp nettle', + 'ground ivy', 'pennyroyal', 'hyssop', 'dead nettle', 'white dead nettle', + 'henbit', 'english lavender', 'french lavender', 'spike lavender', 'dagga', + 'lion\'s-ear', 'motherwort', 'pitcher sage', 'bugleweed', 'water horehound', + 'gipsywort', 'origanum', 'oregano', 'sweet marjoram', 'horehound', + 'common horehound', 'lemon balm', 'corn mint', 'water-mint', + 'bergamot mint', 'horsemint', 'peppermint', 'spearmint', 'apple mint', + 'pennyroyal', 'yerba buena', 'molucca balm', 'monarda', 'bee balm', + 'horsemint', 'bee balm', 'lemon mint', 'plains lemon monarda', 'basil balm', + 'mustang mint', 'catmint', 'basil', 'beefsteak plant', 'phlomis', + 'jerusalem sage', 'physostegia', 'plectranthus', 'patchouli', 'self-heal', + 'mountain mint', 'rosemary', 'clary sage', 'purple sage', 'cancerweed', + 'common sage', 'meadow clary', 'clary', 'pitcher sage', 'mexican mint', + 'wild sage', 'savory', 'summer savory', 'winter savory', 'skullcap', + 'blue pimpernel', 'hedge nettle', 'hedge nettle', 'germander', + 'american germander', 'cat thyme', 'wood sage', 'thyme', 'common thyme', + 'wild thyme', 'blue curls', 'turpentine camphor weed', 'bastard pennyroyal', + 'bladderwort', 'butterwort', 'genlisea', 'martynia', 'common unicorn plant', + 'sand devil\'s claw', 'sweet unicorn plant', 'figwort', 'snapdragon', + 'white snapdragon', 'yellow twining snapdragon', 'mediterranean snapdragon', + 'kitten-tails', 'alpine besseya', 'false foxglove', 'false foxglove', + 'calceolaria', 'indian paintbrush', 'desert paintbrush', + 'giant red paintbrush', 'great plains paintbrush', 'sulfur paintbrush', + 'shellflower', 'maiden blue-eyed mary', 'blue-eyed mary', 'foxglove', + 'common foxglove', 'yellow foxglove', 'gerardia', 'blue toadflax', + 'toadflax', 'golden-beard penstemon', 'scarlet bugler', + 'red shrubby penstemon', 'platte river penstemon', 'hot-rock penstemon', + 'jones\' penstemon', 'shrubby penstemon', 'narrow-leaf penstemon', + 'balloon flower', 'parry\'s penstemon', 'rock penstemon', + 'rydberg\'s penstemon', 'cascade penstemon', 'whipple\'s penstemon', + 'moth mullein', 'white mullein', 'purple mullein', 'common mullein', + 'veronica', 'field speedwell', 'brooklime', 'corn speedwell', 'brooklime', + 'germander speedwell', 'water speedwell', 'common speedwell', + 'purslane speedwell', 'thyme-leaved speedwell', 'nightshade', + 'horse nettle', 'african holly', 'potato vine', 'garden huckleberry', + 'naranjilla', 'potato vine', 'potato tree', 'belladonna', 'bush violet', + 'lady-of-the-night', 'angel\'s trumpet', 'angel\'s trumpet', + 'red angel\'s trumpet', 'cone pepper', 'bird pepper', 'day jessamine', + 'night jasmine', 'tree tomato', 'thorn apple', 'jimsonweed', 'pichi', + 'henbane', 'egyptian henbane', 'matrimony vine', 'common matrimony vine', + 'christmasberry', 'plum tomato', 'mandrake', 'mandrake root', + 'apple of peru', 'flowering tobacco', 'common tobacco', 'wild tobacco', + 'cupflower', 'whitecup', 'petunia', 'large white petunia', + 'violet-flowered petunia', 'hybrid petunia', 'cape gooseberry', + 'strawberry tomato', 'tomatillo', 'tomatillo', 'yellow henbane', + 'cock\'s eggs', 'salpiglossis', 'painted tongue', 'butterfly flower', + 'scopolia carniolica', 'chalice vine', 'verbena', 'lantana', + 'black mangrove', 'white mangrove', 'black mangrove', 'teak', 'spurge', + 'sun spurge', 'petty spurge', 'medusa\'s head', 'wild spurge', + 'snow-on-the-mountain', 'cypress spurge', 'leafy spurge', 'hairy spurge', + 'poinsettia', 'japanese poinsettia', 'fire-on-the-mountain', 'wood spurge', + 'dwarf spurge', 'scarlet plume', 'naboom', 'crown of thorns', + 'toothed spurge', 'three-seeded mercury', 'croton', 'cascarilla', + 'cascarilla bark', 'castor-oil plant', 'spurge nettle', 'physic nut', + 'para rubber tree', 'cassava', 'bitter cassava', 'cassava', 'sweet cassava', + 'candlenut', 'tung tree', 'slipper spurge', 'candelilla', 'jewbush', + 'jumping bean', 'camellia', 'japonica', 'umbellifer', 'wild parsley', + 'fool\'s parsley', 'dill', 'angelica', 'garden angelica', 'wild angelica', + 'chervil', 'cow parsley', 'wild celery', 'astrantia', 'greater masterwort', + 'caraway', 'whorled caraway', 'water hemlock', 'spotted cowbane', 'hemlock', + 'earthnut', 'cumin', 'wild carrot', 'eryngo', 'sea holly', + 'button snakeroot', 'rattlesnake master', 'fennel', 'common fennel', + 'florence fennel', 'cow parsnip', 'lovage', 'sweet cicely', 'water fennel', + 'parsnip', 'cultivated parsnip', 'wild parsnip', 'parsley', + 'italian parsley', 'hamburg parsley', 'anise', 'sanicle', 'purple sanicle', + 'european sanicle', 'water parsnip', 'greater water parsnip', 'skirret', + 'dogwood', 'common white dogwood', 'red osier', 'silky dogwood', + 'silky cornel', 'common european dogwood', 'bunchberry', 'cornelian cherry', + 'puka', 'kapuka', 'valerian', 'common valerian', 'common corn salad', + 'red valerian', 'filmy fern', 'bristle fern', 'hare\'s-foot bristle fern', + 'killarney fern', 'kidney fern', 'flowering fern', 'royal fern', + 'interrupted fern', 'crape fern', 'crepe fern', 'curly grass', 'pine fern', + 'climbing fern', 'creeping fern', 'climbing maidenhair', 'scented fern', + 'clover fern', 'nardoo', 'water clover', 'pillwort', 'regnellidium', + 'floating-moss', 'mosquito fern', 'adder\'s tongue', 'ribbon fern', + 'grape fern', 'daisyleaf grape fern', 'leathery grape fern', + 'rattlesnake fern', 'flowering fern', 'powdery mildew', 'dutch elm fungus', + 'ergot', 'rye ergot', 'black root rot fungus', 'dead-man\'s-fingers', + 'sclerotinia', 'brown cup', 'earthball', 'scleroderma citrinum', + 'scleroderma flavidium', 'scleroderma bovista', 'podaxaceae', + 'stalked puffball', 'stalked puffball', 'false truffle', + 'rhizopogon idahoensis', 'truncocolumella citrina', 'mucor', 'rhizopus', + 'bread mold', 'slime mold', 'true slime mold', 'cellular slime mold', + 'dictostylium', 'pond-scum parasite', 'potato wart fungus', 'white fungus', + 'water mold', 'downy mildew', 'blue mold fungus', 'onion mildew', + 'tobacco mildew', 'white rust', 'pythium', 'damping off fungus', + 'phytophthora citrophthora', 'phytophthora infestans', 'clubroot fungus', + 'geglossaceae', 'sarcosomataceae', 'rufous rubber cup', 'devil\'s cigar', + 'devil\'s urn', 'truffle', 'club fungus', 'coral fungus', 'tooth fungus', + 'lichen', 'ascolichen', 'basidiolichen', 'lecanora', 'manna lichen', + 'archil', 'roccella', 'beard lichen', 'horsehair lichen', 'reindeer moss', + 'crottle', 'iceland moss', 'fungus', 'promycelium', 'true fungus', + 'basidiomycete', 'mushroom', 'agaric', 'mushroom', 'mushroom', 'toadstool', + 'horse mushroom', 'meadow mushroom', 'shiitake', 'scaly lentinus', + 'royal agaric', 'false deathcap', 'fly agaric', 'death cap', + 'blushing mushroom', 'destroying angel', 'chanterelle', + 'floccose chanterelle', 'pig\'s ears', 'cinnabar chanterelle', + 'jack-o-lantern fungus', 'inky cap', 'shaggymane', 'milkcap', + 'fairy-ring mushroom', 'fairy ring', 'oyster mushroom', 'olive-tree agaric', + 'pholiota astragalina', 'pholiota aurea', 'pholiota destruens', + 'pholiota flammans', 'pholiota flavida', 'nameko', + 'pholiota squarrosa-adiposa', 'pholiota squarrosa', + 'pholiota squarrosoides', 'stropharia ambigua', 'stropharia hornemannii', + 'stropharia rugoso-annulata', 'gill fungus', 'entoloma lividum', + 'entoloma aprile', 'chlorophyllum molybdites', 'lepiota', + 'parasol mushroom', 'poisonous parasol', 'lepiota naucina', + 'lepiota rhacodes', 'american parasol', 'lepiota rubrotincta', + 'lepiota clypeolaria', 'onion stem', 'pink disease fungus', + 'bottom rot fungus', 'potato fungus', 'coffee fungus', 'blewits', + 'sandy mushroom', 'tricholoma pessundatum', 'tricholoma sejunctum', + 'man-on-a-horse', 'tricholoma venenata', 'tricholoma pardinum', + 'tricholoma vaccinum', 'tricholoma aurantium', 'volvaria bombycina', + 'pluteus aurantiorugosus', 'pluteus magnus', 'deer mushroom', + 'straw mushroom', 'volvariella bombycina', 'clitocybe clavipes', + 'clitocybe dealbata', 'clitocybe inornata', 'clitocybe robusta', + 'clitocybe irina', 'clitocybe subconnexa', 'winter mushroom', 'mycelium', + 'sclerotium', 'sac fungus', 'ascomycete', 'clavicipitaceae', 'grainy club', + 'yeast', 'baker\'s yeast', 'wine-maker\'s yeast', 'aspergillus fumigatus', + 'brown root rot fungus', 'discomycete', 'leotia lubrica', 'mitrula elegans', + 'sarcoscypha coccinea', 'caloscypha fulgens', 'aleuria aurantia', 'elf cup', + 'peziza domicilina', 'blood cup', 'urnula craterium', 'galiella rufa', + 'jafnea semitosta', 'morel', 'common morel', 'disciotis venosa', 'verpa', + 'verpa bohemica', 'verpa conica', 'black morel', 'morchella crassipes', + 'morchella semilibera', 'wynnea americana', 'wynnea sparassoides', + 'false morel', 'lorchel', 'helvella', 'helvella crispa', + 'helvella acetabulum', 'helvella sulcata', 'discina', 'gyromitra', + 'gyromitra californica', 'gyromitra sphaerospora', 'gyromitra esculenta', + 'gyromitra infula', 'gyromitra fastigiata', 'gyromitra gigas', + 'gasteromycete', 'stinkhorn', 'common stinkhorn', 'phallus ravenelii', + 'dog stinkhorn', 'calostoma lutescens', 'calostoma cinnabarina', + 'calostoma ravenelii', 'stinky squid', 'puffball', 'giant puffball', + 'earthstar', 'geastrum coronatum', 'radiigera fuscogleba', + 'astreus pteridis', 'astreus hygrometricus', 'bird\'s-nest fungus', + 'gastrocybe lateritia', 'macowanites americanus', 'polypore', + 'bracket fungus', 'albatrellus dispansus', 'albatrellus ovinus', + 'neolentinus ponderosus', 'oligoporus leucospongia', 'polyporus tenuiculus', + 'hen-of-the-woods', 'polyporus squamosus', 'beefsteak fungus', 'agaric', + 'bolete', 'boletus chrysenteron', 'boletus edulis', 'frost\'s bolete', + 'boletus luridus', 'boletus mirabilis', 'boletus pallidus', + 'boletus pulcherrimus', 'boletus pulverulentus', 'boletus roxanae', + 'boletus subvelutipes', 'boletus variipes', 'boletus zelleri', + 'fuscoboletinus paluster', 'fuscoboletinus serotinus', + 'leccinum fibrillosum', 'suillus albivelatus', 'old-man-of-the-woods', + 'boletellus russellii', 'jelly fungus', 'snow mushroom', 'witches\' butter', + 'tremella foliacea', 'tremella reticulata', 'jew\'s-ear', 'rust', 'aecium', + 'flax rust', 'blister rust', 'wheat rust', 'apple rust', 'smut', + 'covered smut', 'loose smut', 'cornsmut', 'boil smut', 'sphacelotheca', + 'head smut', 'bunt', 'bunt', 'onion smut', 'flag smut fungus', + 'wheat flag smut', 'felt fungus', 'waxycap', 'hygrocybe acutoconica', + 'hygrophorus borealis', 'hygrophorus caeruleus', + 'hygrophorus inocybiformis', 'hygrophorus kauffmanii', + 'hygrophorus marzuolus', 'hygrophorus purpurascens', 'hygrophorus russula', + 'hygrophorus sordidus', 'hygrophorus tennesseensis', 'hygrophorus turundus', + 'neohygrophorus angelesianus', 'cortinarius armillatus', + 'cortinarius atkinsonianus', 'cortinarius corrugatus', + 'cortinarius gentilis', 'cortinarius mutabilis', + 'cortinarius semisanguineus', 'cortinarius subfoetidus', + 'cortinarius violaceus', 'gymnopilus spectabilis', 'gymnopilus validipes', + 'gymnopilus ventricosus', 'mold', 'mildew', 'verticillium', 'monilia', + 'candida', 'candida albicans', 'blastomycete', 'yellow spot fungus', + 'green smut fungus', 'dry rot', 'rhizoctinia', 'houseplant', 'bedder', + 'succulent', 'cultivar', 'weed', 'wort', 'brier', 'aril', 'sporophyll', + 'sporangium', 'sporangiophore', 'ascus', 'ascospore', 'arthrospore', + 'eusporangium', 'tetrasporangium', 'gametangium', 'sorus', 'sorus', + 'partial veil', 'lignum', 'vascular ray', 'phloem', 'evergreen', + 'deciduous plant', 'poisonous plant', 'vine', 'creeper', 'tendril', + 'root climber', 'lignosae', 'arborescent plant', 'snag', 'tree', + 'timber tree', 'treelet', 'arbor', 'bean tree', 'pollard', 'sapling', + 'shade tree', 'gymnospermous tree', 'conifer', 'angiospermous tree', + 'nut tree', 'spice tree', 'fever tree', 'stump', 'bonsai', 'ming tree', + 'ming tree', 'undershrub', 'subshrub', 'bramble', 'liana', 'geophyte', + 'desert plant', 'mesophyte', 'marsh plant', 'hemiepiphyte', 'strangler', + 'lithophyte', 'saprobe', 'autophyte', 'root', 'taproot', 'prop root', + 'prophyll', 'rootstock', 'quickset', 'stolon', 'tuberous plant', 'rhizome', + 'rachis', 'caudex', 'cladode', 'receptacle', 'scape', 'umbel', 'petiole', + 'peduncle', 'pedicel', 'flower cluster', 'raceme', 'panicle', 'thyrse', + 'cyme', 'cymule', 'glomerule', 'scorpioid cyme', 'ear', 'spadix', + 'bulbous plant', 'bulbil', 'cormous plant', 'fruit', 'fruitlet', 'seed', + 'bean', 'nut', 'nutlet', 'kernel', 'syconium', 'berry', 'aggregate fruit', + 'simple fruit', 'acinus', 'drupe', 'drupelet', 'pome', 'pod', 'loment', + 'pyxidium', 'husk', 'cornhusk', 'pod', 'accessory fruit', 'buckthorn', + 'buckthorn berry', 'cascara buckthorn', 'cascara', 'carolina buckthorn', + 'coffeeberry', 'redberry', 'nakedwood', 'jujube', 'christ\'s-thorn', + 'hazel', 'fox grape', 'muscadine', 'vinifera', 'pinot blanc', + 'sauvignon grape', 'sauvignon blanc', 'muscadet', 'riesling', 'zinfandel', + 'chenin blanc', 'malvasia', 'verdicchio', 'boston ivy', 'virginia creeper', + 'true pepper', 'betel', 'cubeb', 'schizocarp', 'peperomia', + 'watermelon begonia', 'yerba mansa', 'pinna', 'frond', 'bract', 'bracteole', + 'involucre', 'glume', 'palmate leaf', 'pinnate leaf', 'bijugate leaf', + 'decompound leaf', 'acuminate leaf', 'deltoid leaf', 'ensiform leaf', + 'linear leaf', 'lyrate leaf', 'obtuse leaf', 'oblanceolate leaf', + 'pandurate leaf', 'reniform leaf', 'spatulate leaf', 'even-pinnate leaf', + 'odd-pinnate leaf', 'pedate leaf', 'crenate leaf', 'dentate leaf', + 'denticulate leaf', 'erose leaf', 'runcinate leaf', 'prickly-edged leaf', + 'deadwood', 'haulm', 'branchlet', 'osier', 'giant scrambling fern', + 'umbrella fern', 'floating fern', 'polypody', 'licorice fern', + 'grey polypody', 'leatherleaf', 'rock polypody', 'common polypody', + 'bear\'s-paw fern', 'strap fern', 'florida strap fern', 'basket fern', + 'snake polypody', 'climbing bird\'s nest fern', 'golden polypody', + 'staghorn fern', 'south american staghorn', 'common staghorn fern', + 'felt fern', 'potato fern', 'myrmecophyte', 'grass fern', 'spleenwort', + 'black spleenwort', 'bird\'s nest fern', 'ebony spleenwort', + 'black-stem spleenwort', 'walking fern', 'green spleenwort', + 'mountain spleenwort', 'lobed spleenwort', 'lanceolate spleenwort', + 'hart\'s-tongue', 'scale fern', 'scolopendrium', 'deer fern', 'doodia', + 'chain fern', 'virginia chain fern', 'silver tree fern', 'davallia', + 'hare\'s-foot fern', 'canary island hare\'s foot fern', + 'squirrel\'s-foot fern', 'bracken', 'soft tree fern', 'scythian lamb', + 'false bracken', 'thyrsopteris', 'shield fern', 'broad buckler-fern', + 'fragrant cliff fern', 'goldie\'s fern', 'wood fern', 'male fern', + 'marginal wood fern', 'mountain male fern', 'lady fern', 'alpine lady fern', + 'silvery spleenwort', 'holly fern', 'bladder fern', 'brittle bladder fern', + 'mountain bladder fern', 'bulblet fern', 'silvery spleenwort', 'oak fern', + 'limestone fern', 'ostrich fern', 'hart\'s-tongue', 'sensitive fern', + 'christmas fern', 'holly fern', 'braun\'s holly fern', 'western holly fern', + 'soft shield fern', 'leather fern', 'button fern', 'indian button fern', + 'woodsia', 'rusty woodsia', 'alpine woodsia', 'smooth woodsia', + 'boston fern', 'basket fern', 'golden fern', 'maidenhair', + 'common maidenhair', 'american maidenhair fern', 'bermuda maidenhair', + 'brittle maidenhair', 'farley maidenhair', 'annual fern', 'lip fern', + 'smooth lip fern', 'lace fern', 'wooly lip fern', 'southwestern lip fern', + 'bamboo fern', 'american rock brake', 'european parsley fern', 'hand fern', + 'cliff brake', 'coffee fern', 'purple rock brake', 'bird\'s-foot fern', + 'button fern', 'silver fern', 'golden fern', 'gold fern', 'pteris cretica', + 'spider brake', 'ribbon fern', 'potato fern', 'angiopteris', + 'skeleton fork fern', 'horsetail', 'common horsetail', 'swamp horsetail', + 'scouring rush', 'marsh horsetail', 'wood horsetail', + 'variegated horsetail', 'club moss', 'shining clubmoss', 'alpine clubmoss', + 'fir clubmoss', 'ground cedar', 'ground fir', 'foxtail grass', 'spikemoss', + 'meadow spikemoss', 'desert selaginella', 'resurrection plant', + 'florida selaginella', 'quillwort', 'earthtongue', 'snuffbox fern', + 'christella', 'mountain fern', 'new york fern', 'massachusetts fern', + 'beech fern', 'broad beech fern', 'long beech fern', 'shoestring fungus', + 'armillaria caligata', 'armillaria ponderosa', 'armillaria zelleri', + 'honey mushroom', 'milkweed', 'white milkweed', 'poke milkweed', + 'swamp milkweed', 'mead\'s milkweed', 'purple silkweed', 'showy milkweed', + 'poison milkweed', 'butterfly weed', 'whorled milkweed', 'cruel plant', + 'wax plant', 'silk vine', 'stapelia', 'stapelias asterias', 'stephanotis', + 'madagascar jasmine', 'negro vine', 'zygospore', 'tree of knowledge', + 'orangery', 'pocketbook', 'shit', 'cordage', 'yard', 'extremum', + 'leaf shape', 'equilateral', 'figure', 'pencil', 'plane figure', + 'solid figure', 'line', 'bulb', 'convex shape', 'concave shape', 'cylinder', + 'round shape', 'heart', 'polygon', 'convex polygon', 'concave polygon', + 'reentrant polygon', 'amorphous shape', 'closed curve', + 'simple closed curve', 's-shape', 'wave', 'extrados', 'hook', 'envelope', + 'bight', 'diameter', 'cone', 'funnel', 'oblong', 'circle', 'circle', + 'equator', 'scallop', 'ring', 'loop', 'bight', 'helix', 'element of a cone', + 'element of a cylinder', 'ellipse', 'quadrate', 'triangle', + 'acute triangle', 'isosceles triangle', 'obtuse triangle', 'right triangle', + 'scalene triangle', 'parallel', 'trapezoid', 'star', 'pentagon', 'hexagon', + 'heptagon', 'octagon', 'nonagon', 'decagon', 'rhombus', 'spherical polygon', + 'spherical triangle', 'convex polyhedron', 'concave polyhedron', 'cuboid', + 'quadrangular prism', 'bell', 'angular distance', 'true anomaly', + 'spherical angle', 'angle of refraction', 'acute angle', 'groove', 'rut', + 'bulge', 'belly', 'bow', 'crescent', 'ellipsoid', 'hypotenuse', 'balance', + 'conformation', 'symmetry', 'spheroid', 'spherule', 'toroid', 'column', + 'barrel', 'pipe', 'pellet', 'bolus', 'dewdrop', 'ridge', 'rim', 'taper', + 'boundary', 'incisure', 'notch', 'wrinkle', 'dermatoglyphic', 'frown line', + 'line of life', 'line of heart', 'crevice', 'cleft', 'roulette', 'node', + 'tree', 'stemma', 'brachium', 'fork', 'block', 'ovoid', 'tetrahedron', + 'pentahedron', 'hexahedron', 'regular polyhedron', 'polyhedral angle', + 'cube', 'truncated pyramid', 'truncated cone', 'tail', 'tongue', + 'trapezohedron', 'wedge', 'keel', 'place', 'herpes', 'chlamydia', 'wall', + 'micronutrient', 'chyme', 'ragweed pollen', 'pina cloth', + 'chlorobenzylidenemalononitrile', 'carbon', 'charcoal', 'rock', 'gravel', + 'aflatoxin', 'alpha-tocopheral', 'leopard', 'bricks and mortar', 'lagging', + 'hydraulic cement', 'choline', 'concrete', 'glass wool', 'soil', + 'high explosive', 'litter', 'fish meal', 'greek fire', 'culture medium', + 'agar', 'blood agar', 'hip tile', 'hyacinth', 'hydroxide ion', 'ice', + 'inositol', 'linoleum', 'lithia water', 'lodestone', 'pantothenic acid', + 'paper', 'papyrus', 'pantile', 'blacktop', 'tarmacadam', 'paving', + 'plaster', 'poison gas', 'ridge tile', 'roughcast', 'sand', 'spackle', + 'render', 'wattle and daub', 'stucco', 'tear gas', 'toilet tissue', + 'linseed', 'vitamin', 'fat-soluble vitamin', 'water-soluble vitamin', + 'vitamin a', 'vitamin a1', 'vitamin a2', 'b-complex vitamin', 'vitamin b1', + 'vitamin b12', 'vitamin b2', 'vitamin b6', 'vitamin bc', 'niacin', + 'vitamin d', 'vitamin e', 'biotin', 'vitamin k', 'vitamin k1', 'vitamin k3', + 'vitamin p', 'vitamin c', 'planking', 'chipboard', 'knothole', +] diff --git a/Tipsomaly/model/big_vision/datasets/infovqa/infovqa.py b/Tipsomaly/model/big_vision/datasets/infovqa/infovqa.py new file mode 100644 index 0000000000000000000000000000000000000000..4e6500548954b706ab0ebd203589871cca898b24 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/jsonl.py b/Tipsomaly/model/big_vision/datasets/jsonl.py new file mode 100644 index 0000000000000000000000000000000000000000..719deba2b25987a4b9d58b56474e420cb5b1e706 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/nocaps/nocaps.py b/Tipsomaly/model/big_vision/datasets/nocaps/nocaps.py new file mode 100644 index 0000000000000000000000000000000000000000..fab90082bf3852100c59230ee23ddeb923144792 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/okvqa/okvqa.py b/Tipsomaly/model/big_vision/datasets/okvqa/okvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9a5631c440bdca28627f656c87fdb4b8b3f7164e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/pope/pope.py b/Tipsomaly/model/big_vision/datasets/pope/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..3f266d6ce21d218186736e434db328744a377389 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/refcoco/refcoco.py b/Tipsomaly/model/big_vision/datasets/refcoco/refcoco.py new file mode 100644 index 0000000000000000000000000000000000000000..27d391bfdbc4ffcdb0467940ad5597119089a1d5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/rsvqa_hr/rsvqa_hr.py b/Tipsomaly/model/big_vision/datasets/rsvqa_hr/rsvqa_hr.py new file mode 100644 index 0000000000000000000000000000000000000000..9f41612edfa9606572c316a68c8908061933e6c7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/rsvqa_lr/rsvqa_lr.py b/Tipsomaly/model/big_vision/datasets/rsvqa_lr/rsvqa_lr.py new file mode 100644 index 0000000000000000000000000000000000000000..7b2a963446ab2524141e55f07af4b64cf483c54e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/scicap/scicap.py b/Tipsomaly/model/big_vision/datasets/scicap/scicap.py new file mode 100644 index 0000000000000000000000000000000000000000..3aa714981785b6c5cda5a6e880160987e2a2e250 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/science_qa/science_qa.py b/Tipsomaly/model/big_vision/datasets/science_qa/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..8eb8189e57fbe9352d4b82044e8d7e44576052f5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/screen2words/screen2words.py b/Tipsomaly/model/big_vision/datasets/screen2words/screen2words.py new file mode 100644 index 0000000000000000000000000000000000000000..97d469b13734d1a566cbfa05e8e1de91c6e55e6b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/sequence_packing.py b/Tipsomaly/model/big_vision/datasets/sequence_packing.py new file mode 100644 index 0000000000000000000000000000000000000000..48966d3c488886b3ab0d0f061a1c88c57fdeabae --- /dev/null +++ b/Tipsomaly/model/big_vision/datasets/sequence_packing.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. + +"""Packed Sequence Op.""" + +# Forked from +# https://github.com/google/maxtext/blob/main/MaxText/sequence_packing.py. + + +from typing import Dict, Optional, List, Union + +from flax import traverse_util +import tensorflow as tf + +AUTOTUNE = tf.data.experimental.AUTOTUNE +FLATTEN_SEPARATOR = "<|sep|>" + + +def pack_dataset( + dataset: tf.data.Dataset, + batch_size: int | None, + key2length: Union[int, Dict[str, int]], + keys: Optional[List[str | tuple[str, ...]]] = None) -> tf.data.Dataset: + """Creates a 'packed' version of a dataset on-the-fly. + + Wrap `tensorflow.grain` ops. + + 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: + _segment_ids: an int32 tensor identifying the parts + representing the original example. + _positions: 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`. + batch_size: Batch size of the packed 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`. + """ + raise ValueError("Not implemented in OSS yet.") diff --git a/Tipsomaly/model/big_vision/datasets/stvqa/stvqa.py b/Tipsomaly/model/big_vision/datasets/stvqa/stvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..6d215554bd820411415a1a490c6337a43ad61258 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/tallyqa/tallyqa.py b/Tipsomaly/model/big_vision/datasets/tallyqa/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..0fcde490c89f652fc2c3a9397670c340016f1eb7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/textcaps/textcaps.py b/Tipsomaly/model/big_vision/datasets/textcaps/textcaps.py new file mode 100644 index 0000000000000000000000000000000000000000..4b004f03082d4e576c3a306dab3a94a3887059b3 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/textvqa/textvqa.py b/Tipsomaly/model/big_vision/datasets/textvqa/textvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..a9b996c682b9bf6fe156e1dcbc1b089ac40cf751 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/tfds.py b/Tipsomaly/model/big_vision/datasets/tfds.py new file mode 100644 index 0000000000000000000000000000000000000000..0c15dbc26f46e87d4df27027c1cca5a01b5e74fa --- /dev/null +++ b/Tipsomaly/model/big_vision/datasets/tfds.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. + +"""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, shuffle_files, split=None, **rckw): + """Returns a tf.data to be used.""" + ds = builder.as_dataset( + split=split, shuffle_files=shuffle_files, + 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 + }) + + 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/Tipsomaly/model/big_vision/datasets/vizwizvqa/vizwizvqa.py b/Tipsomaly/model/big_vision/datasets/vizwizvqa/vizwizvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9f30a124e916f0557e1d101b2952a22485ccc5bd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/vqa/vqa.py b/Tipsomaly/model/big_vision/datasets/vqa/vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..b310ae20cf2efca3eeb18d55819a852bb9a2479a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/widgetcap/widgetcap.py b/Tipsomaly/model/big_vision/datasets/widgetcap/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..cfb7cab5589291e7f77eda05df3ee02689a1040a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/xgqa/xgqa.py b/Tipsomaly/model/big_vision/datasets/xgqa/xgqa.py new file mode 100644 index 0000000000000000000000000000000000000000..ea94addf8bd04fa1ea48694476b6e9cd7911ffe6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/datasets/xm3600/xm3600.py b/Tipsomaly/model/big_vision/datasets/xm3600/xm3600.py new file mode 100644 index 0000000000000000000000000000000000000000..599b3bd4df3fb590f09b24b9d99afe04921f19bd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/__init__.py b/Tipsomaly/model/big_vision/evaluators/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/evaluators/classification.py b/Tipsomaly/model/big_vision/evaluators/classification.py new file mode 100644 index 0000000000000000000000000000000000000000..263ead8f5027f4b8e640b9ba42a72b3cbc33adf2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/common.py b/Tipsomaly/model/big_vision/evaluators/common.py new file mode 100644 index 0000000000000000000000000000000000000000..16a27e50b31f8d64fbdf028ce184cfb0727f37ab --- /dev/null +++ b/Tipsomaly/model/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 or "")), + 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/Tipsomaly/model/big_vision/evaluators/fewshot_lsr.py b/Tipsomaly/model/big_vision/evaluators/fewshot_lsr.py new file mode 100644 index 0000000000000000000000000000000000000000..1b7019ad3fa58936975b631206947b3b33ecdc67 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/mean.py b/Tipsomaly/model/big_vision/evaluators/mean.py new file mode 100644 index 0000000000000000000000000000000000000000..a38fb21d3cd7ab7d37a5734c67640994c0956b36 --- /dev/null +++ b/Tipsomaly/model/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`.""" + metrics = predict_fn(train_state, batch) + mask = batch['_mask'] + # 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/Tipsomaly/model/big_vision/evaluators/proj/cappa/perplexity.py b/Tipsomaly/model/big_vision/evaluators/proj/cappa/perplexity.py new file mode 100644 index 0000000000000000000000000000000000000000..2ce69398be4243304ef62a7a1276172a4e648787 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/cappa/scoring_classifier.py b/Tipsomaly/model/big_vision/evaluators/proj/cappa/scoring_classifier.py new file mode 100644 index 0000000000000000000000000000000000000000..60906bacac2e6c0a2cabd106e7d6d82a06c08b8e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/distill/distance.py b/Tipsomaly/model/big_vision/evaluators/proj/distill/distance.py new file mode 100644 index 0000000000000000000000000000000000000000..2cc35391fe3e8f92ac88b1ba21e137d6685b88ed --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/givt/coco_panoptic.py b/Tipsomaly/model/big_vision/evaluators/proj/givt/coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..264f30a80ad51b131c25f59bad4aef2f19125730 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/givt/nyu_depth.py b/Tipsomaly/model/big_vision/evaluators/proj/givt/nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..23c6b3559703ed9af18bd1bf7de5371abe458e1c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/givt/save_predictions.py b/Tipsomaly/model/big_vision/evaluators/proj/givt/save_predictions.py new file mode 100644 index 0000000000000000000000000000000000000000..d72c48e31ee754cc2722c8e062db064c5f110264 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/contrastive.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/contrastive.py new file mode 100644 index 0000000000000000000000000000000000000000..0726d2066cb7c406aec47c508e195b2f40399ff5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/discriminative_classifier.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/discriminative_classifier.py new file mode 100644 index 0000000000000000000000000000000000000000..e233564404c17ef9e8d898d13a57a96148f6c9ae --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/discriminative_classifier_test.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/discriminative_classifier_test.py new file mode 100644 index 0000000000000000000000000000000000000000..06f4fe28910191555779c21a838032ae8bc506cd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/image_text_retrieval.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/image_text_retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..8d0f7b2d4b84b121b8a642ee301835d1dc59ed62 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/image_text_retrieval_test.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/image_text_retrieval_test.py new file mode 100644 index 0000000000000000000000000000000000000000..5125b17f77d9c0c3a15f0c358415334af0b6c216 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering.py new file mode 100644 index 0000000000000000000000000000000000000000..52450f6a7640d9865dbf19e11945281c9464657d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering_constants.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering_constants.py new file mode 100644 index 0000000000000000000000000000000000000000..0997971f639711e592f83f71daafd0d6d47f6e49 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering_test.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/prompt_engineering_test.py new file mode 100644 index 0000000000000000000000000000000000000000..e6833c60157d01ba9d787115e643b85506e57d4f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/retrieval.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/retrieval.py new file mode 100644 index 0000000000000000000000000000000000000000..daf43927f3273dca1232567578614aa6f783f613 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/image_text/retrieval_test.py b/Tipsomaly/model/big_vision/evaluators/proj/image_text/retrieval_test.py new file mode 100644 index 0000000000000000000000000000000000000000..6a6a993df88819d8d626710bb8135444fba2130a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/perplexity.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/perplexity.py new file mode 100644 index 0000000000000000000000000000000000000000..1efc1b4983bb710ffd63f6a50ce837fde7698913 --- /dev/null +++ b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/perplexity.py @@ -0,0 +1,63 @@ +# 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, pad_token=None): + """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 pad_token is not None: + weights = weights * (labels != pad_token).astype(jnp.float32) + + 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, pad_token=None, **kw): + kw.setdefault('prefetch', 0) # More memory-saving default. + super().__init__( + perplexity(predict_fn, key, shift_labels, pad_token), *a, **kw) diff --git a/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/chartqa.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/chartqa.py new file mode 100644 index 0000000000000000000000000000000000000000..41dadb9e1fb148e83ecf4b09673d3346722c7b96 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/coco_caption.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/coco_caption.py new file mode 100644 index 0000000000000000000000000000000000000000..69721ff57dc061432c7e06e2cd3f1c5b7f9a4cde --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/pope.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/pope.py new file mode 100644 index 0000000000000000000000000000000000000000..7ef4df53f89f5929b644e6b95881df7c77c3cdbb --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/rsvqa.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/rsvqa.py new file mode 100644 index 0000000000000000000000000000000000000000..575bd12d1f53b20b1393bba264f5c38ae08f7c6b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/science_qa.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/science_qa.py new file mode 100644 index 0000000000000000000000000000000000000000..cbf61fe3785e631231f016f9b007ff572dca3c2d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/segmentation.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..6180ae09e6aa9c393ccee807bb0d423060ed7b43 --- /dev/null +++ b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/segmentation.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. + +"""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( # pytype: disable=wrong-arg-types # pillow-102-upgrade + [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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/storepreds.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/storepreds.py new file mode 100644 index 0000000000000000000000000000000000000000..ee7314754a00c49dc9d7a5583c860607530e07f0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/tallyqa.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/tallyqa.py new file mode 100644 index 0000000000000000000000000000000000000000..b2f219a5737ece149798004f0cf3ba6100f75385 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/vqa.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/vqa.py new file mode 100644 index 0000000000000000000000000000000000000000..9f6ddccaf3640306ec9bbd9fe62e3a34ca78b78b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/vqav2.py b/Tipsomaly/model/big_vision/evaluators/proj/paligemma/transfers/vqav2.py new file mode 100644 index 0000000000000000000000000000000000000000..5f6f2a1839a6f9644674ca20de880edce22ddd11 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/coco_panoptic.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/coco_panoptic.py new file mode 100644 index 0000000000000000000000000000000000000000..b20d4458826dfe568b5a8f85246e1635c91a3f3b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid.py new file mode 100644 index 0000000000000000000000000000000000000000..555a7a2b008e6c4a09e172eed250cef92cd04c1b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid_data/eval_file_names.txt b/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid_data/eval_file_names.txt new file mode 100644 index 0000000000000000000000000000000000000000..d3a08b9dd2cc6bc41e07ea92039e49b564312153 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid_data/reference_file_names.txt b/Tipsomaly/model/big_vision/evaluators/proj/uvim/coltran_fid_data/reference_file_names.txt new file mode 100644 index 0000000000000000000000000000000000000000..8c4faa20f48bfb29e6f8c4e4d70823795c846672 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/common.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/common.py new file mode 100644 index 0000000000000000000000000000000000000000..937fde88ea416c87346e2dd9dfbd3de8940e6867 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/compute_mean.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/compute_mean.py new file mode 100644 index 0000000000000000000000000000000000000000..86bceac51b5a97231cff86ab4344331d67efe35a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/nyu_depth.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/nyu_depth.py new file mode 100644 index 0000000000000000000000000000000000000000..ea5d3e706d9ae50f8395930b7ddd033c1fc120e2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/psnr.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/psnr.py new file mode 100644 index 0000000000000000000000000000000000000000..dc9404b86b4d38077182063bebb8f7a37f5a4c73 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/proj/uvim/save_predictions.py b/Tipsomaly/model/big_vision/evaluators/proj/uvim/save_predictions.py new file mode 100644 index 0000000000000000000000000000000000000000..26bb75cf2a34c09c85e8018a0dd0c9c4de102cdd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/evaluators/save.py b/Tipsomaly/model/big_vision/evaluators/save.py new file mode 100644 index 0000000000000000000000000000000000000000..49bcfc59b9fd9c613611b1edcbd157b2d8c2d6d5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/input_pipeline.py b/Tipsomaly/model/big_vision/input_pipeline.py new file mode 100644 index 0000000000000000000000000000000000000000..afe20894c3ac5fee4e0ef6bd549151b79f4f224c --- /dev/null +++ b/Tipsomaly/model/big_vision/input_pipeline.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. + +"""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 + + if pack: + data = sequence_packing.pack_dataset( + data, + batch_size // jax.process_count() if batch_size else None, + pack.to_dict()) + + # 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, + **input_config.get("tfdata", {})), + 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, **dataset.get("tfdata", {})), + # 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( + input_config.get("thread_pool_size", 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:.2g}%)" + 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["batch_size"] // jax.process_count(), + input_config.pack.to_dict()) + + 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/Tipsomaly/model/big_vision/load_siglip.py b/Tipsomaly/model/big_vision/load_siglip.py new file mode 100644 index 0000000000000000000000000000000000000000..faef9fa828ec23b891186ffb2d6f69b1c06469b7 --- /dev/null +++ b/Tipsomaly/model/big_vision/load_siglip.py @@ -0,0 +1,157 @@ +from big_vision.models.proj.image_text import two_towers as model_mod + +# Remember that the each module like pp should be imported under the same name +# if some other internal files use big_vision.pp and i use .pp then it run through the +# module pp twice +from big_vision.pp import builder as pp_builder +from big_vision.pp import ops_general +from big_vision.pp import ops_image +from big_vision.pp import ops_text +from big_vision.pp.proj.image_text import ops_naflex +from big_vision.pp.proj.paligemma import ops +import PIL + +import jax +import jax.numpy as jnp +import ml_collections +import numpy as np + +# images = [PIL.Image.open(fname) for fname in [ +# 'apple-ipod.jpg', +# 'apple-blank.jpg', +# 'cold_drink.jpg', +# 'hot_drink.jpg', +# 'caffeine.jpg', +# 'siglip.jpg', +# 'authors.jpg', +# 'robosign.jpg', +# 'cow_beach.jpg', +# 'cow_beach2.jpg', +# 'mountain_view.jpg', +# ]] +# pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)') +# imgs = np.array([pp_img({'image': np.array(image)})['image'] for image in images]) +# print('imgs', imgs.shape) + +class InputTransform: + def __init__(self, RES): + self.transform = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)') + + def __call__(self, img): + # print(type(img)) + if np.array(img).size < 3: + raise ValueError("invalid image: fewer than 3 elements") + return np.array(self.transform({'image': np.array(img)})['image']) + +class TargetTransform: + def __init__(self, RES): + self.transform = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(0, 1)') + + def __call__(self, img): + # print(type(img)) + # print(np.expand_dims(np.array(img), axis=-1).shape) + + out = np.array(self.transform({'image': np.expand_dims(np.array(img), axis=-1)})['image']) + # print(out.shape) + return np.squeeze(out, axis=-1) + +def create_preprocessors_siglip2(RES): + # transform = transforms.Compose([ + # Ensure3Channels(), + # transforms.Resize((image_size, image_size)), + # transforms.ToTensor(), + # transforms.Normalize(IMAGE_MEAN, IMAGE_STD), + # ]) + + # target_transform = transforms.Compose([ + # transforms.Resize((image_size, image_size)), + # transforms.ToTensor(), + # ]) + pp_input_img = InputTransform(RES) + pp_target_img = TargetTransform(RES) + return pp_input_img, pp_target_img + +def input_transforms(images, RES): + pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)') + imgs = np.array([pp_img({'image': np.array(image)})['image'] for image in images]) + return imgs + +def target_transforms(images, RES): + pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(0, 1)') + imgs = np.array([pp_img({'image': np.array(image)})['image'] for image in images]) + return imgs + +def load(VARIANT, RES, ROOT_PATH='/kaggle/working/cpt/'): + CKPT = f'siglip2_{VARIANT.lower().replace("/", "")}_{RES}.npz' + TXTVARIANT, PATCH_SIZE = VARIANT.split('/') + EMBDIM = {'B': 768, 'L': 1024, 'So400m': 1152, 'g-opt': 1536}[TXTVARIANT] + # Note: The g-opt vision encoder is paired with a So400m text encoder + TXTVARIANT = 'So400m' if TXTVARIANT == 'g-opt' else TXTVARIANT + PATCH_SIZE = int(PATCH_SIZE) + VOCAB = 256_000 + SEQLEN = 64 + + # It is significantly faster to first copy the checkpoint (30s vs 8m30 for B and 1m vs ??? for L) + # !test -f {ROOT_PATH}/{CKPT} || gsutil cp gs://big_vision/siglip2/{CKPT} {ROOT_PATH} + # print(f'{ROOT_PATH}/{CKPT} ', f'gs://big_vision/siglip2/{CKPT} ') + + model_cfg = ml_collections.ConfigDict(dict( + image_model='vit', + image=dict( + pool_type='map', + scan=True, + variant=VARIANT, + ), + text_model='proj.image_text.text_transformer', + text=dict( + scan=True, + variant=TXTVARIANT, + vocab_size=256_000, + ), + out_dim=[None, EMBDIM], + bias_init=-10, # without this arg, no "b" param is added + )) + model = model_mod.Model(**model_cfg) + + # Using `init_params` is slower but will lead to `load` below performing sanity-checks. + # 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'] + init_params = None # Faster but bypasses loading sanity-checks. + params = model_mod.load(init_params, f'/{ROOT_PATH}/{CKPT}', model_cfg) + + return model, params + + +class SigLIPTokenizer: + def __init__(self, SEQLEN): + self.pp_txt = pp_builder.get_preprocess_fn(f'lower(key="text")|tok(length={SEQLEN}, model="gemma", bos="no", eos="sticky", key="text")') + + def __call__(self, texts): + """texts: str | list[str] -> np.ndarray[int] (B, L)""" + if isinstance(texts, str): + texts = [texts] + return np.array([self.pp_txt({'text': t})['text'] for t in texts]) + + +class SigLIPImageEncoder: + def __init__(self, model, params): + self.model = model + self.params = params + + def __call__(self, imgs): + return self.model.apply({'params': self.params}, imgs, None) + + +class SigLIPTextEncoder: + def __init__(self, model, params): + self.model = model + self.params = params + + def __call__(self, text_ids, learnable_prompts=None, learning_method=None): + return self.model.apply({'params': self.params}, None, text_ids, learnable_prompts=learnable_prompts, learning_method=learning_method) + +def build_siglip_modules(model_version, image_size, SEQLEN=64): + model, params = load(model_version, image_size) + tok = SigLIPTokenizer(SEQLEN) + img_enc = SigLIPImageEncoder(model, params) + txt_enc = SigLIPTextEncoder(model, params) + return img_enc, txt_enc, tok \ No newline at end of file diff --git a/Tipsomaly/model/big_vision/models/__init__.py b/Tipsomaly/model/big_vision/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/models/bit.py b/Tipsomaly/model/big_vision/models/bit.py new file mode 100644 index 0000000000000000000000000000000000000000..cd4235df9ec87549590396deb69739e310e6770d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/bit_paper.py b/Tipsomaly/model/big_vision/models/bit_paper.py new file mode 100644 index 0000000000000000000000000000000000000000..26e5ba83616ce046a78d1a9b3fa32f8b4cbc1000 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/common.py b/Tipsomaly/model/big_vision/models/common.py new file mode 100644 index 0000000000000000000000000000000000000000..175dfa77a1360bc2a0276fa12245c8d357b39406 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/mlp_mixer.py b/Tipsomaly/model/big_vision/models/mlp_mixer.py new file mode 100644 index 0000000000000000000000000000000000000000..58bd4b99d21f061693da007b26dd24013e341851 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/ppp/__init__.py b/Tipsomaly/model/big_vision/models/ppp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/models/ppp/gemma.py b/Tipsomaly/model/big_vision/models/ppp/gemma.py new file mode 100644 index 0000000000000000000000000000000000000000..d17c9f67ec3163957670a2649d37b159616170aa --- /dev/null +++ b/Tipsomaly/model/big_vision/models/ppp/gemma.py @@ -0,0 +1,651 @@ +# 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) + +This implementation does *not* currently support the local sliding attention +pattern used in the v2 models. But since we mostly use sequences <4096 tokens, +this shouldn't make any difference. Since RoPE embedding is used throughout, +it's unclear if there is any practical difference (other than wasting some +memory). +""" + + +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_000, + 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_000, + scan=True, + remat_policy="nothing_saveable", + ) + ) + if variant == "gemma2_2b": + return ml_collections.ConfigDict( + dict( + variant=variant, + width=2304, + depth=26, + mlp_dim=9_216, + num_heads=8, + num_kv_heads=4, + head_dim=256, + norm_eps=1e-6, + vocab_size=256_000, + final_logits_softcap=30.0, + attn_logits_softcap=50.0, + post_norms=True, + scan=True, + remat_policy="nothing_saveable", + ) + ) + if variant == "gemma2_9b": + return ml_collections.ConfigDict( + dict( + variant=variant, + width=3584, + depth=42, + mlp_dim=14_336, + num_heads=16, + num_kv_heads=8, + head_dim=256, + norm_eps=1e-6, + vocab_size=256_000, + final_logits_softcap=30.0, + attn_logits_softcap=50.0, + post_norms=True, + scan=True, + remat_policy="nothing_saveable", + ) + ) + if variant == "gemma2_27b": + return ml_collections.ConfigDict( + dict( + variant=variant, + width=4608, + depth=46, + mlp_dim=36_864, + num_heads=32, + num_kv_heads=16, + head_dim=128, + norm_eps=1e-6, + vocab_size=256_000, + query_pre_attn_norm="rsqrt_emb_per_head", + final_logits_softcap=30.0, + attn_logits_softcap=50.0, + post_norms=True, + 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 + + query_pre_attn_norm: str + attn_logits_softcap: float | None + + 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 / GQA + 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) + if self.query_pre_attn_norm == "rsqrt_head_dim": + q *= self.head_dim**-0.5 + elif self.query_pre_attn_norm == "rsqrt_emb_per_head": + q *= (self.features // self.num_heads)**-0.5 + else: + raise ValueError( + f"Unknown query_pre_attn_norm: {self.query_pre_attn_norm}" + ) + + 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 self.attn_logits_softcap: + logits = jnp.tanh(logits / self.attn_logits_softcap) + logits = logits * self.attn_logits_softcap + + 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 + + query_pre_attn_norm: str + attn_logits_softcap: float | None + post_norms: bool + + 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, + query_pre_attn_norm=self.query_pre_attn_norm, + attn_logits_softcap=self.attn_logits_softcap, + ) + 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 + if self.post_norms: + self.post_attention_norm = RMSNorm() + self.post_ffw_norm = RMSNorm() + + 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) + if self.post_norms: + attn_output = self.post_attention_norm(attn_output) + 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) + if self.post_norms: + outputs = self.post_ffw_norm(outputs) + 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 + + query_pre_attn_norm: str = "rsqrt_head_dim" + final_logits_softcap: float = 0.0 + attn_logits_softcap: float = 0.0 + post_norms: bool = False + + 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, + query_pre_attn_norm=self.query_pre_attn_norm, + attn_logits_softcap=self.attn_logits_softcap, + post_norms=self.post_norms, + ) + layers = self.scope.push("layers") # pytype: disable=attribute-error + 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_pre_norm"] = x + if self.final_logits_softcap: + x = jnp.tanh(x / self.final_logits_softcap) * self.final_logits_softcap + 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"] + if emb.shape[0] == 256_128: + params["embedder"]["input_embedding"] = jax.device_get(emb)[:256_000] + assert params["embedder"]["input_embedding"].shape[0] == 256_000 + + +def _maybe_transpose_gating_einsum(params): + """The `transpose_gating_einsum` case in gemma/modules.py.""" + mlp = params["layers"]["mlp"] + *_, d1, d2 = mlp["gating_einsum"].shape + if d1 > d2: + *ns, n1, n2 = range(len(mlp["gating_einsum"].shape)) + mlp["gating_einsum"] = mlp["gating_einsum"].transpose(*ns, n2, n1) + + +def _load_like_bv(params): + params = jax.tree.map(lambda x: x, params) + _del_pad_rows(params) + _maybe_transpose_gating_einsum(params) + 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 = _load_like_bv(_load_orbax(_ORBAX_INITS[init_variant])) + elif init_variant in _BV_INITS: + params = _load_like_bv(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/Tipsomaly/model/big_vision/models/proj/__init__.py b/Tipsomaly/model/big_vision/models/proj/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/models/proj/cappa/cappa.py b/Tipsomaly/model/big_vision/models/proj/cappa/cappa.py new file mode 100644 index 0000000000000000000000000000000000000000..8c20b1b78f06d3eece7f2e5e8707eef995466529 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/clippo/one_tower.py b/Tipsomaly/model/big_vision/models/proj/clippo/one_tower.py new file mode 100644 index 0000000000000000000000000000000000000000..c7ce5abd03174b91328e74302d1f15dadd98e0dc --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/flaxformer/bert.py b/Tipsomaly/model/big_vision/models/proj/flaxformer/bert.py new file mode 100644 index 0000000000000000000000000000000000000000..4bad30be3dbdb736586fa04109ff756b3b0975c5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/flaxformer/bert_test.py b/Tipsomaly/model/big_vision/models/proj/flaxformer/bert_test.py new file mode 100644 index 0000000000000000000000000000000000000000..90405c2ec70a45b3a8ae90aa2dd39aaea2fee240 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/flaxformer/bert_test_util.py b/Tipsomaly/model/big_vision/models/proj/flaxformer/bert_test_util.py new file mode 100644 index 0000000000000000000000000000000000000000..11a8a0105dec13490e8438d4162040b2fa4a06fe --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/flexi/vit.py b/Tipsomaly/model/big_vision/models/proj/flexi/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..3a0af0e95045016fa6adb0dd7f49eee70d4005bf --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/flexi/vit_test.py b/Tipsomaly/model/big_vision/models/proj/flexi/vit_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f7dd588e3a4231ece5ac0a92771fddcac803b66d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/adaptor.py b/Tipsomaly/model/big_vision/models/proj/givt/adaptor.py new file mode 100644 index 0000000000000000000000000000000000000000..6b76c7704beee10a5d57f822577f490cb1ad1d59 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/adaptor_test.py b/Tipsomaly/model/big_vision/models/proj/givt/adaptor_test.py new file mode 100644 index 0000000000000000000000000000000000000000..e197d1d2ba6c5e3247bb14cd533e2b916f373779 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/cnn.py b/Tipsomaly/model/big_vision/models/proj/givt/cnn.py new file mode 100644 index 0000000000000000000000000000000000000000..27896b3d365c75aa7ec62edd0f872a98ad6afe3f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/decode.py b/Tipsomaly/model/big_vision/models/proj/givt/decode.py new file mode 100644 index 0000000000000000000000000000000000000000..14f685185ff51fa0e47e517c538c68ebc82df8a3 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/decode_test.py b/Tipsomaly/model/big_vision/models/proj/givt/decode_test.py new file mode 100644 index 0000000000000000000000000000000000000000..53b41c09c9bc6a842ac4d565ae5de9a05562819d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/givt.py b/Tipsomaly/model/big_vision/models/proj/givt/givt.py new file mode 100644 index 0000000000000000000000000000000000000000..0bd4c84427fde5873c33dd7e76e6a289e163cb22 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/givt_test.py b/Tipsomaly/model/big_vision/models/proj/givt/givt_test.py new file mode 100644 index 0000000000000000000000000000000000000000..acfc59e73448efffbb34bb73728ca3a52cbd02fb --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/parallel_decode.py b/Tipsomaly/model/big_vision/models/proj/givt/parallel_decode.py new file mode 100644 index 0000000000000000000000000000000000000000..b05903a7e30a870ba5b09b0965932321090f1190 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/parallel_decode_test.py b/Tipsomaly/model/big_vision/models/proj/givt/parallel_decode_test.py new file mode 100644 index 0000000000000000000000000000000000000000..c03effc5f944c99db7ec3e392abe255bb8583eb9 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/vae.py b/Tipsomaly/model/big_vision/models/proj/givt/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..db1ab66721e0fc6131f2f98a2cbbddad9d66007c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/givt/vit.py b/Tipsomaly/model/big_vision/models/proj/givt/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..7ed36ef069db09868a8b3be97ce6cb607b7e4053 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/image_text/__init__.py b/Tipsomaly/model/big_vision/models/proj/image_text/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/models/proj/image_text/naflex_vit.py b/Tipsomaly/model/big_vision/models/proj/image_text/naflex_vit.py new file mode 100644 index 0000000000000000000000000000000000000000..5323c8a8ae45b04b9d7599876e8532181493ae2f --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/image_text/naflex_vit.py @@ -0,0 +1,300 @@ +# 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. + +"""NaFlex ViT = NaViT + FlexiViT. + +Based on: +* FlexiViT: https://arxiv.org/abs/2212.08013 +* NaViT: https://arxiv.org/abs/2307.06304 +""" + +import re +from big_vision.models import vit +import big_vision.models.proj.image_text.utils as it_utils +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np + + +def _decode_posemb(posemb): + if (m := re.fullmatch(r"learn_2d(\(\d+\))", posemb)): + grid_size = int(m.groups()[0][1:-1]) + return "learn_2d", grid_size + return posemb, None + + +def _pos_emb_resize(pos_emb, shapes, coords, l): + """Resizes the positional embeddings to match the input image size. + + Args: + pos_emb: Positional embeddings. + shapes: Image shapes (usually `coords.max(axis=1) + 1`). + coords: Patch coordinates. + l: Maximum number of patches per side. Necesary in order to have a static + return shape. + + Setting l to 64 is a heuristic. Ideally, we would use + `l = tokens.shape[1]` here, but that requires too much memory, + especially for high-resolution inputs. Using a lower value + effectively limits the maximum resolution to `l x patch_size`. + Resolutions above that will lead to NaNs in the positional + embeddings and NaN model outputs. + Note: this value can be adjusted post-hoc without retraining. + + Returns: + Postional embeddings for every patch. + """ + + def resize_fn(shape, coords): + emb = jax.image.scale_and_translate( + pos_emb, + shape=(l, l, pos_emb.shape[-1]), + spatial_dims=(0, 1), + scale=shape / jnp.asarray(pos_emb.shape[:2]), + translation=jnp.asarray([0, 0]), + method="bilinear", antialias=True) + gather_dim = jax.lax.GatherDimensionNumbers( + offset_dims=(1,), + collapsed_slice_dims=(0, 1), + start_index_map=(0, 1, 2) + ) + return jax.lax.gather( + emb, + jnp.pad(coords, [[0, 0], [0, 1]]), + gather_dim, + [1, 1, emb.shape[-1]], + mode="fill") + return it_utils.batch_shmap( + jax.vmap(resize_fn, in_axes=(0, 0), out_axes=0), + shapes, coords) + + +class Encoder1DBlock(nn.Module): + """Single transformer encoder block (MHSA + MLP).""" + mlp_dim: int | None = None # Defaults to 4x input dim + num_heads: int = 12 + dropout: float = 0.0 + dtype_mm: str = "float32" + + @nn.compact + def __call__(self, x, mask=None, deterministic=True): + if mask is not None: + mask = mask[..., None, :, :] # Broadcast mask along the head dim. + + 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, mask=mask) + 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"] = vit.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: int | None = 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, mask=None, deterministic=True): + out = {} + + if self.scan: + block = nn.remat( + Encoder1DBlock, + prevent_cse=False, + static_argnums=(3,), # 0=self, 3=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, mask, 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, mask, 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: int | None = None # Defaults to 4x input dim + num_heads: int = 12 + + @nn.compact + def __call__(self, x, mask=None): + 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]) + + if mask is not None: + mask = mask[..., None, None, :] # Add query and head dims. + + x = nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, + kernel_init=nn.initializers.xavier_uniform())(probe, x, mask=mask) + + y = nn.LayerNorm()(x) + x = x + vit.MlpBlock(mlp_dim=self.mlp_dim)(y) + return x[:, 0] + + +class _Model(nn.Module): + """ViT model.""" + + num_classes: int | None = None + width: int = 768 + depth: int = 12 + mlp_dim: int | None = None # Defaults to 4x input dim + num_heads: int = 12 + rep_size: int | bool = False + 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" + + posemb: str = "learn_2d(64)" + nposemb: int | None = None # Needs to be overwritten + + patchln_pre: bool = False + patchln_post: bool = False + + @nn.compact + def __call__(self, image, *, train=False): + out = {} + + patches, ptype, yabs, xabs = image + patches = jnp.asarray(patches, self.dtype_mm) # BN(hw3) of float32 + + if self.patchln_pre: + patches = nn.LayerNorm(name="patchln_pre")(patches) + + # Embed the patches. + tokens = out["stem"] = nn.Dense( + self.width, name="embedding", dtype=self.dtype_mm)(patches) + + if self.patchln_post: + tokens = nn.LayerNorm(name="patchln_post")(tokens) + + x = tokens + posemb, posemb_grid_size = _decode_posemb(self.posemb) + if posemb == "learn_2d": + posembs = self.param( + "pos_embedding", + nn.initializers.normal(stddev=1/np.sqrt(self.width)), + (self.nposemb, self.nposemb, self.width), self.dtype_mm) + coords = jnp.stack([yabs, xabs], axis=-1) + shapes = coords.max(axis=1) + 1 + # See comment in `_pos_emb_resize` for details. + x += _pos_emb_resize(posembs, shapes, coords, posemb_grid_size or 64) + else: + raise ValueError(f"Unknown posemb: '{self.posemb}'") + + out["with_posemb"] = x + + # Only use patch tokens in self-attention: + sa_mask = ptype == 1 # 1 == patch (pad is 0). + sa_mask = jnp.logical_and(sa_mask[..., :, None], sa_mask[..., None, :]) + x, out["encoder"] = Encoder( + depth=self.depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + scan=self.scan, + remat_policy=self.remat_policy, + dtype_mm=self.dtype_mm, + name="Transformer")( + x, mask=sa_mask, deterministic=not train) + out["encoded"] = x + + # Ignore the padding tokens when pooling: + pool_mask = (ptype == 1) # 1 == patch (not pad) + if self.pool_type == "map": + maphead = MAPHead(num_heads=self.num_heads, mlp_dim=self.mlp_dim) + x = maphead(x, mask=pool_mask) + elif self.pool_type == "gap": + pool_mask = pool_mask[..., None] + x = jnp.sum(x * pool_mask, axis=1) / jnp.sum(pool_mask, axis=1) + elif self.pool_type == "max": + # Tested in (internal link) + pool_mask = pool_mask[..., None] + ignore = jnp.where(pool_mask, 0, jnp.finfo(x.dtype).min) + x = jnp.max(pool_mask * x + ignore, axis=1) + elif self.pool_type == "none": + pass + else: + raise ValueError(f"Unknown pool type: '{self.pool_type}'") + out["head_input"] = x + + 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") + x = nn.tanh(hid(x)) + + 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 = 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, **{**vit.decode_variant(variant), **kw}) + +load = vit.load diff --git a/Tipsomaly/model/big_vision/models/proj/image_text/text_transformer.py b/Tipsomaly/model/big_vision/models/proj/image_text/text_transformer.py new file mode 100644 index 0000000000000000000000000000000000000000..f43210aafae3091cbf9cfdc1dc12b45eb5bc62bc --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/image_text/text_transformer.py @@ -0,0 +1,238 @@ +# # 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) + + +from typing import Any, Optional +import jax.numpy as jnp + +ConfigDict = Any + + +class _Model(nn.Module): + """Text transformer similar to CLIP, with optional prompt learning.""" + # (all fields unchanged) + 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: bool = False, + # NEW: optional prompt-learning inputs (all default to None -> no-op) + learnable_prompts: Optional[jnp.ndarray] = None, # [P, D] + learning_method: Optional[str] = None, # {"concat","sumate","entire_learnable", None} + deep_parameters: Optional[list] = None # optional, see NOTE (per-layer deep prompts) + ): + """Args: + text: [N, L] token ids (EOS-padded; EOS id must be 1 as in original code). + learnable_prompts: prompt tokens to apply; shape [P, D] where D == width. + learning_method: 'concat' | 'sumate' | 'entire_learnable' | None. + deep_parameters: placeholder for per-layer prompt tokens (see NOTE). + """ + del deep_parameters # NOTE: see "Deep prompts" section below. + + out = {} + + embedding = nn.Embed(num_embeddings=self.vocab_size, features=self.width) + x = out["embedded"] = embedding(text) # [N, L, D] + n, l, d = x.shape + + # ------------------------------- + # PROMPT LEARNING (optional) + # ------------------------------- + if learnable_prompts is not None and learning_method: + # Validate dimensionality early. + p, d_p = learnable_prompts.shape + if d_p != d: + raise ValueError(f"learnable_prompts has dim {d_p}, expected {d}.") + + # Broadcast prompts across batch: [N, P, D] + prompts_b = jnp.broadcast_to(learnable_prompts[None, ...], (n, p, d)) + + if learning_method == "concat": + # Prepend prompts so EOS (last token) stays last. + x = jnp.concatenate([prompts_b, x], axis=1) # [N, P+L, D] + + elif learning_method == "sumate": + # Add prompts element-wise to the first P tokens. + if p > l: + raise ValueError(f"sumate needs P <= L, got P={p}, L={l}.") + x = x.at[:, :p, :].add(prompts_b) + + elif learning_method == "entire_learnable": + # Replace by prompts, but append an EOS token so 'last' pooling remains valid. + eos_ids = jnp.ones((n, 1), dtype=text.dtype) # EOS id is 1 in this codebase + eos_embed = embedding(eos_ids) # [N, 1, D] + x = jnp.concatenate([prompts_b, eos_embed], axis=1) # [N, P+1, D] + else: + raise ValueError(f"Unknown learning_method: {learning_method}") + + # Update sequence length after prompt ops. + _, l, _ = x.shape + + # ------------------------------- + # Positional embeddings (unchanged logic, but after prompt ops) + # ------------------------------- + x = x + self.param( + "pos_embedding", + nn.initializers.normal(stddev=1/np.sqrt(d)), + (1, l, d), + x.dtype, + ) + + # Encoder (kept identical; see NOTE to enable deep prompts) + 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 (unchanged). + out["vocab_logits"] = embedding.attend(x) + + # Pooling (unchanged semantics). + if self.pool_type == "last": + # With 'concat', EOS is still last. With 'entire_learnable', we explicitly appended 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 \ No newline at end of file diff --git a/Tipsomaly/model/big_vision/models/proj/image_text/two_towers.py b/Tipsomaly/model/big_vision/models/proj/image_text/two_towers.py new file mode 100644 index 0000000000000000000000000000000000000000..75c141335eb890930c281a7e9ed9bd34216bc2cd --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/image_text/two_towers.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. + +"""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, learnable_prompts=None, learning_method=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, learnable_prompts=learnable_prompts, learning_method=learning_method, **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) + + out["img/2d_norm"] = jnp.linalg.norm(out_img['encoded'], axis=2, keepdims=True) + out["img/2d_normalized"] = out_img['encoded'] / (out["img/2d_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", + + # SigLIP 2 image and text encoder checkpoints from https://arxiv.org/abs/2502.14786 + "SigLIP2 B/16 224": "gs://big_vision/siglip2/siglip2_b16_224.npz", + "SigLIP2 B/16 256": "gs://big_vision/siglip2/siglip2_b16_256.npz", + "SigLIP2 B/16 384": "gs://big_vision/siglip2/siglip2_b16_384.npz", + "SigLIP2 B/16 512": "gs://big_vision/siglip2/siglip2_b16_512.npz", + "SigLIP2 B/32 256": "gs://big_vision/siglip2/siglip2_b32_256.npz", + "SigLIP2 L/16 256": "gs://big_vision/siglip2/siglip2_l16_256.npz", + "SigLIP2 L/16 384": "gs://big_vision/siglip2/siglip2_l16_384.npz", + "SigLIP2 L/16 512": "gs://big_vision/siglip2/siglip2_l16_512.npz", + "SigLIP2 So400m/14 224": "gs://big_vision/siglip2/siglip2_so400m14_224.npz", + "SigLIP2 So400m/14 384": "gs://big_vision/siglip2/siglip2_so400m14_384.npz", + "SigLIP2 So400m/16 256": "gs://big_vision/siglip2/siglip2_so400m16_256.npz", + "SigLIP2 So400m/16 384": "gs://big_vision/siglip2/siglip2_so400m16_384.npz", + "SigLIP2 So400m/16 512": "gs://big_vision/siglip2/siglip2_so400m16_512.npz", + "SigLIP2 g-opt/16 256": "gs://big_vision/siglip2/siglip2_g-opt16_256.npz", + "SigLIP2 g-opt/16 384": "gs://big_vision/siglip2/siglip2_g-opt16_384.npz", + # SigLIP 2 NaFlex image and text encoder checkpoints. + # These need `image_model="proj.image_text.naflex_vit"` for the image encoder + # and a non-standard preprocessing, see configs/proj/image_text/README_siglip2.md. + "SigLIP2 B/16 NaFlex": "gs://big_vision/siglip2/siglip2_b16_naflex.npz", + "SigLIP2 So400m/16 NaFlex": "gs://big_vision/siglip2/siglip2_so400m16_naflex.npz", + # pylint: enable=line-too-long +} diff --git a/Tipsomaly/model/big_vision/models/proj/image_text/utils.py b/Tipsomaly/model/big_vision/models/proj/image_text/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..1b9924be050fa576bca43c5b005c312328892f18 --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/image_text/utils.py @@ -0,0 +1,42 @@ +# 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. + +"""Utility functions.""" + +import jax +from jax.experimental import shard_map +from jax.interpreters import pxla + + +P = jax.sharding.PartitionSpec + + +def batch_shmap(fn, *args, **kwargs): + """Shard map to map along the data dimension w/o triggering communication.""" + + mesh = pxla.thread_resources.env.physical_mesh + if not mesh.empty: + devices_flat = mesh.devices.flatten() + mesh_flat = jax.sharding.Mesh(devices_flat, ("data",)) + fn = shard_map.shard_map( + fn, + mesh=mesh_flat, + in_specs=P("data"), out_specs=P("data"), check_rep=True) + return fn(*args, **kwargs) + + +def subsample_batch(x, subsample: int): + """Shard map to subsample the data dimension w/o triggering communication.""" + fn = lambda x: jax.tree.map(lambda xx: xx[::subsample], x) + return batch_shmap(fn, x) if subsample > 1 else x diff --git a/Tipsomaly/model/big_vision/models/proj/jet/jet.py b/Tipsomaly/model/big_vision/models/proj/jet/jet.py new file mode 100644 index 0000000000000000000000000000000000000000..8fd60fea4470b003d579f52ca54bab321e72f455 --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/jet/jet.py @@ -0,0 +1,334 @@ +# 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. + +"""Jet: A Modern Transformer-Based Normalizing Flow. + +https://arxiv.org/abs/2412.15129 +""" + +import itertools +from typing import Any, Sequence + +from big_vision import utils +from big_vision.models import common +from big_vision.models import vit +import einops +import flax.core +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np + + +class DNN(nn.Module): + """Main non-invertible compute block with a ViT used in coupling layers.""" + + depth: int = 1 + emb_dim: int = 256 + num_heads: int = 4 + + @nn.compact + def __call__(self, x, context=None): + out_dim = x.shape[-1] # pytype: disable=attribute-error + x = nn.Dense(self.emb_dim, name="init_proj")(x) + posemb = self.param("posemb", + nn.initializers.normal(stddev=1/np.sqrt(self.emb_dim)), + (1,) + x.shape[1:], jnp.float32) + x = x + posemb + + if context is not None: + y = nn.MultiHeadDotProductAttention( + num_heads=self.num_heads, + qkv_features=self.emb_dim, + out_kernel_init=nn.initializers.zeros, + out_features=x.shape[-1])(x, context) + x = x + y + + x, _ = vit.Encoder(depth=self.depth, num_heads=self.num_heads, + name="vit")(x) + bias, scale = jnp.split( + nn.Dense(2 * out_dim, + kernel_init=nn.initializers.zeros, + name="final_proj")(x), + 2, axis=-1) + return bias, scale + + +class Coupling(nn.Module): + """Coupling layer. + + Supports two kinds of couplings: channels-wise and spatial. + + The implementation is a bit convoluted, but it is necessary to support + scan over layers. The coupling needs to be modulated by the input arguments + and can not be modulated by the static attributes (because scan loops over + a fixed Module instance). + + To make things worse, we want to support both channels-wise and spatial + couplings, which need differently-shaped projections applied along different + axes. Also they will have different output shapes. To work around this, we + perform both projections, select the right one with jax.lax.cond, and then + also do depth-to-space for spatial couplings to ensure consistent shapes. + """ + + depth: int = 1 + emb_dim: int = 256 + num_heads: int = 4 + scale_factor: float = 2.0 + + @nn.compact + def compact_setup(self, x, kind, channel_proj, spatial_proj, context=None): + + dnn = DNN(depth=self.depth, emb_dim=self.emb_dim, + num_heads=self.num_heads, name="dnn") + + # Channel-wise split and merge utils. + def split_channels(x): + x = jnp.einsum("ntk,km->ntm", x, channel_proj, precision="highest") + x1, x2 = jnp.split(x, 2, axis=-1) + return x1, x2 + def merge_channels(x1, x2): + x = jnp.concatenate([x1, x2], axis=-1) + x = jnp.einsum("ntk,km->ntm", x, channel_proj.T, precision="highest") + return x + + # Spatial split and merge utils. Note that we do extra reshape with the + # the `cut` function to ensure that channel-wise and spatial-wise + # projections result in the same output shape. + def split_spatial(x): + x = jnp.einsum("ntk,tm->nmk", x, spatial_proj, precision="highest") + x1, x2 = jnp.split(x, 2, axis=-2) + cut = lambda a: einops.rearrange(a, "... n (s c) -> ... (n s) c", s=2) + x1, x2 = jax.tree.map(cut, (x1, x2)) + return x1, x2 + def merge_spatial(x1, x2): + uncut = lambda a: einops.rearrange(a, "... (n s) c -> ... n (s c)", s=2) + x1, x2 = jax.tree.map(uncut, (x1, x2)) + x = jnp.concatenate([x1, x2], axis=-2) + x = jnp.einsum("ntk,tm->nmk", x, spatial_proj.T, precision="highest") + return x + + # Dynamically select the right splitting + x1, x2 = jax.lax.cond(kind, split_channels, split_spatial, x) + + # The same for forward/inverse, so compute inside the setup. + bias, raw_scale = dnn(x1, context) + scale = jax.nn.sigmoid(raw_scale) * self.scale_factor + logdet = jax.nn.log_sigmoid(raw_scale) + jnp.log(self.scale_factor) + logdet = jnp.sum(logdet, axis=range(1, logdet.ndim)) + + return x1, x2, bias, scale, merge_channels, merge_spatial, logdet + + def forward(self, x, kind, channel_proj, spatial_proj, context=None): + """Implements spatial (kind=0) or channel (kind=1) coupling.""" + x1, x2, bias, scale, merge_channels, merge_spatial, logdet = ( + self.compact_setup(x, kind, channel_proj, spatial_proj, context)) + x2 = (x2 + bias) * scale + x = jax.lax.cond(kind, merge_channels, merge_spatial, x1, x2) + return x, logdet + + def inverse(self, x, kind, channel_proj, spatial_proj, context=None): + """Implements spatial (kind=0) or channel (kind=1) coupling.""" + x1, x2, bias, scale, merge_channels, merge_spatial, logdet = ( + self.compact_setup(x, kind, channel_proj, spatial_proj, context)) + x2 = (x2 / scale) - bias + x = jax.lax.cond(kind, merge_channels, merge_spatial, x1, x2) + return x, -logdet + + +class Model(nn.Module): + """Jet: a normalizing flow model parameterized by ViT blocks.""" + + depth: int = 2 + block_depth: int = 1 + emb_dim: int = 256 + num_heads: int = 4 + scale_factor: float = 2.0 + ps: int = 4 + channels_coupling_projs: Sequence[str] = ("random",) + spatial_coupling_projs: Sequence[str] = ("checkerboard", "checkerboard-inv") + kinds: Sequence[str] = ("channels", "channels", "spatial") + + @nn.compact + def compact_setup(self, x): + + # `kinds` variable defines the sequence of channel and spatial couplings. + # After that, we draw specific coupling projection types. + def _interleave_couplings(): + kinds = itertools.cycle(self.kinds) + cc = itertools.cycle(self.channels_coupling_projs) + sc = itertools.cycle(self.spatial_coupling_projs) + + # Zero coupling is a placeholder and will never be executed. If it happens + # due to a bug, the objective will explode. + # Yields `(coupling_type, channel_proj type and spatial_proj type)`. + while True: + k = next(kinds) + if k == "channels": + yield 1, next(cc), "zero" + elif k == "spatial": + yield 0, "zero", next(sc) + else: + raise ValueError(f"Unknown coupling kind: {k}") + + kinds, c_proj_kinds, s_proj_kinds = ( + list(zip(*itertools.islice(_interleave_couplings(), self.depth)))) + kinds = jnp.array(kinds) + + # Initialize channel-wise coupling projections. + channels_init_fn = get_channels_coupling_init( + self.depth, x.shape[1:], self.ps, proj_kinds=c_proj_kinds) + c_proj = self.param("channel_coupling_masks-FREEZE_ME", channels_init_fn, + jnp.float32) + + # Initialize spatial-wise coupling projections. + spatial_init_fn = get_spatial_coupling_init( + self.depth, x.shape[1:], self.ps, proj_kinds=s_proj_kinds) + s_proj = self.param("spatial_coupling_masks-FREEZE_ME", spatial_init_fn, + jnp.float32) + + remat_coupling = nn.remat( + Coupling, + prevent_cse=False, + policy=jax.checkpoint_policies.nothing_saveable, + methods=("forward", "inverse",)) + block = remat_coupling( + name="couplings", + depth=self.block_depth, + emb_dim=self.emb_dim, + num_heads=self.num_heads, + scale_factor=self.scale_factor, + ) + + def body_fn_forward(m, carry, kind, c_proj, s_proj, context): + carry, y = m.forward(carry, kind, c_proj, s_proj, context) + return carry, y + + def body_fn_inverse(m, carry, kind, c_proj, s_proj, context): + carry, y = m.inverse(carry, kind, c_proj, s_proj, context) + return carry, y + + scan_kwargs = dict( + variable_axes={"params": 0}, + in_axes=(0, 0, 0, nn.broadcast), + split_rngs={"params": True}, + length=self.depth) + m_forward = nn.scan(body_fn_forward, **scan_kwargs) + m_inverse = nn.scan(body_fn_inverse, **scan_kwargs, reverse=True) + + return block, m_forward, m_inverse, kinds, c_proj, s_proj + + def forward(self, x, context=None): + + block, m_forward, _, kinds, c_projs, s_projs = self.compact_setup(x) + + x = einops.rearrange(x, "b (h hp) (w wp) c -> b (h w) (hp wp c)", + hp=self.ps, wp=self.ps) + + x, logdet = m_forward(block, x, kinds, c_projs, s_projs, context) + logdet = jnp.sum(logdet, axis=0) + + x = einops.rearrange(x, "b (h w) (hp wp c) -> b (h hp) (w wp) c", + hp=self.ps, wp=self.ps, + h=np.round(x.shape[1] ** 0.5).astype(np.int32)) + + return x, logdet + + def inverse(self, x, context=None): + + block, _, m_inverse, kinds, c_projs, s_projs = self.compact_setup(x) + + x = einops.rearrange(x, "b (h hp) (w wp) c -> b (h w) (hp wp c)", + hp=self.ps, wp=self.ps) + + x, logdet = m_inverse(block, x, kinds, c_projs, s_projs, context) + logdet = jnp.sum(logdet, axis=0) + + x = einops.rearrange(x, "b (h w) (hp wp c) -> b (h hp) (w wp) c", + hp=self.ps, wp=self.ps, + h=np.round(x.shape[1] ** 0.5).astype(np.int32)) + + return x, logdet + + +def get_channels_coupling_init(depth, image_shape, ps, proj_kinds): + """Channel-wise coupling projection init.""" + assert image_shape[-3] % ps == 0, f"Shape and patch size: {image_shape}, {ps}" + assert image_shape[-2] % ps == 0, f"Shape and patch size: {image_shape}, {ps}" + c = image_shape[-1] * ps * ps # number of dims + + def _init(k, dtype): + w = jnp.zeros((depth, c, c), dtype=dtype) + for i, kind in enumerate(proj_kinds): + if kind == "random": + p = jax.random.permutation(jax.random.fold_in(k, i), c) + w = w.at[jnp.ones_like(p) * i, p, jnp.arange(c)].set(1.0) + elif kind == "zero": # placeholder all-zero proj + pass + else: + raise ValueError(f"Unknown coupling kind: {kind}") + return w + + return _init + + +def get_spatial_coupling_init(depth, image_shape, ps, proj_kinds): + """Spatial coupling projection init.""" + assert image_shape[-3] % ps == 0, f"Shape and patch size: {image_shape}, {ps}" + assert image_shape[-2] % ps == 0, f"Shape and patch size: {image_shape}, {ps}" + nh = image_shape[-3] // ps + nw = image_shape[-2] // ps + n = nh * nw # number of tokens in transformer + + def _init(k, dtype): + del k + w = jnp.zeros((depth, n, n), dtype=dtype) + for i, kind in enumerate(proj_kinds): + if kind.startswith("vstripes"): + idx1 = jnp.arange(n)[::2] + idx2 = jnp.arange(1, n)[::2] + elif kind.startswith("hstripes"): + idx1 = jnp.where((jnp.arange(n) // nw) % 2 == 0, size=n//2)[0] + idx2 = jnp.where((jnp.arange(n) // nw) % 2 == 1, size=n//2)[0] + elif kind.startswith("checkerboard"): + vals = jnp.arange(n).reshape([nh, nw]) + jnp.arange(nh).reshape([nh, 1]) + idx1 = jnp.where((vals.flatten() % 2) == 0, size=n//2)[0] + idx2 = jnp.where((vals.flatten() % 2) == 1, size=n//2)[0] + elif kind == "zero": # placeholder all-zero proj + continue + else: + raise ValueError(f"Unknown coupling kind: {kind}") + + idx1, idx2 = (idx2, idx1) if kind.endswith("-inv") else (idx1, idx2) + w = w.at[i, idx1, jnp.arange(n//2)].set(1) + w = w.at[i, idx2, jnp.arange(n//2, n)].set(1) + return w + + return _init + + +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/Tipsomaly/model/big_vision/models/proj/jetformer/jetformer.py b/Tipsomaly/model/big_vision/models/proj/jetformer/jetformer.py new file mode 100644 index 0000000000000000000000000000000000000000..9507eba4fac93c23a394d4a8ca92c5609b68a64c --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/jetformer/jetformer.py @@ -0,0 +1,656 @@ +# 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. + +"""JetFormer transformer backbone 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) +""" + +from typing import Optional, Sequence, Any, Mapping + +from big_vision import utils +from big_vision.models import common +from big_vision.models.ppp import gemma +import distrax +import flax.linen as nn +import jax +import jax.numpy as jnp + + +@jax.vmap +def right_align(x, attn_mask, input_mask): + """Converts input with masked tokens to be right-aligned.""" + # Note: this right align supports padded tokens anywhere. In particular it + # supports prefilling with variable text prompt padded to tex_len and also + # prefill the first image token (e.g. BOI). + + # Due to vmap, this is operating in a single example (not batch level). + assert x.ndim == 2 and attn_mask.ndim == 2 and input_mask.ndim == 1 + assert x.shape[0] == input_mask.shape[0] == attn_mask.shape[0] + assert attn_mask.shape[0] == attn_mask.shape[1] + if x.shape[0] == 0: + return x, attn_mask, input_mask + + # Compute the final position of each token in the sequence. + # Tokens with mask==False get assigned position "-1" which ends up being + # an empty one_hot vector and therefore not used in the permuted version. + m_cumsum = jnp.cumsum(input_mask) + seqlen = m_cumsum[-1] + x_pos = (x.shape[0] - seqlen + m_cumsum) * input_mask - 1 + + # Permute x and input mask using desired x_pos of each token. + x_permut = jax.nn.one_hot(x_pos, x.shape[0], dtype=jnp.bool) + x = jnp.einsum("n...,nm->m...", x, x_permut).astype(x.dtype) + + # Attention mask has shape [A, B] indicating token A can attent to token B. + # We need to permute both A and B to reflect the new token positions. + attn_mask = jnp.einsum("nb,nm->mb", attn_mask, x_permut) + attn_mask = jnp.einsum("bn,nm->bm", attn_mask, x_permut) + + # Special case: input_mask will be right-aligned. + # input_mask = jnp.einsum( + # "n...,nm->m...", input_mask, x_permut).astype(input_mask.dtype) + input_mask = jnp.arange(x.shape[0]) >= (x.shape[0] - seqlen) + return x, attn_mask, input_mask + + +class GemmaBlock(gemma.Block): + """Gemma Transformer block variant which collects intermediate outputs.""" + + def __call__(self, x, 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 + scan_arg["outputs"] = outputs + return outputs, scan_arg + + +class GemmaBackbone(nn.Module): + """Gemma backbone (only decoder: no embedding and no logits).""" + + width: int + depth: int + mlp_dim: int + num_heads: int + num_kv_heads: int + head_dim: int + out_dim: int + norm_eps: float + + 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" + head_dtype: str = "float32" + + scan: bool = False + remat_policy: str = "none" + + @nn.compact + def __call__( + self, x, *, + mask, + positions=None, decode=False, deterministic=True, + ): + """Forward pass through the transformer backbone. + + Args: + x: [B, T, E] with already embedded tokens. + mask: Attention mask `[B, T, S]`, where its true if token T can attend to + token S. + positions: Optional `[B, T]` allows to specify the absolute position of + the tokens. + decode: Whether to use kv-cache. Caller must pass masks and positions. + deterministic: Forwarded to all dropout layers. + + Returns: + `(x, out)` where `x`is the output of the transformer backbone. + """ + out = {} + + x = x.astype(self.embed_dtype) + batch_size, seq_len, width = x.shape + + 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.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 = GemmaBlock + else: + block_cls = nn.remat( + GemmaBlock, + 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, + # Gemma v1 settings: + query_pre_attn_norm="rsqrt_head_dim", + attn_logits_softcap=None, + post_norms=False, + ) + 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) + ] + scan_arg = {} + for i, block in enumerate(blocks): + x, scan_arg = block( + x, scan_arg, positions, mask, decode, deterministic) + out[f"block{i:02}_rep"] = x + + if self.scan: + # When using scan, out only contains the final block output, so we need to + # add the intermediate outputs from the scan_arg. + for i, block_out in enumerate(scan_arg["outputs"]): + out[f"block{i:02}_rep"] = block_out + + assert x.dtype == jnp.dtype(self.embed_dtype) # Sanity check. + out["encoded"] = x + + x = gemma.RMSNorm(name="final_norm")(x) + out["pre_logits"] = x + + return x, out + + +class Model(nn.Module): + """GIVT model supporting decoder-only applications.""" + width: int + depth: int + mlp_dim: int + num_heads: int + num_kv_heads: int + head_dim: int + norm_eps: float = 1e-6 + + 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 = "nothing_saveable" + + vocab_size: Optional[int] = 1000 + bos_id: Optional[int] = None + boi_id: Optional[int] = None + nolabel_id: Optional[int] = None + # Multiply vocab size by this number, repeatedly embed text input with + # different vocabs, and concatenate. This is mainly aimed at class-conditional + # generation where the text sequence length is 1. + num_vocab_repeats: int = 1 + causal_mask_on_prefix: bool = True + untie_output_vocab: bool = False + + num_mixtures: int = 4 + multivariate: bool = False + out_dim: int = 32 + scale_tol: float = 1e-6 + head_dtype: str = "float32" + per_modality_final_norm: bool = False + + # Set to >0 for CFG support. + drop_labels_probability: float = 0.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 + 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.num_mixtures > 0 + + if self.multivariate and self.num_mixtures != 1: + raise ValueError("Cannot do multivariate GMM!") + + self.text_emb = nn.Embed( + self.vocab_size * self.num_vocab_repeats, + self.width, + name="EmbedText", + embedding_init=nn.initializers.normal(stddev=1.0), + ) + if self.untie_output_vocab: + assert self.num_vocab_repeats == 1 + self._text_logits = nn.Dense( + self.vocab_size, + name="LogitsText", + kernel_init=nn.initializers.normal(stddev=1.0), + ) + + self.img_emb = nn.Dense(self.width, name="EmbedImage") + self._img_logits = nn.Dense( + self.num_logits, + kernel_init=nn.initializers.zeros, + name="LogitsImage", + dtype=self.head_dtype, + ) + + if self.per_modality_final_norm: + self.text_norm = gemma.RMSNorm(name="TextNorm") + self.img_norm = gemma.RMSNorm(name="ImageNorm") + + self.decoder = GemmaBackbone( + width=self.width, + depth=self.depth, + mlp_dim=self.mlp_dim, + num_heads=self.num_heads, + num_kv_heads=self.num_kv_heads, + head_dim=self.head_dim, + out_dim=self.num_logits, + norm_eps=self.norm_eps, + dropout=self.dropout, + dropout_bdims=self.dropout_bdims, + cache_dtype=self.cache_dtype, + embed_dtype=self.embed_dtype, + head_dtype=self.head_dtype, + scan=self.scan, + remat_policy=self.remat_policy, + ) + + def prefill_cache(self, x, attn_mask, input_mask, *, cache_size): + """Initializes decoding cache with `x` [B, N, E] and returns pre-logits.""" + # WARNING: This function right aligns the inputs! The output + # positions do not match the input tokens for examples which have been + # aligned to the right. OTOH x[:,-1:] will match the last prefilled token. + _, prefill_len, _ = x.shape + + # Examples have variable number of tokens. We need to align the valid + # tokens to the right as the cache may depend on it. + x, attn_mask, input_mask = right_align(x, attn_mask, input_mask) + seq_len = jnp.sum(input_mask, axis=-1) + positions = jnp.cumsum(input_mask, axis=-1) - 1 + + self.put_variable("cache", "seq_len", seq_len) + self.put_variable("cache", "cache_begin", prefill_len - seq_len) + self.put_variable("cache", "cache_end", + jnp.full(seq_len.shape, prefill_len)) + + # Extend mask to cache_size. + attn_mask = jnp.pad( + attn_mask, ((0, 0), (0, 0), (0, cache_size - prefill_len))) + + x, out = self.decoder(x, positions=positions, mask=attn_mask, decode=True) + if self.per_modality_final_norm: + x = out["encoded"] + return x + + def extend_cache(self, x): + """Extends decoding cache with `x` [B, 1, E] and returns pre-logits.""" + assert x.shape[1] == 1, "Only supports extend the cache by one token." + assert self.decoder.scan, "Not implemented yet." + cache_size = self.variables[ + "cache"]["decoder"]["layers"]["attn"]["k_cache"].shape[2] + + # 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]) + + x, out = self.decoder( + x, positions=positions[:, None], mask=mask, decode=True) + if self.per_modality_final_norm: + x = out["encoded"] + return x + + def _square_plus(self, x): + # Via https://twitter.com/jon_barron/status/1387167648669048833 + 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) + 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 + ), + ) + + @staticmethod + def get_pmf(logits: jax.Array) -> distrax.Distribution: + return distrax.Categorical(logits=logits) + + def __call__( + self, + text_tokens: jax.Array, + image_tokens: jax.Array, + text_first_mask: jax.Array, + *, + text_input_mask: jax.Array | None = None, + drop_prefix: jax.Array | None = None, + train: bool = False, + ) -> tuple[jax.Array, distrax.Distribution]: + """Applies Transformer model on the inputs. + + Args: + text_tokens: batch of text token sequences [B, L]. + image_tokens: batch of image soft token sequences [B, L, D]. + text_first_mask: batch of text masks (1=text, 0=image) [B]. + text_input_mask: boolean [B, L] indicating which are valid tokens. + drop_prefix: If given, drop labels of the corresponding batches [B]. + train: whether it is training. + + Returns: + Return (text_logits, image_logits, pmf, pdf, decoder_out). + """ + x, attn_mask, input_mask = self.embed_image_and_text( + text_tokens, image_tokens, + text_first_mask=text_first_mask, text_input_mask=text_input_mask, + drop_prefix=drop_prefix) + + positions = jnp.cumsum(input_mask, axis=-1) - 1 + prelogits, decoder_out = self.decoder(x, mask=attn_mask, + positions=positions, + deterministic=not train) + if self.per_modality_final_norm: + prelogits = decoder_out["encoded"] + + # Note: For now the text part never tries to predict "boi" that follows it. + # That is not an issue for now as the text has a fixed number of tokens, for + # variable number of tokens, one should replace "eos" with "boi" in the + # labels for the loss of the prefix. + text_prelogits, img_prelogits = self.split_image_and_text_prelogits( + prelogits, text_first_mask, text_tokens.shape[1], image_tokens.shape[1], + ) + + text_logits = self.text_logits(text_prelogits) + pmf = self.get_pmf(text_logits) + image_logits = self.img_logits(img_prelogits) + pdf = self.get_pdf(image_logits) + return text_logits, image_logits, pmf, pdf, decoder_out + + def embed_image_and_text( + self, text_tokens, image_tokens, + *, text_first_mask, text_input_mask=None, drop_prefix=None, shift=True): + assert text_tokens is not None and image_tokens is not None + + if text_input_mask is None: # Default to all text tokens being valid. + text_input_mask = jnp.full(text_tokens.shape, True) + + txt_prefix, img_prefix = text_first_mask, ~text_first_mask + + # Embed image and text tokens. + if self.num_vocab_repeats > 1: + offsets = jnp.repeat( + jnp.arange(self.num_vocab_repeats) * self.vocab_size, + text_tokens.shape[1]) + def _repeat_text(tokens): + return jnp.tile(tokens, (1, self.num_vocab_repeats)) + offsets[None] + nolabel = jnp.full_like(text_tokens, self.nolabel_id) + nolabel = _repeat_text(nolabel) + nolabel = self.text_emb(nolabel) + text_tokens = _repeat_text(text_tokens) + text_input_mask = jnp.tile(text_input_mask, (1, self.num_vocab_repeats)) + else: + nolabel = self.lookup_token(self.nolabel_id, batch_size=1) + x_txt = self.text_emb(text_tokens) + x_img = self.img_emb(image_tokens) + + x_txt_m = text_input_mask + x_img_m = jnp.full(x_img.shape[:-1], True) + + # Replace embedded tokens with nolabel if they were to be dropped. + if drop_prefix is not None: + drop_txt = txt_prefix & drop_prefix + drop_img = img_prefix & drop_prefix + x_txt = jnp.where(drop_txt[:, None, None], nolabel, x_txt) + # Always use full prefix when dropping it. + x_txt_m = jnp.where( + drop_txt[:, None], jnp.full_like(x_txt_m, True), x_txt_m) + x_img = jnp.where(drop_img[:, None, None], nolabel[:, :1, :], x_img) + + # There are two versions controlled by whether we have a boi token. + batch_size, _, _ = image_tokens.shape + if self.boi_id is not None: + # [bos, text, boi, image], [boi, image, bos, text] + bos = self.lookup_token(self.bos_id, batch_size) + boi = self.lookup_token(self.boi_id, batch_size) + bos_m = jnp.full(bos.shape[:-1], True) + boi_m = jnp.full(boi.shape[:-1], True) + x_txt_img = jnp.concatenate([bos, x_txt, boi, x_img], axis=1) + x_txt_img_m = jnp.concatenate([bos_m, x_txt_m, boi_m, x_img_m], axis=1) + x_img_txt = jnp.concatenate([boi, x_img, bos, x_txt], axis=1) + x_img_txt_m = jnp.concatenate([boi_m, x_img_m, bos_m, x_txt_m], axis=1) + + else: + # [bos, text, image], [bos, image, text] + bos = self.lookup_token(self.bos_id, batch_size) + bos_m = jnp.full(bos.shape[:-1], True) + x_txt_img = jnp.concatenate([bos, x_txt, x_img], axis=1) + x_txt_img_m = jnp.concatenate([bos_m, x_txt_m, x_img_m], axis=1) + x_img_txt = jnp.concatenate([bos, x_img, x_txt], axis=1) + x_img_txt_m = jnp.concatenate([bos_m, x_img_m, x_txt_m], axis=1) + + if shift: + x_txt_img = x_txt_img[:, :-1] + x_img_txt = x_img_txt[:, :-1] + x_txt_img_m = x_txt_img_m[:, :-1] + x_img_txt_m = x_img_txt_m[:, :-1] + + # Select between the two versions: [text,image], [image,text]. + x = jnp.where(txt_prefix[:, None, None], x_txt_img, x_img_txt) + input_mask = jnp.where(txt_prefix[:, None], x_txt_img_m, x_img_txt_m) + + batch_size, seq_len = x.shape[:2] + attn_mask = nn.attention.make_causal_mask( + jnp.ones([batch_size, seq_len])).squeeze(1) + # Optionally remove attention mask for prefix tokens. + if not self.causal_mask_on_prefix: + txt_prefix_mask = jnp.full_like( + input_mask, False).at[:, :x_txt.shape[1] + 1].set(True) + img_prefix_mask = jnp.full_like( + input_mask, False).at[:, :x_img.shape[1] + 1].set(True) + prefix_mask = jnp.where( + txt_prefix[:, None], txt_prefix_mask, img_prefix_mask) + # Set all unmasked input positions corresponding to prefix tokens to True. + attn_mask = jnp.logical_or(attn_mask, prefix_mask[:, None, :]) + + # Also mask out the attn_mask with input mask. attn_mask is [B, N1, N2] + # and indicates if token N1 can attend to token N2. We mask the N2 part. + attn_mask = jnp.logical_and(attn_mask, input_mask[:, None, :]) + + return x, attn_mask, input_mask + + def split_image_and_text_prelogits( + self, prelogits, text_first_mask, + text_len, image_len): + # There are two versions controlled by whether we have a boi token. + if self.boi_id is not None: + # [bos, text, boi, image], [boi, image, bos, text] + a_txt = prelogits[:, :text_len] + a_img = prelogits[:, self.num_vocab_repeats*text_len+1:] + b_img = prelogits[:, :image_len] + b_txt = prelogits[:, image_len+1:image_len+1+text_len] + else: + # [bos, text, image], [bos, image, text] + a_txt = prelogits[:, :text_len] + a_img = prelogits[:, self.num_vocab_repeats*text_len:] + b_img = prelogits[:, :image_len] + b_txt = prelogits[:, image_len:image_len+text_len] + + txt_prelogits = jnp.where(text_first_mask[:, None, None], a_txt, b_txt) + img_prelogits = jnp.where(text_first_mask[:, None, None], a_img, b_img) + return txt_prelogits, img_prelogits + + def lookup_token(self, token_id: int, batch_size: int): + """Lookup a statically defined token (e.g. bos, boi, nolabel).""" + assert isinstance(token_id, int) + # TODO: Avoid matmul with full matrix for this code path. + return jnp.repeat( + self.text_emb(jnp.full((1, 1), token_id)), batch_size, axis=0) + + def text_logits(self, pre_logits): + if self.per_modality_final_norm: + pre_logits = self.text_norm(pre_logits) + if self.untie_output_vocab: + return self._text_logits(pre_logits) + return self.text_emb.attend(pre_logits) + + def img_logits(self, pre_logits): + if self.per_modality_final_norm: + pre_logits = self.img_norm(pre_logits) + return self._img_logits(pre_logits) + + 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] = (), +) -> Any: + """Loads params from init checkpoint and merges into init_params.""" + del model_params + assert isinstance(init_files, str), init_files + ckpt_params = utils.load_params(init_files) + ckpt_params = common.merge_params(ckpt_params, init_params, dont_load) + + return ckpt_params diff --git a/Tipsomaly/model/big_vision/models/proj/jetformer/patch_pca.py b/Tipsomaly/model/big_vision/models/proj/jetformer/patch_pca.py new file mode 100644 index 0000000000000000000000000000000000000000..6efcf1f232fec619db24197278c93566089fc7f6 --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/jetformer/patch_pca.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. + +"""Replace VAE with pretrained PCA patch embeddings. + +Colab used to train the PCA model: +(internal link) +""" + +import functools +import time +from typing import Sequence, Mapping, Any + +from absl import logging + +from big_vision import utils +from big_vision.models.proj.givt import vae + +import einops +import jax +import jax.numpy as jnp + + +@functools.lru_cache(maxsize=None) +def _load_pca_params(pca_init_file): + start = time.monotonic() + x = utils.npload(pca_init_file) + logging.info("Loaded PCA params %s in %.2f seconds", pca_init_file, + time.monotonic() - start) + return x + + +class Model(vae.Model): + """Patch PCA embedding model.""" + + pca_init_file: str = "" + noise_std: float = 0.01 + add_dequant_noise: bool = False + input_size: Sequence[int] = (256, 256) + patch_size: Sequence[int] = (16, 16) + whiten: bool = True + depth_to_seq: int = 1 + skip_pca: bool = False + + def setup(self) -> None: + assert self.codeword_dim is not None + assert self.pca_init_file or self.skip_pca + assert not self.skip_pca or self.depth_to_seq == 1 + if self.skip_pca: + return + + # Load sklearn PCA model params. + pca_params = _load_pca_params(self.pca_init_file) + self.components_ = jnp.asarray(pca_params["components_"], dtype=jnp.float32) + self.explained_variance_ = jnp.asarray( + pca_params["explained_variance_"], dtype=jnp.float32) + self.mean_ = jnp.asarray(pca_params["mean_"], dtype=jnp.float32) + + def _flatten_images(self, patches): + """Flatten images into a sequence of vectors and apply chroma scaling.""" + flatten = lambda x: einops.rearrange( + x, "b (h p) (w q) c -> b (h w) (p q c)", + p=self.patch_size[0], q=self.patch_size[1]) + return flatten(patches) + + def _unflatten_patches(self, patches): + """Unflatten a sequence of vectors into an image, apply chroma scaling.""" + (h, w), (p, q) = self.input_size, self.patch_size + unflatten = lambda x, c: einops.rearrange( + x, "b (h w) (p q c) -> b (h p) (w q) c", + h=h // p, w=w // q, p=p, q=q, c=c) + return unflatten(patches, 3) + + def encode( + self, + x: jax.Array, + *, + train: bool = False, + ) -> tuple[jax.Array, jax.Array]: + if self.add_dequant_noise: + x += jax.random.uniform( + self.make_rng("dropout"), x.shape, + minval=0.0, maxval=1.0 / 127.5) + + x = self._flatten_images(x) + + if self.skip_pca: + return x, jnp.zeros_like(x) + + ### Mimic scipy PCA transform code + x_emb = x @ self.components_.T + x_emb -= jnp.reshape(self.mean_, (1, -1)) @ self.components_.T + if self.whiten: + scale = jnp.sqrt(self.explained_variance_) + eps = jnp.finfo(scale.dtype).eps + scale = jnp.where(scale < eps, eps, scale) + x_emb /= scale + ### + + if self.depth_to_seq > 1: + x_emb = einops.rearrange( + x_emb, "b s (f d) -> b (f s) d", f=self.depth_to_seq) + + if self.noise_std <= 0.0: + logvar = jnp.zeros_like(x_emb) + else: + logvar = 2.0 * jnp.log(jnp.full(x_emb.shape, self.noise_std)) + + return x_emb, logvar + + # VAE-like reparametrization - add noise to PCA latents. + def reparametrize( + self, + mu: jax.Array, + logvar: jax.Array, + rng: jax.Array | None = None, + ) -> jax.Array: + if self.noise_std <= 0.0: + return mu + return super().reparametrize(mu, logvar, rng) + + def decode( + self, + x: jax.Array, + train: bool = False, + ) -> jax.Array | Mapping[str, jax.Array]: + del train + + if not self.skip_pca: + if self.depth_to_seq > 1: + x = einops.rearrange( + x, "b (f s) d -> b s (f d)", f=self.depth_to_seq) + + ### Mimic scipy PCA inverse transform code + if self.whiten: + scaled_components = ( + jnp.sqrt(self.explained_variance_[:, None]) * self.components_) + x_rec = x @ scaled_components + self.mean_ + else: + x_rec = x @ self.components_ + self.mean_ + ### + else: + x_rec = x + + x_rec = self._unflatten_patches(x_rec) + + return jnp.clip(x_rec, -1.0, 1.0) + + +def load(*args: Any) -> Any: + """Dummy loading function returning an empty params dict.""" + del args + return {} diff --git a/Tipsomaly/model/big_vision/models/proj/paligemma/gemma_bv.py b/Tipsomaly/model/big_vision/models/proj/paligemma/gemma_bv.py new file mode 100644 index 0000000000000000000000000000000000000000..0e3104df76389e9c10dce9547fe7dd6bae8415fb --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/paligemma/gemma_bv.py @@ -0,0 +1,189 @@ +# 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): + """Returns modified config from `gemma.get_config()`.""" + 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 + if model.final_logits_softcap is not None: + config.final_logits_softcap = model.final_logits_softcap + if model.attn_logits_softcap is not None: + config.attn_logits_softcap = model.attn_logits_softcap + 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. + final_logits_softcap: float | None = None + attn_logits_softcap: float | None = None + + 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/Tipsomaly/model/big_vision/models/proj/paligemma/paligemma.py b/Tipsomaly/model/big_vision/models/proj/paligemma/paligemma.py new file mode 100644 index 0000000000000000000000000000000000000000..aecbce09ebdfd6eb0b4497352037acfb7cfb4ec5 --- /dev/null +++ b/Tipsomaly/model/big_vision/models/proj/paligemma/paligemma.py @@ -0,0 +1,301 @@ +# 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. + # PaliGemma (https://arxiv.org/abs/2407.07726) + # (note that report shows bf16 is practically as good as f32) + "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"), + # PaliGemma 2 (https://arxiv.org/abs/2412.03555) + "pt_3b_224": os.path.join(GEMMA_DIR, "pt_3b_224.bf16.npz"), + "pt_3b_448": os.path.join(GEMMA_DIR, "pt_3b_448.bf16.npz"), + "pt_3b_896": os.path.join(GEMMA_DIR, "pt_3b_896.bf16.npz"), + "pt_10b_224": os.path.join(GEMMA_DIR, "pt_10b_224.bf16.npz"), + "pt_10b_448": os.path.join(GEMMA_DIR, "pt_10b_448.bf16.npz"), + "pt_10b_896": os.path.join(GEMMA_DIR, "pt_10b_896.bf16.npz"), + "pt_28b_224": os.path.join(GEMMA_DIR, "pt_28b_224.bf16.npz"), + "pt_28b_448": os.path.join(GEMMA_DIR, "pt_28b_448.bf16.npz"), + "pt_28b_896": os.path.join(GEMMA_DIR, "pt_28b_896.bf16.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/Tipsomaly/model/big_vision/models/proj/uvim/decode.py b/Tipsomaly/model/big_vision/models/proj/uvim/decode.py new file mode 100644 index 0000000000000000000000000000000000000000..809d4f342b2ae35a2b83a5edcba5f52fcd165248 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/uvim/vit.py b/Tipsomaly/model/big_vision/models/proj/uvim/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..7f3ac42b357b2f2f7315373df4419d3c69e7073c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/uvim/vit_test.py b/Tipsomaly/model/big_vision/models/proj/uvim/vit_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f42c85a2bb3ff040c73a233d01d1053adfbba5ed --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/uvim/vtt.py b/Tipsomaly/model/big_vision/models/proj/uvim/vtt.py new file mode 100644 index 0000000000000000000000000000000000000000..7bfd87cd7e6a5bc86d1aa6e6f7bc0afa8efa3b08 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/proj/uvim/vtt_test.py b/Tipsomaly/model/big_vision/models/proj/uvim/vtt_test.py new file mode 100644 index 0000000000000000000000000000000000000000..50b279b565af6cb644bae83253b297cae1569cd7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/models/vit.py b/Tipsomaly/model/big_vision/models/vit.py new file mode 100644 index 0000000000000000000000000000000000000000..221051dd2b50bd24c95e05c641d0e53959fe8fee --- /dev/null +++ b/Tipsomaly/model/big_vision/models/vit.py @@ -0,0 +1,505 @@ +# 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), + ) + + d = x.shape[-1] + x = nn.Dense(self.mlp_dim or 4 * d, dtype=self.dtype_mm, **inits)(x) + # In some extreme batch-size cases, this is needed as of Sept 2024: + x = nn.with_logical_constraint(x, ("act_batch", "act_len", "act_emb")) + 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: + # raise Exception("It should not come here, patch embds should not be ...") + 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)) + print('here_rep_size') + # print('after_rep_size') + # print(f'self.pool_type: {self.pool_type}') + + 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", + + # SigLIP 2 image encoder checkpoints from https://arxiv.org/abs/2502.14786 + "SigLIP2 B/16 224": "gs://big_vision/siglip2/siglip2_b16_224.npz:img", + "SigLIP2 B/16 256": "gs://big_vision/siglip2/siglip2_b16_256.npz:img", + "SigLIP2 B/16 384": "gs://big_vision/siglip2/siglip2_b16_384.npz:img", + "SigLIP2 B/16 512": "gs://big_vision/siglip2/siglip2_b16_512.npz:img", + "SigLIP2 B/32 256": "gs://big_vision/siglip2/siglip2_b32_256.npz:img", + "SigLIP2 L/16 256": "gs://big_vision/siglip2/siglip2_l16_256.npz:img", + "SigLIP2 L/16 384": "gs://big_vision/siglip2/siglip2_l16_384.npz:img", + "SigLIP2 L/16 512": "gs://big_vision/siglip2/siglip2_l16_512.npz:img", + "SigLIP2 So400m/14 224": "gs://big_vision/siglip2/siglip2_so400m14_224.npz:img", + "SigLIP2 So400m/14 384": "gs://big_vision/siglip2/siglip2_so400m14_384.npz:img", + "SigLIP2 So400m/16 256": "gs://big_vision/siglip2/siglip2_so400m16_256.npz:img", + "SigLIP2 So400m/16 384": "gs://big_vision/siglip2/siglip2_so400m16_384.npz:img", + "SigLIP2 So400m/16 512": "gs://big_vision/siglip2/siglip2_so400m16_512.npz:img", + "SigLIP2 g-opt/16 256": "gs://big_vision/siglip2/siglip2_g-opt16_256.npz:img", + "SigLIP2 g-opt/16 384": "gs://big_vision/siglip2/siglip2_g-opt16_384.npz:img", + # SigLIP 2 NaFlex image encoder checkpoints. + # These need `proj.image_text.naflex_vit.py` as the image encoder model + # and a non-standard preprocessing, see configs/proj/image_text/README_siglip2.md. + "SigLIP2 B/16 NaFlex": "gs://big_vision/siglip2/siglip2_b16_naflex.npz:img", + "SigLIP2 So400m/16 NaFlex": "gs://big_vision/siglip2/siglip2_so400m16_naflex.npz:img", + # pylint: enable=line-too-long +} diff --git a/Tipsomaly/model/big_vision/optax.py b/Tipsomaly/model/big_vision/optax.py new file mode 100644 index 0000000000000000000000000000000000000000..39ddb0fc3d8075985a75d7cdf150d430e141d681 --- /dev/null +++ b/Tipsomaly/model/big_vision/optax.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. + +"""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) + batch_size = grads_flat[0].shape[0] + clipped, _ = optax.per_example_global_norm_clip(grads_flat, max_norm) + grads_sum = jax.tree_util.tree_unflatten(grads_treedef, clipped) + grads_mean = jax.tree_util.tree_map(lambda x: x / batch_size, grads_sum) + return grads_mean, 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/Tipsomaly/model/big_vision/optax_test.py b/Tipsomaly/model/big_vision/optax_test.py new file mode 100644 index 0000000000000000000000000000000000000000..86f7bd9999079b393565ad5a718b4c1dbd815e79 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/__init__.py b/Tipsomaly/model/big_vision/pp/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/pp/archive/__init__.py b/Tipsomaly/model/big_vision/pp/archive/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/big_vision/pp/archive/autoaugment.py b/Tipsomaly/model/big_vision/pp/archive/autoaugment.py new file mode 100644 index 0000000000000000000000000000000000000000..f1af14ec6a1125ee9c3ea426f9224153483fd8e6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/archive/randaug.py b/Tipsomaly/model/big_vision/pp/archive/randaug.py new file mode 100644 index 0000000000000000000000000000000000000000..e8acec830237ce8840a75e44788d1a425ec17289 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/autoaugment.py b/Tipsomaly/model/big_vision/pp/autoaugment.py new file mode 100644 index 0000000000000000000000000000000000000000..6cc45f14e5d8c49cb54c649104851e0729ebb180 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/builder.py b/Tipsomaly/model/big_vision/pp/builder.py new file mode 100644 index 0000000000000000000000000000000000000000..8e254cdfdd17c3b58e1c9522bbc2a5be1bb089b9 --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/builder.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. + +"""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, log_steps=False): + """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. + log_steps: Whether to log the steps of the preprocessing pipeline. + + Returns: + preprocessing function. + + Raises: + ValueError: if preprocessing function name is unknown + """ + + names, ops, spec_strings = [], [], [] + 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]) + spec_strings.append(op_spec) + 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, log_steps + + # 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, spec in zip(names, ops, spec_strings): + if log_steps: + logging.info("Pre-processing step (%s): %s\n%s", name, spec, data) + 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/Tipsomaly/model/big_vision/pp/builder_test.py b/Tipsomaly/model/big_vision/pp/builder_test.py new file mode 100644 index 0000000000000000000000000000000000000000..f3a75cc05417a7f3ace70f97583d2e7b1ae4c432 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/ops_general.py b/Tipsomaly/model/big_vision/pp/ops_general.py new file mode 100644 index 0000000000000000000000000000000000000000..1c04da62ed7c1df69a4c7d887b80aa66af02e33f --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/ops_general.py @@ -0,0 +1,468 @@ +# 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(keys=None): + """Flattens the keys of data with separator '/'.""" + + def _flatten(data): + flatten_keys = keys or list(data.keys()) + not_flattened = {k: v for k, v in data.items() if k not in flatten_keys} + flattened = {k: v for k, v in data.items() if k in flatten_keys} + flattened, _ = bv_utils.tree_flatten_with_names(flattened) + return {**dict(flattened), **not_flattened} + + 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): + # Catch a hard to identify/understand user error: + assert data[inkeys[0]].ndim > 0, ( + f"You're calling `choice_no_replacement` on {inkeys}, a scalar." + " That's probably a mistake ; double-check and then just don't." + ) + + 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): + # Catch a hard to identify/understand user error: + assert data[inkeys[0]].ndim > 0, ( + f"You're calling `choice` on {inkeys}, a scalar." + " That's probably a mistake ; double-check and then just don't." + ) + + 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/Tipsomaly/model/big_vision/pp/ops_general_test.py b/Tipsomaly/model/big_vision/pp/ops_general_test.py new file mode 100644 index 0000000000000000000000000000000000000000..89f616e1690c6e83aff818cf0fff540dcad073fd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/ops_image.py b/Tipsomaly/model/big_vision/pp/ops_image.py new file mode 100644 index 0000000000000000000000000000000000000000..bdc55a5659c3e0df0c209edef798bbd5c6a7f623 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/ops_image_test.py b/Tipsomaly/model/big_vision/pp/ops_image_test.py new file mode 100644 index 0000000000000000000000000000000000000000..080fe673cf90f83b405106dd057870ee8e8f76a2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/ops_text.py b/Tipsomaly/model/big_vision/pp/ops_text.py new file mode 100644 index 0000000000000000000000000000000000000000..5ff8bdc3dae1197c7796ec5416c23052d61bef4e --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/ops_text.py @@ -0,0 +1,411 @@ +# 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 +import string + +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.i21k_label_names") +@utils.InKeyOutKey(indefault="label", outdefault="labels") +def get_pp_i21k_label_names(): + """Converts i21k label ids to strings.""" + + def _pp_imagenet_labels(label): + return tf.gather(imagenet_class_names.IMAGENET21k_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 + + +@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 + + +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): + s = txt.numpy().decode() + return tf.constant(self.to_int(s, 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/Tipsomaly/model/big_vision/pp/ops_text_test.py b/Tipsomaly/model/big_vision/pp/ops_text_test.py new file mode 100644 index 0000000000000000000000000000000000000000..ccf09d8e35bab0a0e5daec1ead7583d4b02cb82f --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/ops_text_test.py @@ -0,0 +1,200 @@ +# 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"]) + + def test_get_pp_i21k_label_names(self): + op = pp.get_pp_i21k_label_names() + labels = op({"label": tf.constant([0, 1])})["labels"].numpy().tolist() + self.assertAllEqual(labels, ["organism", "benthos"]) + + @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" + ) + + def test_strfmt(self): + data = { + "int": tf.constant(42, tf.uint8), + "float": tf.constant(3.14, tf.float32), + "vec": tf.range(3), + "empty_str": tf.constant(""), + "regex_problem1": tf.constant(r"no \replace pattern"), + "regex_problem2": tf.constant(r"yes \1 pattern"), + } + for out in self.tfrun(pp.get_strfmt("Nothing"), data): + self.assertEqual(out["text"], b"Nothing") + for out in self.tfrun(pp.get_strfmt("{int}"), data): + self.assertEqual(out["text"], b"42") + for out in self.tfrun(pp.get_strfmt("A{int}"), data): + self.assertEqual(out["text"], b"A42") + for out in self.tfrun(pp.get_strfmt("{int}A"), data): + self.assertEqual(out["text"], b"42A") + for out in self.tfrun(pp.get_strfmt("{int}{int}"), data): + self.assertEqual(out["text"], b"4242") + for out in self.tfrun(pp.get_strfmt("A{int}A{int}A"), data): + self.assertEqual(out["text"], b"A42A42A") + for out in self.tfrun(pp.get_strfmt("A{float}A"), data): + self.assertEqual(out["text"], b"A3.14A") + for out in self.tfrun(pp.get_strfmt("A{float}A{int}"), data): + self.assertEqual(out["text"], b"A3.14A42") + for out in self.tfrun(pp.get_strfmt("A{vec}A"), data): + self.assertEqual(out["text"], b"A[0 1 2]A") + for out in self.tfrun(pp.get_strfmt("A{empty_str}A"), data): + self.assertEqual(out["text"], b"AA") + for out in self.tfrun(pp.get_strfmt("{empty_str}"), data): + self.assertEqual(out["text"], b"") + for out in self.tfrun(pp.get_strfmt("A{regex_problem1}A"), data): + self.assertEqual(out["text"], br"Ano \replace patternA") + for out in self.tfrun(pp.get_strfmt("A{regex_problem2}A"), data): + self.assertEqual(out["text"], br"Ayes \1 patternA") + + +@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/Tipsomaly/model/big_vision/pp/proj/clippo/download_unifont.sh b/Tipsomaly/model/big_vision/pp/proj/clippo/download_unifont.sh new file mode 100644 index 0000000000000000000000000000000000000000..44251749e0c8171318f49e75b9b725d29d06c53a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/clippo/pp_ops.py b/Tipsomaly/model/big_vision/pp/proj/clippo/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..e92050dc5f3371c730913637be191c0c63bf5423 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/flaxformer/bert_ops.py b/Tipsomaly/model/big_vision/pp/proj/flaxformer/bert_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..bcc76ef30b1187b178a9a1b309f188fee4468971 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/flaxformer/bert_ops_test.py b/Tipsomaly/model/big_vision/pp/proj/flaxformer/bert_ops_test.py new file mode 100644 index 0000000000000000000000000000000000000000..3df58abd8e4f3712424ccc496d7ed39aae71cdcf --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/givt/pp_ops.py b/Tipsomaly/model/big_vision/pp/proj/givt/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..882bc93c45e4a4fd30d061d7b51a6295241653d6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex.py b/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex.py new file mode 100644 index 0000000000000000000000000000000000000000..bbfb2b70e7a2909c8fe7b412c669bd71fc1fd49e --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex.py @@ -0,0 +1,202 @@ +# 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. + +"""NaFlex (NaViT + FlexiViT) preprocessing ops.""" + +from big_vision.pp import utils +from big_vision.pp.registry import Registry +import big_vision.utils as u +import tensorflow as tf + + +def _get_image_size_for_seq( + image_hw, + patch_size: int, + max_sequence_len: int, + divisible_by_patch: bool = True, + eps: float = 1e-5): + """Determine scaling ratio and image size for `get_resize_to_sequence`. + + Did not prove monotonicity necessary for binary search correctness, but this + works well in practice. + + Args: + image_hw: Image height and width. + patch_size: Patchification patch size. + max_sequence_len: Maximum allowed sequence length for the resulting image. + divisible_by_patch: If True, the resulting image height and width must be + divisible by patch size. + eps: Small number used for binary search convergence. + + Returns: + ratio: Scaling ratio to applied to image. + target_hw: Target image height and width taking into account the scaling + ratio and the `divisible_by_patch` constraint. + """ + def search_not_done(lb, rb): + return (rb - lb) >= eps + + def prepare_target_hw(ratio): + target_hw = tf.cast(image_hw, tf.float32) * ratio + if divisible_by_patch: + # Round to multiple of patch size as we want to avoid dropping patches. + target_hw = patch_size * tf.math.ceil(target_hw / patch_size) + # Ensure that the image is at least 1 patch in height / width. + target_hw = tf.maximum(target_hw, patch_size) + target_hw = tf.cast(target_hw, tf.int32) + return target_hw + + def is_feasible(ratio): + target_hw = prepare_target_hw(ratio) + num_patches = target_hw / patch_size + sequence_len = tf.math.reduce_prod(num_patches) + return sequence_len <= max_sequence_len + + def _search_fn(lb, rb): + mid = (lb + rb) / 2 + return tf.cond(is_feasible(mid), lambda: (mid, rb), lambda: (lb, mid)) + + # Left and right boundaries for the binary search. + state = (tf.constant(eps / 10.), tf.constant(100.0)) + ratio, _ = tf.while_loop( + search_not_done, _search_fn, state, parallel_iterations=1) + tf.assert_greater( + ratio, eps, message="Binary search failed - image too large?") + tf.assert_less( + ratio, 100.0, message="Binary search failed - image too small?") + + return ratio, prepare_target_hw(ratio) + + +@Registry.register("preprocess_ops.resize_to_sequence") +@utils.InKeyOutKey(indefault="image", outdefault="image") +def get_resize_to_sequence( + patch_size: int, + max_sequence_len: int, + divisible_by_patch: bool = True, + eps: float = 1e-5): + """Resizes image if it violates restrictions on sequence/side length. + + This op attempts to resize the image in an AR-preserving manner such that: + - The sequence length of the resulting image (after patchification) is + maximized, but <= `max_sequence_len`. + + This op *violates* the AR-preserving property if: + - Image size resulting from the above procedure is not a multiple of patch + size. In this case AR is distorted to ensure this condition is satisfied. + + Args: + patch_size: Patchification patch size. + max_sequence_len: Maximum allowed sequence length for the resulting image. + divisible_by_patch: If True, the resulting image height and width must be + divisible by patch size. + eps: Small number used for binary search convergence. + + Returns: + Pre-processing op. + """ + def _resize_fn(image): + """Performs binary search to find a feasible image size.""" + image_hw = tf.shape(image)[:2] + _, target_hw = _get_image_size_for_seq( + image_hw, + patch_size, + max_sequence_len, + divisible_by_patch=divisible_by_patch, + eps=eps) + + # Actually resize image. + image = tf.image.resize( + image, + target_hw, + preserve_aspect_ratio=False, + antialias=True) + return tf.ensure_shape(image, [None, None, 3]) + return _resize_fn + + +@Registry.register("preprocess_ops.central_crop_to_sequence") +@utils.InKeyOutKey(indefault="image", outdefault="image") +def get_central_crop_to_sequence( + patch_size: int, + max_sequence_len: int, + divisible_by_patch: bool = True, + eps: float = 1e-5): + """Central crops image such that patch sequence length satisfies constraints. + + Constraints used are the as in `resize_to_sequence`. + + Args: + patch_size: Patchification patch size. + max_sequence_len: Maximum allowed sequence length for the resulting image. + divisible_by_patch: If True, the resulting image height and width must be + divisible by patch size. + eps: Small number used for binary search convergence. + + Returns: + Pre-processing op. + """ + def _central_crop_fn(image): + image_hw = tf.shape(image)[:2] + _, target_hw = _get_image_size_for_seq( + image_hw, + patch_size, + max_sequence_len, + divisible_by_patch=divisible_by_patch, + eps=eps) + + tf.assert_greater( + image_hw + 1, target_hw, + "For central crop the image must be larger than target HW.") + offset_hw = (image_hw - target_hw) // 2 + image = image[ + offset_hw[0]:offset_hw[0] + target_hw[0], + offset_hw[1]:offset_hw[1] + target_hw[1], + :] + return tf.ensure_shape(image, [None, None, 3]) + return _central_crop_fn + + +@Registry.register("preprocess_ops.patchify") +@utils.InKeyOutKey(indefault="image", outdefault="image") +def get_patchify(patch_size): + """Reshapes image into patches and provides patch coordinates.""" + ph, pw = utils.maybe_repeat(patch_size, 2) + + def _patchify(img): + patches = tf.image.extract_patches( + img[None, ...], sizes=[1, ph, pw, 1], strides=[1, ph, pw, 1], + rates=[1, 1, 1, 1], padding="VALID")[0] + # Patches is now (nh, nw, ph*pw*3), i.e. contains flattened patches. + nh, nw, d = tf.shape(patches)[0], tf.shape(patches)[1], tf.shape(patches)[2] + + # Get two (nh, nw) tensors of y/x indices of the patches. + gy, gx = tf.meshgrid(tf.range(nh), tf.range(nw), indexing="ij") + + return { + "patches": tf.reshape(patches, (nh * nw, d)), + "yidx": tf.reshape(gy, [nh * nw]), + "xidx": tf.reshape(gx, [nh * nw]), + "type": tf.fill([nh * nw], 1), + } + return _patchify + + +@Registry.register("preprocess_ops.tuplify") +def get_tuplify(inkeys: list[str], outkey: str): + """Create a tuple of multiple inputs.""" + def tuplify(data): + data[outkey] = tuple(u.tree_get(data, k) for k in inkeys) + return data + return tuplify diff --git a/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex_test.py b/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex_test.py new file mode 100644 index 0000000000000000000000000000000000000000..fce4cb1a88f7c54f01add03db81b6d5432e9950e --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/proj/image_text/ops_naflex_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 NaFlex preprocessing ops.""" + +import copy + +from absl.testing import parameterized +from big_vision.pp.proj.image_text import ops_naflex as pp +import numpy as np +import tensorflow as tf + + +def get_image_data(h, w): + img = tf.random.uniform((h, w, 3), 0, 255, tf.int32) # Can't ask uint8!? + return {"image": tf.cast(img, tf.uint8)} + + +class NaflexTest(tf.test.TestCase, parameterized.TestCase): + + def tfrun(self, ppfn, data): + # Run once as standalone, as could happen eg in colab. + yield tf.nest.map_structure(np.array, ppfn(copy.deepcopy(data))) + + # 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 + + @parameterized.parameters( + (6, 8), + (7, 9), + (8, 10), + ) + def test_patchify_valid(self, h, w): + """Tests the patchification op.""" + op = pp.get_patchify((3, 4)) + inputs = get_image_data(h, w) + for data in self.tfrun(op, inputs): + self.assertEqual(data["image"]["patches"].shape, (4, 3*4*3)) + self.assertAllEqual( + data["image"]["patches"][-1], + np.array(inputs["image"])[3:6, 4:8, :].flatten()) + self.assertAllEqual(data["image"]["yidx"], [0, 0, 1, 1]) + self.assertAllEqual(data["image"]["xidx"], [0, 1, 0, 1]) + + @parameterized.named_parameters([ + ("square_121_exact", (48, 48), 3, 121, (33, 33)), + ("square_225_inexact", (112, 109), 7, 225, (105, 105)), + ("square_64_exact", (176, 176), 11, 64, (88, 88)), + ("rect_12_exact", (256, 64), 16, 12, (96, 32)), + ("rect_15_exact_ps8", (256, 64), 8, 15, (56, 16)), + ("rect_16_inexact", (63, 241), 16, 16, (32, 128)), + ("rect_less_than_patch", (16, 512), 16, 16, (16, 256)), + ]) + def test_pp_resize_to_sequence( + self, image_size, patch_size, seq_len, expected_image_size): + """Tests the AR-preserving `resize_to_sequence` op.""" + op = pp.get_resize_to_sequence(patch_size, seq_len) + inputs = get_image_data(*image_size) + for outputs in self.tfrun(op, inputs): + self.assertAllEqual(outputs["image"].shape, expected_image_size + (3,)) + +if __name__ == "__main__": + tf.test.main() diff --git a/Tipsomaly/model/big_vision/pp/proj/paligemma/ops.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/ops.py new file mode 100644 index 0000000000000000000000000000000000000000..94e6225b560a5b6f319048a55cf3c2afda9cb201 --- /dev/null +++ b/Tipsomaly/model/big_vision/pp/proj/paligemma/ops.py @@ -0,0 +1,168 @@ +# 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 + +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) + tokens = tf.cast(tokens, tf.int32) # Same dtype as in-graph tokenizer. + 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')) + toks = tf.ensure_shape(toks, [None]) + 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): + # Refer to original inputs to support using same key as input/output. + inputs = dict(**data) + data[outkey] = tf.concat([inputs[k] for k in keys], axis=0) + for mask_name, mask_vals in masks.items(): + m = [tf.fill(tf.shape(inputs[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.strjoin') +@utils.InKeyOutKey() +def get_strjoin(glue, axis=None): + def _strjoin(x): + return tf.strings.reduce_join(x, axis=axis, 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/Tipsomaly/model/big_vision/pp/proj/paligemma/robustness.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/robustness.py new file mode 100644 index 0000000000000000000000000000000000000000..e40ed0cc481ff6e5da6349885d3d5a6f50d84da0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/paligemma/sciqa_ops.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/sciqa_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..845e0c0dadadb662231053ebdee64f24979ffc7d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/paligemma/segmentation.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/segmentation.py new file mode 100644 index 0000000000000000000000000000000000000000..7b062acff63e75fb9b7a7ca975ca043c99e7a973 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/paligemma/video.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/video.py new file mode 100644 index 0000000000000000000000000000000000000000..d353d0c64faac0020a5fc3cf0ef29d71724ea766 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/paligemma/widgetcap.py b/Tipsomaly/model/big_vision/pp/proj/paligemma/widgetcap.py new file mode 100644 index 0000000000000000000000000000000000000000..bfd7b82b977871abbabed42306f73ba13ad38df5 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/uvim/pp_ops.py b/Tipsomaly/model/big_vision/pp/proj/uvim/pp_ops.py new file mode 100644 index 0000000000000000000000000000000000000000..929de6e1da161fe450a477cc69c3a42677350373 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/proj/uvim/pp_ops_test.py b/Tipsomaly/model/big_vision/pp/proj/uvim/pp_ops_test.py new file mode 100644 index 0000000000000000000000000000000000000000..78c065e372788ee77f03c53aadb444614a391ebc --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/registry.py b/Tipsomaly/model/big_vision/pp/registry.py new file mode 100644 index 0000000000000000000000000000000000000000..f5c7d996d756be16ba68a5fcb143f23129e1249d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/registry_test.py b/Tipsomaly/model/big_vision/pp/registry_test.py new file mode 100644 index 0000000000000000000000000000000000000000..2296e7de91ce0495bade59e8e65417384507e58e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/tokenizer.py b/Tipsomaly/model/big_vision/pp/tokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..681494e436aacd48d5d720e07d2df1a80c704eb2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/utils.py b/Tipsomaly/model/big_vision/pp/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee834560246549c71f0a6d9785694fd1507ca9b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/pp/utils_test.py b/Tipsomaly/model/big_vision/pp/utils_test.py new file mode 100644 index 0000000000000000000000000000000000000000..beec18cef62a9638ed143229d7aedc5e218a70b6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/requirements.txt b/Tipsomaly/model/big_vision/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..9ae71db5ac26e2746864c37959dfd28cb9fe70bf --- /dev/null +++ b/Tipsomaly/model/big_vision/requirements.txt @@ -0,0 +1,19 @@ +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 +psutil +pycocoevalcap diff --git a/Tipsomaly/model/big_vision/run_tpu.sh b/Tipsomaly/model/big_vision/run_tpu.sh new file mode 100644 index 0000000000000000000000000000000000000000..3c3da2e44e7d2829a00188f5e6177ea9d6e3ba4d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/sharding.py b/Tipsomaly/model/big_vision/sharding.py new file mode 100644 index 0000000000000000000000000000000000000000..be76cb3a1f6b8bc0e494515bac2a54528a53494c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/download_tfds_datasets.py b/Tipsomaly/model/big_vision/tools/download_tfds_datasets.py new file mode 100644 index 0000000000000000000000000000000000000000..b64c33d51a7cb8df063d15e828e30b07007ff6b0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/eval_only.py b/Tipsomaly/model/big_vision/tools/eval_only.py new file mode 100644 index 0000000000000000000000000000000000000000..abdde4a6c0aa656a2e8ec76ce645982a2a6723b3 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/README.md b/Tipsomaly/model/big_vision/tools/lit_demo/README.md new file mode 100644 index 0000000000000000000000000000000000000000..c7c1825219944bbf8141eeea4b8bbca0bc1f6738 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/build.js b/Tipsomaly/model/big_vision/tools/lit_demo/build.js new file mode 100644 index 0000000000000000000000000000000000000000..a44aa8aa7b05c7ecb5acc4f7478a175897f15b9d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/package.json b/Tipsomaly/model/big_vision/tools/lit_demo/package.json new file mode 100644 index 0000000000000000000000000000000000000000..838cf1ff349f7496fc8edd43aa207b2c00174d20 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/app.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..3fccbc940cd173826dad6c6d25fd11a2c177ce16 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-carousel.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-carousel.scss new file mode 100644 index 0000000000000000000000000000000000000000..2da94515d9d3627611c4f3d2b6daf6762916e42d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-carousel.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-carousel.ts new file mode 100644 index 0000000000000000000000000000000000000000..b392d5b95a3a048deb370fa68cac460bf62f9be2 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-prompts.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-prompts.scss new file mode 100644 index 0000000000000000000000000000000000000000..66cd06817ee5d2f5e4af4c392052d357bc4e3456 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-prompts.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/image-prompts.ts new file mode 100644 index 0000000000000000000000000000000000000000..823f166518f5011d17d560b6b1e246060118863f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/lit-demo-app.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/lit-demo-app.scss new file mode 100644 index 0000000000000000000000000000000000000000..884aeb635db195350a8316c95648942be939d4ee --- /dev/null +++ b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/lit-demo-app.scss @@ -0,0 +1,3 @@ +.loading-container { + text-align: center; +} diff --git a/Tipsomaly/model/big_vision/tools/lit_demo/src/components/lit-demo-app.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/lit-demo-app.ts new file mode 100644 index 0000000000000000000000000000000000000000..41f42f484c2ccb79b42858af45d1ddfd3ee304f6 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/loading-animation.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/loading-animation.scss new file mode 100644 index 0000000000000000000000000000000000000000..032be7a31f2a07138f509c7c6a85063c0d25864a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/loading-animation.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/loading-animation.ts new file mode 100644 index 0000000000000000000000000000000000000000..8af9de23b54d3b6b46ad5d9c8a7ac75bd344ac22 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/message-list.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/message-list.scss new file mode 100644 index 0000000000000000000000000000000000000000..79aa76465b2120d256c14c3a4583709a050e2be4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/message-list.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/message-list.ts new file mode 100644 index 0000000000000000000000000000000000000000..542bfb0f9502d55c65ff131c43684246192cb6f7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/model-controls.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/model-controls.scss new file mode 100644 index 0000000000000000000000000000000000000000..a7627c8cb9d15b825cd3a84cdc217eb2b39cfc05 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/components/model-controls.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/components/model-controls.ts new file mode 100644 index 0000000000000000000000000000000000000000..40fdb646c436ef7dfd9332835819f0e9534c067b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/exports.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/exports.ts new file mode 100644 index 0000000000000000000000000000000000000000..b756e172aa6f89692faf1ba295e38ea14c59ccb0 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/index.html b/Tipsomaly/model/big_vision/tools/lit_demo/src/index.html new file mode 100644 index 0000000000000000000000000000000000000000..580e328f1d0efa0b8d682d7d59e20d58b75e2367 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/app.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/app.ts new file mode 100644 index 0000000000000000000000000000000000000000..0e104f298033933514a575ef8ba796c70b11c4c4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/compute.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/compute.ts new file mode 100644 index 0000000000000000000000000000000000000000..06b9c7eb72a4e565047fc6966e56cc67465c6145 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/constants.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/constants.ts new file mode 100644 index 0000000000000000000000000000000000000000..45dab49ca5699a2897b98c0a412c838d8d8ba0e7 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/data.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..6322d7dcefe22147d5715ee4710d2d7f2daf951a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/url_utils.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/lit_demo/url_utils.ts new file mode 100644 index 0000000000000000000000000000000000000000..d169bc4711d44fbeb146a85de55d47a882fe695b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/playground.html b/Tipsomaly/model/big_vision/tools/lit_demo/src/playground.html new file mode 100644 index 0000000000000000000000000000000000000000..112d69eb294ea364ea6b843a491ddb78fb1ff343 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/style.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/style.scss new file mode 100644 index 0000000000000000000000000000000000000000..7fab695cb992dd0552068f92cad28a7c6546eb07 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/style/colors.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/style/colors.scss new file mode 100644 index 0000000000000000000000000000000000000000..8c03eefa93fed513fee7cf58660f7ee7fb8bf6a4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/style/mixins.scss b/Tipsomaly/model/big_vision/tools/lit_demo/src/style/mixins.scss new file mode 100644 index 0000000000000000000000000000000000000000..8e4ad3fd9e0bdd24b1ceb08dc55551a05e37f343 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/common.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/common.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd601bf48f9cc66327d574efc0e189b2228c8330 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/index.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..687e4da496cdfa726599f2791ba601fde62a324d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe.ts new file mode 100644 index 0000000000000000000000000000000000000000..b2603bac81b0066e0c63bf445bc2e41703bf7bb3 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe_test.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_bpe_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..022ae1065fe89dbc28695ac9d8f16dbf2f7aaa36 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e3ac0a40ff83678423978c046f7d035a85de2dd --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram_test.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/sentencepiece_unigram_test.ts new file mode 100644 index 0000000000000000000000000000000000000000..d52af2ba624e6d104b3529491fee6ba6f6c61754 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/trie.ts b/Tipsomaly/model/big_vision/tools/lit_demo/src/tokenizers/trie.ts new file mode 100644 index 0000000000000000000000000000000000000000..2f6954bb962cad952e2f246a65a46bcb4fdb5756 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/tools/lit_demo/src/tsconfig.json b/Tipsomaly/model/big_vision/tools/lit_demo/src/tsconfig.json new file mode 100644 index 0000000000000000000000000000000000000000..f77877754a883975f3680f4cdcdc7df1f0c5e796 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/train.py b/Tipsomaly/model/big_vision/train.py new file mode 100644 index 0000000000000000000000000000000000000000..2e15cdce73eff927a29114983f03a2089ebe2fea --- /dev/null +++ b/Tipsomaly/model/big_vision/train.py @@ -0,0 +1,518 @@ +# 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 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") + logging.info(f"The config:\n{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.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool(1) + + # 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")]) + + write_note("Creating device mesh...") + mesh = u.create_device_mesh( + config_mesh, + allow_split_physical_axes=config.get("mesh_allow_split_physical_axes", + False)) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + # 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 = mesh.devices.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("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) + for k in ("training_loss", "l2_grads", "l2_updates", "l2_params"): + 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)} + + 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/Tipsomaly/model/big_vision/trainers/proj/cappa/generative.py b/Tipsomaly/model/big_vision/trainers/proj/cappa/generative.py new file mode 100644 index 0000000000000000000000000000000000000000..a4b8873a45eac7e5c4d5d9dfea8c42abccda8283 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/cappa/predict_fns.py b/Tipsomaly/model/big_vision/trainers/proj/cappa/predict_fns.py new file mode 100644 index 0000000000000000000000000000000000000000..4e369f23cd840370cdd419b5e67f377e2dcf4ad1 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/distill/distill.py b/Tipsomaly/model/big_vision/trainers/proj/distill/distill.py new file mode 100644 index 0000000000000000000000000000000000000000..e42fbc2c7b9c72c1ba78d1dddc949f65fc90be69 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/flexi/common.py b/Tipsomaly/model/big_vision/trainers/proj/flexi/common.py new file mode 100644 index 0000000000000000000000000000000000000000..2dd59883fb72b171a902a758f3aa578047dd0c3d --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/flexi/distill.py b/Tipsomaly/model/big_vision/trainers/proj/flexi/distill.py new file mode 100644 index 0000000000000000000000000000000000000000..00ce8ad93c32abd13bc8785bb9999a7be9ca9645 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/flexi/train.py b/Tipsomaly/model/big_vision/trainers/proj/flexi/train.py new file mode 100644 index 0000000000000000000000000000000000000000..5e76265271e3cd0db97851e93e3d6bf09a11670f --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/givt/generative.py b/Tipsomaly/model/big_vision/trainers/proj/givt/generative.py new file mode 100644 index 0000000000000000000000000000000000000000..e571b759161d05c129bcb48719f27a2c57fddd1e --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/givt/utils.py b/Tipsomaly/model/big_vision/trainers/proj/givt/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..449b43f37fb8f60fb1e41090ed91c70c92719f02 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/givt/vae.py b/Tipsomaly/model/big_vision/trainers/proj/givt/vae.py new file mode 100644 index 0000000000000000000000000000000000000000..242899e5ea00dd133fad1269382bb9309ba5dfca --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/gsam/gsam.py b/Tipsomaly/model/big_vision/trainers/proj/gsam/gsam.py new file mode 100644 index 0000000000000000000000000000000000000000..89efa8d38c2dddf8acd66b8440beafb980710189 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/gsam/train.py b/Tipsomaly/model/big_vision/trainers/proj/gsam/train.py new file mode 100644 index 0000000000000000000000000000000000000000..8b66b03eb544987cbcbf91240ccf9ee5e1d5883c --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/image_text/_deprecated_contrastive.py b/Tipsomaly/model/big_vision/trainers/proj/image_text/_deprecated_contrastive.py new file mode 100644 index 0000000000000000000000000000000000000000..863941a8e5d84b73530b93a19591cb65e01e0dcf --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/image_text/siglip.py b/Tipsomaly/model/big_vision/trainers/proj/image_text/siglip.py new file mode 100644 index 0000000000000000000000000000000000000000..3ea6118be9f5d1f581667737cb774412672fcaa4 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/jet/train.py b/Tipsomaly/model/big_vision/trainers/proj/jet/train.py new file mode 100644 index 0000000000000000000000000000000000000000..cc287d982b39ffa40b0f85bf6c868eac8a7298f3 --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/jet/train.py @@ -0,0 +1,535 @@ +# 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 Jet.""" +# 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 distrax +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 + + # 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(**config.get("model", {})) + + first_batch = next(train_iter) + def init(rng, batch): + context = batch["label"][:, None] if "label" in batch else None + return model.init(rng, + batch["image"], + context=context, + method=model.forward)["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, first_batch) + + 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, first_batch) + 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 # +# # +################################################################################ + + def _bits_per_dim(logits, logdet, dim_count, reduce=True): + normal = distrax.Normal(0.0, 1.0) + nll = -normal.log_prob(logits) + nll = jnp.sum(nll + np.log(127.5), axis=range(1, nll.ndim)) + + bits = nll - logdet + + reduce_fn = jnp.mean if reduce else lambda x: x + normalizer = np.log(2) * dim_count + + logging.info("nll: %s", nll.shape) + return (reduce_fn(bits) / normalizer, + reduce_fn(nll) / normalizer, + reduce_fn(logdet) / normalizer) + + @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) + + rng_input, rng_model, rng_cond_drop = jax.random.split(rng, 3) + images = (batch["image"] + + jax.random.uniform( + rng_input, + shape=batch["image"].shape, + minval=0.0, + maxval=1.0 / 127.5)) + + def loss_fn(params): + context = None + if "label" in batch: + drop = (config.get("condition_drop_prob", 0.1) > + jax.random.uniform(rng_cond_drop, (), jnp.float32)) + context = batch["label"][:, None] * (~drop) + logits, logdet = model.apply( + {"params": params}, images, + rngs={"dropout": rng_model}, + context=context, + method=model.forward) + bits, nll, logdet = _bits_per_dim(logits, logdet, + np.prod(images.shape[1:])) + return bits, {"bits": bits, "nll": nll, "logdet": logdet} + + params, opt = train_state["params"], train_state["opt"] + (loss, extra), 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, **extra} + 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_loss_fn(train_state, batch): + noise = jax.lax.rng_uniform(0.0, 1.0, batch["image"].shape) / 127.5 + logits, logdet = model.apply( + {"params": train_state["params"]}, + batch["image"] + noise, + context=batch["label"][:, None] if "label" in batch else None, + method=model.forward) + bits, nll, logdet = _bits_per_dim( + logits, logdet, + np.prod(batch["image"].shape[1:]), + reduce=False) + return {"bits": bits, "nll": nll, "logdet": logdet} + + eval_fns = { + "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/Tipsomaly/model/big_vision/trainers/proj/jetformer/predict_fns.py b/Tipsomaly/model/big_vision/trainers/proj/jetformer/predict_fns.py new file mode 100644 index 0000000000000000000000000000000000000000..1335f5be454476adbe6a37bb79e8eeaf52f50501 --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/jetformer/predict_fns.py @@ -0,0 +1,297 @@ +# 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 JetFormer.""" +# pylint: disable=consider-using-from-import +from absl import logging +import big_vision.models.proj.givt.parallel_decode as parallel_decode +import einops +import flax.linen as nn +import jax +import jax.numpy as jnp +import numpy as np + + +# Utils to encode and decode images as latents. +def encode_images( # pylint: disable=missing-function-docstring + params, images, *, adaptor, patch_pca, rngs, reparametrize: bool): + # Apply patch_pca module. + x, logvar = patch_pca.apply({}, images, method=patch_pca.encode, rngs=rngs) + if reparametrize: + x = patch_pca.apply( + {}, x, logvar, method=patch_pca.reparametrize, rngs=rngs) + + # Apply invertible network. + if adaptor is not None: + x = unflatten_latents(x) + x, _ = adaptor.apply({"params": params}, x, method=adaptor.forward) + x = flatten_latents(x) + return x + + +def decode_images(params, x, *, adaptor, patch_pca): + # Apply invertible network backwards. + if adaptor is not None: + x = unflatten_latents(x) + x, _ = adaptor.apply({"params": params}, x, method=adaptor.inverse) + x = flatten_latents(x) + + # Apply patch_pca module backwards. + images = patch_pca.apply({"params": {}}, x, method=patch_pca.decode) + return images + + +def unflatten_latents(x): + hw = int(x.shape[1] ** 0.5) + return einops.rearrange(x, "b (h w) c -> b h w c", h=hw, w=hw) + + +def flatten_latents(x): + return einops.rearrange(x, "b h w c -> b (h w) c") + + +# Utils to sample the decoder. +def sample_image_latents( + params, batch, *, model, + decode_len=256, temperature=1.0, temperature_probs=1.0, + cfg_weight=None, rng=None): + """Sample image latents conditioned on text prompt.""" + rng = rng if rng is not None else jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + # The following makes sure to skip CFG if cfg_weight is a Python float or int + # and is 0, or is None, but performs CFG if cfg_weight is a traced array. + if isinstance(cfg_weight, (int, float)): + do_cfg = (cfg_weight != 0) + else: + do_cfg = cfg_weight is not None + logging.info("Sampling with cfg_weight=%r", cfg_weight) + + def _sample_prelogits(model, pre_logits): + rng = model.make_rng("sample") + logits = model.img_logits(pre_logits) + if do_cfg: + # get_pdf is not jax-friendly as it returns an opaque object so we have + # to split logits before calling it. + logits_cond, logits_uncond = einops.rearrange( + logits, "(b s) ... -> s b ...", s=2) + pdf_cond = model.get_pdf(logits_cond, + temperature_scales=temperature, + temperature_probs=temperature_probs) + pdf_uncond = model.get_pdf(logits_uncond, + temperature_scales=temperature, + temperature_probs=temperature_probs) + pdf = parallel_decode.CFGDensity( + pdf_cond, pdf_uncond, + w=cfg_weight, rng=rng) + samples = pdf.sample(seed=rng) + logprobs = pdf.log_prob(samples) + logprobs = jnp.sum(logprobs, axis=2) # [B, N, C] -> [B, N] + return jnp.repeat(samples, 2, axis=0), jnp.repeat(logprobs, 2, axis=0) + else: + pdf = model.get_pdf(logits, + temperature_scales=temperature, + temperature_probs=temperature_probs) + samples = pdf.sample(seed=rng) + logprobs = pdf.log_prob(samples) + return samples, logprobs + + # Main sample logic where "model" has been bound. + def _sample(model): + text = batch["text"] + text_mask = batch.get("text_mask", jnp.full(text.shape, True)) + + # Add unconditional sequences if needed [x, x_uncond, y, y_uncond, ...] + if do_cfg: + drop_prefix = jnp.tile(jnp.array([False, True]), text.shape[0]) + text = jnp.repeat(text, 2, axis=0) + # Overridden to full mask when dropping the label in embed_image_and_text. + text_mask = jnp.repeat(text_mask, 2, axis=0) + else: + drop_prefix = None + + # Prepare inputs to prefill the decoder. Pass images of seq_len=0 so it + # prefills up to the BOI of images. + batch_size, _ = text.shape + images = jnp.zeros((batch_size, 0, model.out_dim)) # zero-len images. + text_first_mask = jnp.full((batch_size,), True) + x, attn_mask, input_mask = model.embed_image_and_text( + text, images, + text_first_mask=text_first_mask, + text_input_mask=text_mask, + drop_prefix=drop_prefix, shift=False) + + # Prefill the decoder cache with 'x' and sample the first output. + cache_size = x.shape[1] + decode_len - 1 + last_prelogits = model.prefill_cache(x, attn_mask, input_mask, + cache_size=cache_size)[:, -1:] + tokens, logp = _sample_prelogits(model, last_prelogits) + + # Init loop state with the token decoded during prefill. + batch_size, _, prelogits_dim = last_prelogits.shape + out_prelogits = jnp.zeros((batch_size, decode_len, prelogits_dim)) + out_logp = jnp.zeros((batch_size, decode_len)) + out_tokens = jnp.zeros((batch_size, decode_len, tokens.shape[2])) + + out_prelogits = out_prelogits.at[:, 0:1].set(last_prelogits) + out_tokens = out_tokens.at[:, 0:1].set(tokens) + out_logp = out_logp.at[:, 0:1].set(logp) + + # Most callers will only need the out_tokens (i.e. the sample latents). + # This code pattern allows one to easily add other outputs if needed. + # last_tokens is a carry variable to avoid a dynamic lookup in the loop. + state = { + "out_prelogits": out_prelogits, # [B, decode_len, D] + "out_tokens": out_tokens, # [B, decode_len, H] + "out_logp": out_logp, # [B, decode_len] + "last_tokens": tokens, # [B, 1, H] + } + + # Loop to decode remaining tokens. This function will be called by nn.scan + # with xs = 1, 2, ..., decode_len and update the state accordingly. + def loop_step(model, state, xs): + x = model.img_emb(state["last_tokens"]) + prelogits = model.extend_cache(x) + tokens, logp = _sample_prelogits(model, prelogits) + # Update state with the new tokens. + state["out_prelogits"] = jax.lax.dynamic_update_slice( + state["out_prelogits"], prelogits, (0, xs, 0)) + state["out_tokens"] = jax.lax.dynamic_update_slice( + state["out_tokens"], tokens, (0, xs, 0)) + state["out_logp"] = jax.lax.dynamic_update_slice( + state["out_logp"], logp, (0, xs)) + state["last_tokens"] = tokens + return state, None + # Note that besides "state", the "cache" variables are also carried and + # that the sample rng state is splitted so each call sees a different rng. + xs = jnp.arange(1, decode_len) + state, _ = nn.scan( + loop_step, variable_broadcast="params", variable_carry="cache", + split_rngs={"sample": True})(model, state, xs) + del state["last_tokens"] # Not needed anymore. + + # Remove unconditional sequences if needed. + if do_cfg: + state = jax.tree.map(lambda x: x[::2], state) + + return state + + out, _ = nn.apply(_sample, model, mutable=["cache"])( + {"params": params}, rngs={"sample": rng}) + + return out + + +def sample_text( + params, batch, *, model, + decode_len=64, temperature=1.0, rng=None): + """Sample text continuation conditioned on image.""" + rng = rng if rng is not None else jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + + def _sample_prelogits(model, pre_logits): + rng = model.make_rng("sample") + logits = model.text_logits(pre_logits) + # Sample using temperature. + # TODO: Add top-k/top-p/etc... + modified_pmf = model.get_pmf(logits / temperature) + samples = modified_pmf.sample(seed=rng) + # Return logp according to the original unmodified distribution. + pmf = model.get_pmf(logits) + logprobs = pmf.log_prob(samples) + return samples, logprobs + + # Main sample logic where "model" has been bound. + def _sample(model): + images = batch["image_latents"] + + # TODO: Add support for CFG. + drop_prefix = None + + # Prepare inputs to prefill the decoder with [image, Optional[text]]. + batch_size, _, _ = images.shape + text_first_mask = jnp.full((batch_size,), False) + + if batch["text"] is None: + # Zero-len text, so it prefills up to the BOS of text. + text = jnp.full((batch_size, 0), 0) + text_input_mask = jnp.full((batch_size, 0), True) + else: + # If text is present, it will prefill the BOS of text and the tokens + # which are true in the text_mask (i.e. each example can have a variable + # number of text tokens prefilled). + text = batch["text"] + text_input_mask = batch["text_mask"] + + x, attn_mask, input_mask = model.embed_image_and_text( + text, images, + text_first_mask=text_first_mask, + text_input_mask=text_input_mask, + drop_prefix=drop_prefix, shift=False) + + # Prefill the decoder cache with 'x' and sample the first output. + cache_size = x.shape[1] + decode_len - 1 + last_prelogits = model.prefill_cache( + x, attn_mask, input_mask, cache_size=cache_size)[:, -1:] + tokens, logp = _sample_prelogits(model, last_prelogits) + + # Init loop state with the token decoded during prefill. + batch_size, _, prelogits_dim = last_prelogits.shape + out_prelogits = jnp.zeros((batch_size, decode_len, prelogits_dim), + dtype=last_prelogits.dtype) + out_logp = jnp.zeros((batch_size, decode_len), dtype=logp.dtype) + out_tokens = jnp.zeros((batch_size, decode_len), dtype=tokens.dtype) + + out_prelogits = out_prelogits.at[:, 0:1].set(last_prelogits) + out_tokens = out_tokens.at[:, 0:1].set(tokens) + out_logp = out_logp.at[:, 0:1].set(logp) + + # Most callers will only need the out_tokens (i.e. the sample latents). + # This code pattern allows one to easily add other outputs if needed. + # last_tokens is a carry variable to avoid a dynamic lookup in the loop. + state = { + "out_prelogits": out_prelogits, # [B, decode_len, D] + "out_tokens": out_tokens, # [B, decode_len] + "out_logp": out_logp, # [B, decode_len] + "last_tokens": tokens, # [B, 1] + } + + # Loop to decode remaining tokens. This function will be called by nn.scan + # with xs = 1, 2, ..., decode_len and update the state accordingly. + def loop_step(model, state, xs): + x = model.text_emb(state["last_tokens"]) + prelogits = model.extend_cache(x) + tokens, logp = _sample_prelogits(model, prelogits) + # Update state with the new tokens. + state["out_prelogits"] = jax.lax.dynamic_update_slice( + state["out_prelogits"], prelogits, (0, xs, 0)) + state["out_tokens"] = jax.lax.dynamic_update_slice( + state["out_tokens"], tokens, (0, xs)) + state["out_logp"] = jax.lax.dynamic_update_slice( + state["out_logp"], logp, (0, xs)) + state["last_tokens"] = tokens + return state, None + # Note that besides "state", the "cache" variables are also carried and + # that the sample rng state is splitted so each call sees a different rng. + xs = jnp.arange(1, decode_len) + state, _ = nn.scan( + loop_step, variable_broadcast="params", variable_carry="cache", + split_rngs={"sample": True})(model, state, xs) + del state["last_tokens"] # Not needed anymore. + + return state + + out, _ = nn.apply(_sample, model, mutable=["cache"])( + {"params": params}, rngs={"sample": rng}) + + return out diff --git a/Tipsomaly/model/big_vision/trainers/proj/jetformer/train.py b/Tipsomaly/model/big_vision/trainers/proj/jetformer/train.py new file mode 100644 index 0000000000000000000000000000000000000000..b4df7e320d88344ddae2b649e8e92dd8056a12ab --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/jetformer/train.py @@ -0,0 +1,887 @@ +# 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 JetFormer.""" +# 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.trainers.proj.jetformer.predict_fns as predict_fns +import big_vision.utils as u +from clu import parameter_overview +import distrax +import flax +import flax.linen as nn +import jax +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 + + # 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")]) + + write_note("Creating device mesh...") + mesh = u.create_device_mesh( + config_mesh, + allow_split_physical_axes=config.get("mesh_allow_split_physical_axes", + False)) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + # 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 = mesh.devices.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 # +# # +################################################################################ + + assert config.patch_pca.model_name == "proj.jetformer.patch_pca", ( + "This trainer only supports proj.jetformer.patch_pca as an embedder." + ) + write_note(f"Creating {config.patch_pca.model_name} model...") + pca_mod = importlib.import_module( + f"big_vision.models.{config.patch_pca.model_name}") + patch_pca = pca_mod.Model(**config.patch_pca.get("model", {})) + + # Apply PCA - does not require any learnable parameters. + def patch_pca_encode(images, rng=None, reparametrize=True): + mu, logvar = patch_pca.apply( + {"params": {}}, images, method=patch_pca.encode, rngs=rng) + if reparametrize: + assert rng is not None and "dropout" in rng + return patch_pca.apply({"params": {}}, mu, logvar, + method=patch_pca.reparametrize, rngs=rng) + return mu + + 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(**config.adaptor.model) + else: + adaptor = None + + def adaptor_apply(params, sequence, inverse=False): + # Apply NVP and ensure compatible input/output format. + sequence = predict_fns.unflatten_latents(sequence) + assert hasattr(adaptor, "forward") and hasattr(adaptor, "inverse") + sequence, sum_log_det = adaptor.apply( + {"params": params}, sequence, + method=adaptor.inverse if inverse else adaptor.forward) + sequence = predict_fns.flatten_latents(sequence) + return sequence, sum_log_det + + def _maybe_remove_latent_noise_dims(image_tokens): + if (noise_dim := config.get("latent_noise_dim", 0)) > 0: + image_tokens = image_tokens[..., :-noise_dim] + assert image_tokens.shape[-1] == model.out_dim + return image_tokens + + def init(rng, batch=None): + # TODO: Update init function with new arguments + def _get_dummy_input(input_name, dtype=jnp.int64): + if batch is not None: + return batch.get(input_name) + elif input_name in train_ds.element_spec: + return jnp.zeros(train_ds.element_spec[input_name].shape, dtype=dtype) + return None + + images = _get_dummy_input("image", dtype=jnp.float32) + text = _get_dummy_input("text") + assert images is not None and text is not None + + image_tokens = patch_pca_encode(images, rng={"dropout": rng}) + + if adaptor is not None: + rng, rng_adaptor = jax.random.split(rng) + image_tokens = predict_fns.unflatten_latents(image_tokens) + (image_tokens, _), adaptor_variables = adaptor.init_with_output( + rng_adaptor, image_tokens, method=adaptor.forward) + params_adaptor = flax.core.unfreeze(adaptor_variables["params"]) + image_tokens = predict_fns.flatten_latents(image_tokens) + else: + params_adaptor = {} + + image_tokens = _maybe_remove_latent_noise_dims(image_tokens) + + text_first = jnp.full(images.shape[0], 0) + params = model.init(rng, text, image_tokens, + text_input_mask=_get_dummy_input("text_mask"), + text_first_mask=text_first)["params"] + params["params_adaptor"] = params_adaptor + 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("Inferring shardings...") + train_state_shape = {"params": params_shape, "opt": opt_shape} + + if config.get("ema_decay", 0.0) > 0.0: + write_note(f"Tracking parameter EMA with decay {config.ema_decay}.") + train_state_shape["params_ema"] = params_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. + first_batch = next(train_iter) + params = jax.jit(init, out_shardings=train_state_sharding["params"])( + rng_init, first_batch) + 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. + + train_state = {"params": params, "opt": opt} + if config.get("ema_decay", 0.0) > 0.0: + # Copy model parameters for EMA + train_state["params_ema"] = jax.tree.map(jnp.array, train_state["params"]) + + # 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(train_state) + 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, batch, rng, noise_scale=None, train=True): + text, images = batch["text"], batch["image"] + text_mask, text_loss = batch["text_mask"], batch["text_loss"] + + rng, rng_dropout, rng_order, rng_droplabels, rng_noise = ( + jax.random.split(rng, 5)) + + rng_dropout = {"dropout": rng_dropout} + + batch_size = images.shape[0] + # 0 -> image first, 1 -> text first + text_first_mask = jax.random.bernoulli( + rng_order, config.get("text_prefix_prob", 0.5), (batch_size,)) + + if noise_scale is not None: + # Maybe skip noise on image prefix. + if not config.get("rgb_noise_on_image_prefix", True): + noise_scale = jnp.where(text_first_mask, noise_scale, 0.0) + noise_scale = noise_scale[:, None, None, None] # [bs, h, w, 3] + # Convert images to [0, 255] add noise with std scale round to int and + # convert back to [-1, 1]. This way it is as if the input was added + # to the uint8 images in the preprocessing before the value_range(-1, 1). + images = jnp.round((images+1)*127.5) + images = images + noise_scale * jax.random.normal(rng_noise, images.shape) + images = jnp.round(images) + images = images/127.5 - 1 + + image_tokens = patch_pca_encode(images, rng_dropout) + if adaptor is not None: + # Use the (invertible) adaptor to map to a new latent sequence + image_tokens, sum_log_det = adaptor_apply( + params["params_adaptor"], image_tokens) + else: + sum_log_det = jnp.zeros((images.shape[0],),) + + if (noise_dim := config.get("latent_noise_dim", 0)) > 0: + # Mapping the last noise_dim dimensions to a standard normal prior. + assert model.out_dim + noise_dim == image_tokens.shape[-1] + image_tokens, noise = jnp.split(image_tokens, [model.out_dim], axis=-1) + noise_pdf = distrax.Normal(0.0, 1.0) + noise_nll = -noise_pdf.log_prob(noise).sum(axis=(1, 2)) + else: + noise_nll = 0.0 + + if train and (input_noise_std := config.get("input_noise_std", 0.0)) > 0.0: + # Add noise on the input during teacher forcing to make autoregressive + # sampling more robust: Sample a noise std uniformly at random per example + # and add Gaussian noise with that std to the input. + _, rng_std, rng_input_noise = jax.random.split(rng, 3) + sampled_input_noise_std = jax.random.uniform( + rng_std, (batch_size, 1, 1), minval=0.0, maxval=input_noise_std) + # Only apply noise for image generation (i.e. when text is first). + sampled_input_noise_std = jnp.where( + text_first_mask[:, None, None], sampled_input_noise_std, 0.0) + image_tokens = image_tokens + ( + sampled_input_noise_std + * jax.random.normal(rng_input_noise, image_tokens.shape)) + + # TODO: Do cfg for text and don't apply prefix loss when dropped. + # For now only drop when text is first. + if train: + drop_prefix = model.get_drop_labels(rng_droplabels, batch_size=batch_size) + else: + drop_prefix = None + if drop_prefix is None: + drop_prefix = jnp.full((batch_size,), False) + drop_prefix = drop_prefix & text_first_mask + + # Stop gradients to NVP when it is used as an encoder to get an image prefix + if config.get("stop_grad_nvp_prefix", False): + image_tokens = jnp.where( + text_first_mask[:, None, None], + image_tokens, + jax.lax.stop_gradient(image_tokens) + ) + + *_, pmf, pdf, _ = model.apply( + {"params": params}, + text, + image_tokens, + train=train, + text_first_mask=text_first_mask, + text_input_mask=text_mask, + drop_prefix=drop_prefix, + rngs=rng_dropout) + + def _log_prob(value): + # Re-implementing distrax.Categorical.log_prob() without logic to ignore + # NaNs, which is not required and seems to produce a compilation error + # with recent jax versions. + value_one_hot = jax.nn.one_hot( + value, pmf.num_categories, dtype=pmf.logits.dtype) + mask_outside_domain = jnp.logical_or( + value < 0, value > pmf.num_categories - 1) + return jnp.where( + mask_outside_domain, -jnp.inf, + jnp.sum(pmf.logits * value_one_hot, axis=-1)) + nll_txt = -_log_prob(text) # [BS, TXT_LEN] + nll_txt = jnp.mean(nll_txt, axis=1, where=text_loss) + + # Report image related loss in log2/subpixels (bits per subpixels). + # When using PCA this value is off, as it does not accounts for logdet + # of PCA and ignores the perplexity of dropped PCA components. + num_subpixels = np.prod(images.shape[1:]) # H*W*C + nll_image_tokens = -pdf.log_prob(image_tokens) # [BS, IMG_LEN] + nll_image_tokens = ( + jnp.sum(nll_image_tokens, axis=1) + noise_nll) / num_subpixels + nll_image_tokens /= jnp.log(2) + # Convert logdet sum to be per subpixel and account for conversion + # [0, 255]->[-1, 1] (i.e. by divide by 127.5). + logdet = sum_log_det / num_subpixels - jnp.log(127.5) + logdet /= jnp.log(2) + nll_image = nll_image_tokens - logdet + + def mean(x, where=None): + if valid_example_mask := batch.get("_mask", None) is not None: + if where is not None: + where = where & valid_example_mask + else: + where = valid_example_mask + return jnp.mean(x, where=where) + + metrics = { + "nll_text_prefix": mean( + nll_txt, where=text_first_mask & ~drop_prefix), + "nll_text_suffix": mean(nll_txt, where=~text_first_mask), + # Currently, we never drop the image prefix, but we already consider + # this case here. + "nll_image_prefix": mean( + nll_image, where=~text_first_mask & ~drop_prefix), + "nll_image_suffix": mean(nll_image, where=text_first_mask), + } + + text_w = config.get("text_loss_weight", 1.0) + if config.get("loss_on_prefix", True): + valid_txt_nll = (text_first_mask & ~drop_prefix) | ~text_first_mask + valid_img_nll = (~text_first_mask & ~drop_prefix) | text_first_mask + metrics.update({ + "nll_text": mean(nll_txt, where=valid_txt_nll), + "nll_image": mean(nll_image, where=valid_img_nll), + "logdet": mean(logdet), + }) + loss = (mean(nll_txt, where=valid_txt_nll) * text_w + + mean(nll_image, where=valid_img_nll)) + else: + text_suffix = ~text_first_mask + image_suffix = text_first_mask + metrics.update({ + "nll_text": mean(nll_txt, where=text_suffix), + "nll_image": mean(nll_image, where=image_suffix), + "nll_image_tokens": mean(nll_image_tokens, where=image_suffix), + "logdet": mean(logdet, where=image_suffix), + }) + example_loss = jnp.where(text_suffix, nll_txt*text_w, nll_image) + loss = mean(example_loss) + + metrics["loss"] = loss + 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.""" + step_count = bv_optax.get_count(train_state["opt"], jittable=True) + rng = jax.random.fold_in(rng, step_count) + + measurements = {} + progress = step_count / total_steps + + if config.get("noise_scale", 0.0) > 0.0: + noise_min = config.get("noise_min", 0.0) + noise_scale = ((config.noise_scale - noise_min) + * (1+jnp.cos(jnp.pi*progress)) * 0.5) + noise_min + measurements["noise_scale"] = noise_scale + else: + noise_scale = None + + # 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, batch, rng_model, noise_scale=noise_scale) + updates, opt = tx.update(grads, opt, params) + params = optax.apply_updates(params, updates) + new_train_state = {"params": params, "opt": opt} + # Update EMA parameters. + if (ema_decay := config.get("ema_decay", 0.0)) > 0.0: + new_params_ema = jax.tree.map( + lambda pe, p: ema_decay * pe + (1 - ema_decay) * p, + train_state["params_ema"], params) + new_train_state["params_ema"] = new_params_ema + + 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 new_train_state, 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 + 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 # +# # +################################################################################ + + def validation_fn(train_state, batch, *, use_ema=False): + params = train_state["params_ema"] if use_ema else train_state["params"] + rng = jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + + _, aux = loss_fn(params, batch, rng, train=False) + # The metrics produced by loss_fn may already be averaged over the batch, + # since some of them only apply for certain batches. Here we broadcast the + # metrics across the batch dimension, which might introduce some errors when + # the batch size is not divisible by the number of devices. + aux = jax.tree.map( + lambda x: jnp.broadcast_to(x, batch["text"].shape[:1]), aux) + return aux + + def sample_images_fn(train_state, batch, *, decode_len=256, use_ema=False): + params = train_state["params_ema"] if use_ema else train_state["params"] + cfg_weight = config.sample_images.get("cfg_inference_weight", 0.0) + temperature = config.sample_images.get("temperature", 1.0) + temperature_probs = config.sample_images.get("temperature_probs", 1.0) + + if batch["text"].ndim < 2: + batch["text"] = batch["text"][:, None] + + out = predict_fns.sample_image_latents( + params, batch, model=model, decode_len=decode_len, + cfg_weight=cfg_weight, temperature=temperature, + temperature_probs=temperature_probs) + + image_tokens = out["out_tokens"] + if (noise_dim := config.get("latent_noise_dim", 0)) > 0: + rng = jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + noise = jax.random.normal(rng, image_tokens.shape[:-1] + (noise_dim,)) + image_tokens = jnp.concatenate([image_tokens, noise], axis=-1) + + images = predict_fns.decode_images( + params["params_adaptor"], image_tokens, + adaptor=adaptor, patch_pca=patch_pca) + out["logits"] = images + return out + + def sample_text_fn(params, batch, *, temperature, decode_len): + """Jittable sampling of text.""" + image_latents = predict_fns.encode_images( + params["params_adaptor"], batch["image"], + adaptor=adaptor, patch_pca=patch_pca, + rngs={"dropout": jax.random.key(0)}, + reparametrize=False) + + image_latents = _maybe_remove_latent_noise_dims(image_latents) + + out = predict_fns.sample_text( + params, {"image_latents": image_latents, **batch}, model=model, + temperature=temperature, decode_len=decode_len) + return out["out_tokens"] + + def sample_text(train_state, batch, *, + use_ema=False, decode_len, temperature=1e-5, devices, + eos_token=None): + """Predict fn that does the jitting of sample_text_fn for evaluators.""" + del eos_token # Unused. We always sample decode_len tokens. + params = train_state["params_ema"] if use_ema else train_state["params"] + mesh = jax.sharding.Mesh(devices, ("devices",)) + data_sharding = jax.sharding.NamedSharding(mesh, P("devices")) + new_batch = { + "image": batch["image"], + "text": batch.get("text", None), + "text_mask": batch.get("text_mask", None), + } + tokens = jax.jit(sample_text_fn, out_shardings=data_sharding, + static_argnames=("decode_len", "temperature"))( + params, new_batch, + decode_len=decode_len, temperature=temperature) + return tokens + + def score_captions_fn(train_state, batch, *, use_ema=False): + # TODO: Enable caching of the prefix to speed up evaluation. + params = train_state["params_ema"] if use_ema else train_state["params"] + images = batch["image"] + all_labels = batch["_label_tokens"] + all_labels_mask = batch["_label_masks"] + all_loss_masks = batch["_loss_masks"] + batch_size = images.shape[0] + + rng = jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + rng_dropout = {"dropout": rng} + image_tokens = patch_pca_encode(images, rng_dropout) + + if adaptor is not None: + image_tokens, _ = adaptor_apply(params["params_adaptor"], image_tokens) + + image_tokens = _maybe_remove_latent_noise_dims(image_tokens) + + def _score_label(label_and_mask): + label, mask, loss_mask = label_and_mask + label_rep = jnp.tile(label, (batch_size, 1)) + masks_rep = jnp.tile(mask, (batch_size, 1)) + loss_masks_rep = jnp.tile(loss_mask, (batch_size, 1)) + _, _, pmf, *_ = model.apply( + {"params": params}, + text_tokens=label_rep, + image_tokens=image_tokens, + text_first_mask=jnp.full((batch_size,), False), # images always first + text_input_mask=masks_rep, + ) + return jnp.sum(pmf.log_prob(label_rep), axis=-1, where=loss_masks_rep) + + ll_labels = jax.lax.map( + _score_label, (all_labels, all_labels_mask, all_loss_masks)) + return ll_labels.T + + def image_rep_fn(train_state, batch, *, use_ema=False): + params = train_state["params_ema"] if use_ema else train_state["params"] + images = batch["image"] + + rng = jax.random.PRNGKey( + jax.lax.rng_uniform(0, np.iinfo(np.int32).max, tuple())) + rng_dropout = {"dropout": rng} + image_tokens = patch_pca_encode(images, rng_dropout) + + out = {"patch_emb": image_tokens} + if adaptor is not None: + image_tokens, _ = adaptor_apply(params["params_adaptor"], image_tokens) + out["nvp"] = image_tokens + + image_tokens = _maybe_remove_latent_noise_dims(image_tokens) + + # TODO: Allow the code to be called without text tokens and think + # better what representations to use here... For now its the representation + # obtained by feeding the 257 tokens [BOI, image_tokens] to the model. The + # text tokens are not used. It is only [BOS] but since its the last token in + # the input it is not feed to the model (dropped by shift). + *_, decoder_out = model.apply( + {"params": params}, + text_tokens=jnp.full((batch_size, 0,), 0), # [BS, 0]: empty text. + image_tokens=image_tokens, + text_first_mask=jnp.full((batch_size,), False), # images always first + ) + out.update(decoder_out) + + # Average pool intermediate representations. + out = jax.tree.map(lambda x: x.mean(axis=-2), out) + + return out["pre_logits"], out + + # 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_images": sample_images_fn, + "sample_text": sample_text, + "score_captions": score_captions_fn, + "image_representation": image_rep_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. + # 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/Tipsomaly/model/big_vision/trainers/proj/paligemma/predict_fns.py b/Tipsomaly/model/big_vision/trainers/proj/paligemma/predict_fns.py new file mode 100644 index 0000000000000000000000000000000000000000..f048d3f46fe11c3c7f808bcdb6d6cedde7516696 --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/paligemma/predict_fns.py @@ -0,0 +1,486 @@ +# 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", eos_look_behind=0): + """Sample token continuations to the input sequences.""" + mesh = jax.sharding.Mesh(devices, ("devices",)) + replicate_sharding = jax.sharding.NamedSharding(mesh, P()) + bs_shardable = len(batch["image"]) % jax.device_count() == 0 + out_sharding = jax.sharding.NamedSharding( + mesh, P("devices") if bs_shardable else P() + ) + + # Prefill the model cache and generate logits for first token. + logits, cache = jax.jit( + _prefill_cache, + out_shardings=(None, 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"] + + # Mask indicating tokens for which the logits will be set to -Inf. Can be a + # Boolean mask or indices. + tok_mask = batch.get("mask_logits", None) + + # 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, + out_shardings=(None, out_sharding), + 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, tok_mask, 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, tok_mask, *, 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, + tok_mask=tok_mask) + + # 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, tok_mask=None): + """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())) + + masked_logits = logits + if tok_mask is not None: + masked_logits = masked_logits.at[..., tok_mask].set(-jnp.inf) + + # Use Registry to support specifying things like: + # "greedy", "nucleus(0.2)", "temperature(t=1.0)" + sampled_tokens = registry.Registry.lookup("paligemma_sampler." + sampler)( + logits=masked_logits, rng=rng) + + # Find the log probability (normalized logits) of selected tokens. + # NOTE: If you use tok_mask this returns the probability of the tokens while + # ignoring the masking. This is useful for pix2seq-style which has tokens like + # "noise" which it does not want to sample but it wants to use it to affect + # the score/logp of classes being sampled and it wants to intrepet as a + # confidence. + 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): + """Beam search (greedy/top-k exploration).""" + mesh = jax.sharding.Mesh(devices, ("devices",)) + replicate_sharding = jax.sharding.NamedSharding(mesh, P()) + bs_shardable = len(batch["image"]) % jax.device_count() == 0 + out_sharding = jax.sharding.NamedSharding( + mesh, P("devices") if bs_shardable else P() + ) + + # Prefill the model cache and generate logits for first token. + logits, cache = jax.jit( + _prefill_cache, + out_shardings=(None, 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, + donate_argnums=2, + out_shardings=(None, None, out_sharding), + 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, + out_shardings=(None, out_sharding), + 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/Tipsomaly/model/big_vision/trainers/proj/paligemma/run.py b/Tipsomaly/model/big_vision/trainers/proj/paligemma/run.py new file mode 100644 index 0000000000000000000000000000000000000000..d23b3be049755f20fced40b5c1a80642309bc768 --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/paligemma/run.py @@ -0,0 +1,153 @@ +# 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.") +CKPT_DTYPE = flags.DEFINE_string( + "ckpt_dtype", default=None, + help="Convert ckpt to dtype before using it (e.g. float16).") + +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 cast_params(params): + return jax.tree.map(lambda x: x.astype(CKPT_DTYPE.value) if np.issubdtype(x.dtype, np.floating) else x, params) + + +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) + if CKPT_DTYPE.value: + # Note: if running out of HBM or memory consider convert the checkpoint + # ahead of time, or add feature to cast while loading. + params = jax.jit(cast_params, donate_argnums=(0,), + out_shardings=params_sharding)(params) + + # 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/Tipsomaly/model/big_vision/trainers/proj/paligemma/train.py b/Tipsomaly/model/big_vision/trainers/proj/paligemma/train.py new file mode 100644 index 0000000000000000000000000000000000000000..c5a32b2a312e14f1a84d03cfbf2d67b3168e02fd --- /dev/null +++ b/Tipsomaly/model/big_vision/trainers/proj/paligemma/train.py @@ -0,0 +1,521 @@ +# 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 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") +# 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") + +################################################################################ +# # +# 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") + logging.info(f"The config:\n{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.bv") + + # The pool is used to perform misc operations such as logging in async way. + pool = multiprocessing.pool.ThreadPool(1) + + # 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")]) + + write_note("Creating device mesh...") + mesh = u.create_device_mesh( + config_mesh, + allow_split_physical_axes=config.get("mesh_allow_split_physical_axes", + False)) + repl_sharding = jax.sharding.NamedSharding(mesh, P()) + + # 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 = mesh.devices.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("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_grads", "l2_updates", "l2_params"): + 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/Tipsomaly/model/big_vision/trainers/proj/uvim/coco_utils.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/coco_utils.py new file mode 100644 index 0000000000000000000000000000000000000000..70183030ac9d441e0e9b49007be7775b16cab233 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/uvim/colorization_task.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/colorization_task.py new file mode 100644 index 0000000000000000000000000000000000000000..1624e1b2d3465f65f69d0634a3268d682a1c1ccc --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/uvim/depth_task.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/depth_task.py new file mode 100644 index 0000000000000000000000000000000000000000..e4768b769e586da2397e56d6312e61268e479830 --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/uvim/panoptic_task.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/panoptic_task.py new file mode 100644 index 0000000000000000000000000000000000000000..4049c496252105ff283e8f0120dbce06fb24ef2b --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/uvim/train.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/train.py new file mode 100644 index 0000000000000000000000000000000000000000..bbaec203f748197ca7be2fd76a0c8c4aa58c804a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/trainers/proj/uvim/vqvae.py b/Tipsomaly/model/big_vision/trainers/proj/uvim/vqvae.py new file mode 100644 index 0000000000000000000000000000000000000000..13467d02ca3c95c4bd8c9a38b9970db24c0f4e3a --- /dev/null +++ b/Tipsomaly/model/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/Tipsomaly/model/big_vision/utils.py b/Tipsomaly/model/big_vision/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..a954300f9ac7e4ccce27c38eb1367d3233451532 --- /dev/null +++ b/Tipsomaly/model/big_vision/utils.py @@ -0,0 +1,1478 @@ +# 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 import mesh_utils +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/.*" + assert "regex" not in kw, "For a custom regex, use tsload directly." + kw["regex"] = regex + checkpoint = load_checkpoint_ts(ckpt, **kw) + 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) + + log_memory(measure) + + 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 log_memory(measure): + """Log a bunch of memory-related measurements.""" + try: + import psutil + except ImportError: + psutil = None + + if psutil is not None: + # Note that total != available + used, see psutil docs. + vmem = psutil.virtual_memory() + measure("y/hostmem/total", vmem.total) + measure("y/hostmem/available", vmem.available) + measure("y/hostmem/used", vmem.used) + + # We show only device 0 and 1 to avoid spam. The reason to show two and not + # just one, if multiple are available, is because a frequent mistake is to + # create arrays on the default device, which is device 0. + for i, d in zip([0, 1], jax.local_devices()): + for k, v in (d.memory_stats() or {}).items(): + measure(f"y/devmem/dev{i}/{k}", v) + + +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, concurrent_gb=64) + 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 (x := config.get(f"{prefix}_{s}")) is not None and x >= 0 + } + # 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 matches: + 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 matches: + return to_integer(config[f"{prefix}_examples"] / batch_size) + + if batch_size and data_size and f"{prefix}_epochs" in matches: + 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 matches: + 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}. + """ + + def to_steps(name, default=0): + return steps(name, kw, data_size, batch_size, total_steps, default=default) + + warmup_steps = to_steps("warmup") + cooldown_steps = to_steps("cooldown") + + # 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. + t = to_steps("timescale", default=kw.get("timescale", 10_000)) + shift = to_steps("shift", default=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=10, **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 + + +def create_device_mesh( + config_mesh, + *, + allow_split_physical_axes=False, +): + """Returns a JAX device mesh. + + Args: + config_mesh: A list of tuples of (axis_name, axis_size). It is advised to + sort the axis in increasing order of network communication intensity. + allow_split_physical_axes: Whether to allow splitting physical axes. + """ + devices = jax.devices() + mesh_axes, mesh_size = tuple(zip(*config_mesh)) + # Because jax.utils do not support `-1` shape size. + mesh_size = np.array(devices).reshape(mesh_size).shape + device_mesh = mesh_utils.create_device_mesh( + mesh_size, + devices=devices, + allow_split_physical_axes=allow_split_physical_axes) + return jax.sharding.Mesh(device_mesh, mesh_axes) diff --git a/Tipsomaly/model/big_vision/utils_test.py b/Tipsomaly/model/big_vision/utils_test.py new file mode 100644 index 0000000000000000000000000000000000000000..20680ee689262ef10e7a69d0446cc35f0fcf08bf --- /dev/null +++ b/Tipsomaly/model/big_vision/utils_test.py @@ -0,0 +1,360 @@ +# 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 = jnp.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 = jnp.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 = jnp.arange(bs, dtype=dtype) + y = add(x, dtype(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 = jnp.arange(bs, dtype=dtype) + y = add(dtype(5), x, b=dtype(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), + ('ignore_neg', 1000, 100, 10, dict(foo_steps=-1, foo_epochs=-1, + foo_examples=-1, foo_percent=0.30), 3), + ('zero_steps', None, None, 10, dict(foo_percent=0.0), 0), + ('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/Tipsomaly/model/omaly/__init__.py b/Tipsomaly/model/omaly/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..de075260489de44fed55ea58d030eede643fc0f6 --- /dev/null +++ b/Tipsomaly/model/omaly/__init__.py @@ -0,0 +1,2 @@ +from .text_encoder import text_encoder +from .vision_encoder import vision_encoder diff --git a/Tipsomaly/model/omaly/fixed_prompts.py b/Tipsomaly/model/omaly/fixed_prompts.py new file mode 100644 index 0000000000000000000000000000000000000000..565b78d0a6477aa9c271f5e4ebfc4b90accba242 --- /dev/null +++ b/Tipsomaly/model/omaly/fixed_prompts.py @@ -0,0 +1,54 @@ +def generate_prompt_templates(prompt_type): + if prompt_type == 'medical': + prompt_normal = [ + 'normal {}', + 'intact {}', + '{} with uniform structure', + '{} showing clear tissue', + '{} with normal anatomy', + '{} showing no distortion', + '{} with symmetric appearance', + '{} looking normal', + '{} with even texture', + '{} with regular shape', + ] + prompt_abnormal = [ + 'abnormal {}', + '{} with spot', + '{} with abnormality', + 'diseased {}', + '{} showing distortion', + '{} with irregular area', + '{} with irregular shape', + '{} with uneven texture', + ] + # Prompt templates adapted for medical images + prompt_templates = [ + 'a medical image of a {}.', + 'a medical image of the {}.', + 'a diagnostic scan of a {}.', + 'a diagnostic scan of the {}.', + 'a slice showing a {}.', + 'a slice showing the {}.', + 'a scan of the {}.', + 'a clinical brain scan of a {}.' + ] + + elif prompt_type == 'object_agnostic': + prompt_normal = ['{}'] + # prompt_normal = ['normal {}'] + prompt_abnormal = ['damaged {}'] + prompt_templates = ['{}'] + # prompt_templates = ['a photo of a {}'] + + elif prompt_type == 'industrial': + prompt_normal = ['{}', 'flawless {}', 'perfect {}', 'unblemished {}', '{} without flaw', + '{} without defect', + '{} without damage'] + prompt_abnormal = ['damaged {}', 'broken {}', '{} with flaw', '{} with defect', '{} with damage'] + prompt_templates = ['a bad photo of a {}.', + 'a low resolution photo of the {}.', + 'a bad photo of the {}.', + 'a cropped photo of the {}.', + ] + return prompt_normal, prompt_abnormal, prompt_templates diff --git a/Tipsomaly/model/omaly/text_encoder.py b/Tipsomaly/model/omaly/text_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..2c2ae0f924b29522dd2387f4835d30c521060299 --- /dev/null +++ b/Tipsomaly/model/omaly/text_encoder.py @@ -0,0 +1,164 @@ +import torch +import torch.nn as nn +from torch.nn import functional as F + +# NOTE: Since tips is inside model/, your import in model/omaly/text_encoder.py should be: +from model.tips.text_encoder import TextEncoder as BaseTextEncoder +from .fixed_prompts import generate_prompt_templates +import jax.numpy as jnp +import numpy as np +from tqdm import tqdm +import math + +def jax_to_torch(x): + return torch.from_numpy(np.array(x)) + +class text_encoder(nn.Module): + def __init__(self, tokenizer, bb_text_encoder, bb_type, text_embd_dim, MAX_LEN, prompt_learn_method='none', prompt_type='industrial', n_prompt=8, n_deep=0, d_deep=0): + super(text_encoder, self).__init__() + self.tokenizer = tokenizer + self._encoder = bb_text_encoder + self.model = bb_type + self.MAX_LEN = MAX_LEN + self.prompt_learn_method = prompt_learn_method + self.n_deep_tokens = n_deep + self.d_deep_tokens = d_deep + self.n_prompt = n_prompt + self.prompt_type = prompt_type + self.text_embd_dim = text_embd_dim + + self.prompt_normal, self.prompt_abnormal, self.prompt_templates = generate_prompt_templates(self.prompt_type) + + self.prompt_state = [self.prompt_normal, self.prompt_abnormal] + + if self.n_deep_tokens > 0 and self.d_deep_tokens > 0: + self.deep_parameters = torch.nn.ParameterList([torch.nn.Parameter(\ + torch.randn(self.n_deep_tokens, text_embd_dim) * 0.02) \ + for _ in range(self.d_deep_tokens)]) + else: + self.deep_parameters = None + + if not self.prompt_learn_method == 'none': + self.normal_prompt = torch.nn.Parameter(torch.randn(self.n_prompt, text_embd_dim) * 0.02) # Learnable prompt for normal text description with std of 0.02 + self.abnormal_prompt = torch.nn.Parameter(torch.randn(self.n_prompt, text_embd_dim) * 0.02) # Learnable prompt for abnormal text description with std of 0.02 + self.learnable_prompts = torch.nn.ParameterList([self.normal_prompt, self.abnormal_prompt]) + + + def forward(self, texts, device, learned=False): + text_feature_list = [] + + for indx, text in enumerate(texts): + text_features = self.encode_text(text, device, learned) + text_feature_list.append(text_features) + + text_features = torch.stack(text_feature_list, dim=0) + text_features = F.normalize(text_features, dim=2) + return text_features + + def encode_text(self, text, device, learned=False): + text_features = [] + for i in range(len(self.prompt_state)): + learnables = self.learnable_prompts[i] if learned and not self.prompt_learn_method == 'none' else None + deep_parameters = self.deep_parameters if learned else None + + text = text.replace('-', ' ') + prompted_state = [state.format(text) for state in self.prompt_state[i]] + prompted_sentence = [] + for s in prompted_state: + for template in self.prompt_templates: + prompted_sentence.append(template.format(s)) + + if self.model == 'tips': + # NOTE: replace the class based prompt learning concatenated to the templates with only 2 sentences + text_ids, text_paddings = self.tokenizer.tokenize(prompted_sentence, max_len=self.MAX_LEN) + class_embeddings = self._encoder(text_ids.to(device), text_paddings.to(device), learnables, self.prompt_learn_method, deep_parameters, device) + + # NOTE: Avoid in-place /=, +=, _add() on tensor which are actively gradiented + class_embeddings = class_embeddings / class_embeddings.norm(dim=-1, keepdim=True).clamp(min=1e-3) + class_embedding = class_embeddings.mean(dim=0) + class_embedding = class_embedding / class_embedding.norm(dim=-1, keepdim=True) + + elif self.model == 'siglip2': + num_prompts = len(prompted_sentence) + if num_prompts == 0: + class_embedding = torch.zeros(getattr(self, "expected_text_dim", 512), device=device) + text_features.append(class_embedding) + continue + + batch_size = 20 + batch_ranges = range(0, num_prompts, batch_size) + total_batches = math.ceil(num_prompts / batch_size) + + # accumulator for sum in JAX (None -> initialize on first batch) + sum_jax = None + + # optional tqdm over batches + batch_iter = tqdm(batch_ranges, total=total_batches, + desc=f"Encoding text {i+1}/{len(self.prompt_state)} ({self.model})", + leave=False) + + for start in batch_iter: + batch_sentences = prompted_sentence[start:start + batch_size] + # tokenizer expects a list of strings (batch) + txts = self.tokenizer(batch_sentences) + + # encoder returns jax arrays; ztxt shape -> (batch, dim) + _, ztxt, out = self._encoder(txts) + + # sum along batch axis in JAX to accumulate raw (unnormalized) vectors + batch_sum = jnp.sum(ztxt, axis=0) # shape (dim,) + + if sum_jax is None: + sum_jax = batch_sum + else: + sum_jax = sum_jax + batch_sum + + # now compute mean in JAX + mean_jax = sum_jax / float(num_prompts) + mean_jax = mean_jax / (jnp.linalg.norm(mean_jax, axis=-1, keepdims=True) + 1e-8) + + # convert final normalized mean to torch once + class_embedding = jax_to_torch(mean_jax) # shape: (dim,) + elif self.model == 'siglip2-hf': + ids = self.tokenizer(text=prompted_sentence, padding="max_length", max_length=self.MAX_LEN, return_tensors="pt") + ztxt = self._encoder(ids['input_ids'].to(device), learnable_prompts=learnables, learning_method=self.prompt_learn_method).pooler_output + ztxt = ztxt / ztxt.norm(dim=-1, keepdim=True).clamp(min=1e-3) + ztxt = ztxt.mean(dim=0) + class_embedding = ztxt / ztxt.norm(dim=-1, keepdim=True) + # elif self.model == 'siglip2-hf': # NOTE: Use this block if you lack enough memory for encoding text input + # num_prompts = len(prompted_sentence) + # if num_prompts == 0: + # class_embedding = torch.zeros(getattr(self, "expected_text_dim", 512), device=device) + # text_features.append(class_embedding) + # continue + + # batch_size = 20 + # batch_ranges = range(0, num_prompts, batch_size) + # total_batches = math.ceil(num_prompts / batch_size) + + # sum_torch = None + + # # optional tqdm over batches + # batch_iter = tqdm(batch_ranges, total=total_batches, + # desc=f"Encoding text {i+1}/{len(self.prompt_state)} ({self.model})", + # leave=False) + + # for start in batch_iter: + # batch_sentences = prompted_sentence[start:start + batch_size] + # ids = self.tokenizer(text=batch_sentences, padding="max_length", max_length=self.MAX_LEN, return_tensors="pt") + # ztxt = self._encoder(ids['input_ids'].to(device), learnable_prompts=learnables, learning_method=self.prompt_learn_method).pooler_output + # batch_sum = torch.sum(ztxt, dim=0) # shape (dim,) + + # if sum_torch is None: + # sum_torch = batch_sum + # else: + # sum_torch = sum_torch + batch_sum + + # mean_torch = sum_torch / float(num_prompts) + # mean_torch = mean_torch / (torch.norm(mean_torch, dim=-1, keepdim=True) + 1e-8) + # class_embedding = mean_torch + + text_features.append(class_embedding) + + text_features = torch.stack(text_features, dim=0) + return text_features \ No newline at end of file diff --git a/Tipsomaly/model/omaly/vision_encoder.py b/Tipsomaly/model/omaly/vision_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..547337ab183a18f431602a70b8e5974c6e2afce6 --- /dev/null +++ b/Tipsomaly/model/omaly/vision_encoder.py @@ -0,0 +1,48 @@ +import torch +import torch.nn as nn +from torch.nn import functional as F +import jax.numpy as jnp +import numpy as np + +def jax_to_torch(x): + return torch.from_numpy(np.array(x)) + +# The backbone can be SigLIP2 or TIPS +class vision_encoder(nn.Module): + def __init__(self, bb_vision_encoder, bb_type): + super(vision_encoder, self).__init__() + self._encoder = bb_vision_encoder + self.model = bb_type + + def _forward_tips(_encoder, images): + outputs = _encoder(images) + + first_cls_token = outputs[0] + first_cls_token = first_cls_token / first_cls_token.norm(dim=-1, keepdim=True).clamp(min=1e-3) + + second_cls_token = outputs[1] + second_cls_token = second_cls_token / second_cls_token.norm(dim=-1, keepdim=True).clamp(min=1e-3) + + spatial_tokens = outputs[2] + spatial_tokens = spatial_tokens / spatial_tokens.norm(dim=-1, keepdim=True).clamp(min=1e-3) + + return first_cls_token, second_cls_token, spatial_tokens + + def _forward_siglip2(_encoder, images): + _, _, out = _encoder(images) + return jax_to_torch(out['img/normalized']), jax_to_torch(out['img/normalized']), jax_to_torch(out['img/2d_normalized']) + + def _forward_siglip2_hf(_encoder, images): + image_features = _encoder(images) + return image_features.pooler_output, image_features.pooler_output, image_features.last_hidden_state + + def forward(self, images): + if self.model == 'siglip2': + return vision_encoder._forward_siglip2(self._encoder, images) + elif self.model == 'tips': + return vision_encoder._forward_tips(self._encoder, images) + elif self.model == "siglip2-hf": + return vision_encoder._forward_siglip2_hf(self._encoder, images) + + # outputs /= outputs.norm(dim=-1, keepdim=True).clamp(min=1e-3) + # first_cls_token, second_cls_token, spatial_tokens = outputs[:, 0], outputs[:, 1], outputs[:, 2:] \ No newline at end of file diff --git a/Tipsomaly/model/siglip2/__init__.py b/Tipsomaly/model/siglip2/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Tipsomaly/model/siglip2/siglip2_prompt_learnable.py b/Tipsomaly/model/siglip2/siglip2_prompt_learnable.py new file mode 100644 index 0000000000000000000000000000000000000000..1aa700bd565b00ddd74627b0c72b3091c251f259 --- /dev/null +++ b/Tipsomaly/model/siglip2/siglip2_prompt_learnable.py @@ -0,0 +1,183 @@ +from typing import Optional +import torch +import torch.nn as nn +from transformers.models.siglip.modeling_siglip import SiglipTextConfig, SiglipTextEmbeddings, SiglipTextTransformer, SiglipTextModel, SiglipPreTrainedModel +from transformers.utils import ( + auto_docstring, + TransformersKwargs, + can_return_tuple, +) + +from transformers.utils.generic import check_model_inputs +from transformers.processing_utils import Unpack +from transformers.modeling_outputs import ( + BaseModelOutput, + BaseModelOutputWithPooling, +) + +from transformers.modeling_attn_mask_utils import _prepare_4d_attention_mask + +class SiglipTextEmbeddingsWithPromptLearning(SiglipTextEmbeddings): + """ + Extends SiglipTextEmbeddings with learnable prompt tokens. + Prompts are added BEFORE the position embeddings are applied (so they + receive position embeddings as part of the combined sequence). + """ + def __init__(self, config: SiglipTextConfig): + super().__init__(config) + + def forward( + self, + input_ids: Optional[torch.LongTensor] = None, + position_ids: Optional[torch.LongTensor] = None, + inputs_embeds: Optional[torch.FloatTensor] = None, + learnable_prompts: torch.Tensor = None, + learning_method: str = None, + ) -> torch.Tensor: + seq_length = input_ids.shape[-1] if input_ids is not None else inputs_embeds.shape[-2] + max_position_embedding = self.position_embedding.weight.shape[0] + + if seq_length > max_position_embedding: + raise ValueError( + f"Sequence length must be less than max_position_embeddings (got `sequence length`: " + f"{seq_length} and max_position_embeddings: {max_position_embedding}" + ) + + if position_ids is None: + position_ids = self.position_ids[:, :seq_length] + + if inputs_embeds is None: + inputs_embeds = self.token_embedding(input_ids) # [B, L, D] + + batch_size = inputs_embeds.shape[0] + device = inputs_embeds.device + + if learnable_prompts is not None: + if learning_method == 'concat': + prompts = learnable_prompts.unsqueeze(0).expand(batch_size, -1, -1).to(device) # [B, P, D] + inputs_embeds = torch.cat([prompts, inputs_embeds], dim=1) # [B, P+L, D] + elif learning_method == 'sumate': + prompt_len = learnable_prompts.size(0) + inputs_embeds[:, :prompt_len, :] += learnable_prompts.unsqueeze(0) + elif learning_method == 'entire_learnable': + inputs_embeds = learnable_prompts.unsqueeze(0).expand(batch_size, -1, -1) + + current_len = inputs_embeds.size(1) + if current_len > seq_length: + inputs_embeds = inputs_embeds[:, :seq_length, :] + elif current_len < seq_length: # must check this case if happens or not + pad_len = seq_length - current_len + pad_embed = torch.zeros((batch_size, pad_len, inputs_embeds.size(2))) + inputs_embeds = torch.cat([inputs_embeds, pad_embed], dim=1) + + position_embeddings = self.position_embedding(position_ids) + embeddings = inputs_embeds + position_embeddings + + return embeddings + + +class SiglipTextTransformerWithPromptLearning(SiglipTextTransformer): + def __init__(self, config: SiglipTextConfig): + super().__init__(config) + self.embeddings = SiglipTextEmbeddingsWithPromptLearning(config) + + @can_return_tuple + # @auto_docstring + def forward( + self, + input_ids: Optional[torch.Tensor] = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.Tensor] = None, + learnable_prompts: torch.Tensor = None, + learning_method: str = None, + **kwargs: Unpack[TransformersKwargs], + ) -> BaseModelOutputWithPooling: + if input_ids is None: + raise ValueError("You have to specify input_ids") + + input_shape = input_ids.size() + input_ids = input_ids.view(-1, input_shape[-1]) + + hidden_states = self.embeddings(input_ids=input_ids, position_ids=position_ids, + learnable_prompts=learnable_prompts, learning_method=learning_method) + + # note: SigLIP's text model does not use a causal mask, unlike the original CLIP model. + # expand attention_mask + uses_flash_attention = "flash" in self.config._attn_implementation + if uses_flash_attention: + attention_mask = None + elif attention_mask is not None and not uses_flash_attention: + # [batch_size, seq_len] -> [batch_size, 1, tgt_seq_len, src_seq_len] + attention_mask = _prepare_4d_attention_mask(attention_mask, hidden_states.dtype) + + encoder_outputs: BaseModelOutput = self.encoder( + inputs_embeds=hidden_states, + attention_mask=attention_mask, + **kwargs, + ) + + last_hidden_state = encoder_outputs.last_hidden_state + last_hidden_state = self.final_layer_norm(last_hidden_state) + + # The model uses the last token's hidden state, which may be padding. + pooled_output = last_hidden_state[:, -1, :] + pooled_output = self.head(pooled_output) + + return BaseModelOutputWithPooling( + last_hidden_state=last_hidden_state, + pooler_output=pooled_output, + ) + + +class SiglipTextModelWithPromptLearning(SiglipPreTrainedModel): + config: SiglipTextConfig + input_modalities = ("text",) + + def __init__(self, config: SiglipTextConfig): + super().__init__(config) + self.text_model = SiglipTextTransformerWithPromptLearning(config) + # Initialize weights and apply final processing + self.post_init() + + def get_input_embeddings(self) -> nn.Module: + return self.text_model.embeddings.token_embedding + + def set_input_embeddings(self, value): + self.text_model.embeddings.token_embedding = value + + @check_model_inputs(tie_last_hidden_states=False) + # @auto_docstring + def forward( + self, + input_ids: Optional[torch.Tensor] = None, + attention_mask: Optional[torch.Tensor] = None, + position_ids: Optional[torch.Tensor] = None, + learnable_prompts: torch.Tensor = None, + learning_method: str = None, + **kwargs: Unpack[TransformersKwargs], + ) -> BaseModelOutputWithPooling: + r""" + Examples: + + ```python + >>> from transformers import AutoTokenizer, SiglipTextModel + + >>> model = SiglipTextModel.from_pretrained("google/siglip-base-patch16-224") + >>> tokenizer = AutoTokenizer.from_pretrained("google/siglip-base-patch16-224") + + >>> # important: make sure to set padding="max_length" as that's how the model was trained + >>> inputs = tokenizer(["a photo of a cat", "a photo of a dog"], padding="max_length", return_tensors="pt") + + >>> outputs = model(**inputs) + >>> last_hidden_state = outputs.last_hidden_state + >>> pooled_output = outputs.pooler_output # pooled (EOS token) states + ```""" + + return self.text_model( + input_ids=input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + learnable_prompts=learnable_prompts, + learning_method=learning_method, + **kwargs, + ) \ No newline at end of file diff --git a/Tipsomaly/model/tips/__init__.py b/Tipsomaly/model/tips/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..567aaec8d3d4abfc631d1928f60369ee41d80943 --- /dev/null +++ b/Tipsomaly/model/tips/__init__.py @@ -0,0 +1,35 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +# All software is licensed under the Apache License, Version 2.0 (Apache 2.0); +# you may not use this file except in compliance with the Apache 2.0 license. +# You may obtain a copy of the Apache 2.0 license at: +# https://www.apache.org/licenses/LICENSE-2.0 + +# All other materials are licensed under the Creative Commons Attribution 4.0 +# International License (CC-BY). You may obtain a copy of the CC-BY license at: +# https://creativecommons.org/licenses/by/4.0/legalcode + +# Unless required by applicable law or agreed to in writing, all software and +# materials distributed here under the Apache 2.0 or CC-BY licenses are +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +# either express or implied. See the licenses for the specific language +# governing permissions and limitations under those licenses. + +# This is not an official Google product. +"""Import all files.""" +# from . import text_encoder +# from . import image_encoder +from . import load_model \ No newline at end of file diff --git a/Tipsomaly/model/tips/checkpoints/checkpoint.py b/Tipsomaly/model/tips/checkpoints/checkpoint.py new file mode 100644 index 0000000000000000000000000000000000000000..fdc389e4b79a490ed40be9a0fb8717217863b247 --- /dev/null +++ b/Tipsomaly/model/tips/checkpoints/checkpoint.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +import os +import subprocess +from pathlib import Path +from typing import Dict, List + +# ---- Model registry ---- +# Short unique IDs -> full checkpoint base name on GCS +# (all files are under: https://storage.googleapis.com/tips_data/v1_0/checkpoints/pytorch/) +MODEL_REGISTRY: Dict[str, str] = { + # id # full checkpoint basename + "s14h": "tips_oss_s14_highres_distilled", + "b14h": "tips_oss_b14_highres_distilled", + "l14h": "tips_oss_l14_highres_distilled", + "so4h": "tips_oss_so400m14_highres_largetext_distilled", + "g14l": "tips_oss_g14_lowres", + "g14h": "tips_oss_g14_highres", +} + +TOKENIZER_FILENAME = "tokenizer.model" +TOKENIZER_URL = "https://storage.googleapis.com/tips_data/v1_0/checkpoints/tokenizer.model" + +BASE_URL = "https://storage.googleapis.com/tips_data/v1_0/checkpoints/pytorch" + +def _require_wget() -> None: + from shutil import which + if which("wget") is None: + raise EnvironmentError( + "wget is required but was not found on PATH. " + "Please install wget or add it to your PATH." + ) + +def _wget(url: str, dest: Path) -> None: + dest.parent.mkdir(parents=True, exist_ok=True) + # -q for quiet except errors; --show-progress if attached to TTY would be nice, + # but we keep it simple and quiet here. + result = subprocess.run( + ["wget", "-q", "-O", str(dest), url], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + if result.returncode != 0: + # Clean up partial file if any + if dest.exists() and dest.stat().st_size == 0: + try: + dest.unlink() + except OSError: + pass + raise RuntimeError( + f"Failed to download {url} -> {dest}\n" + f"wget stderr:\n{result.stderr.strip()}" + ) + +def _model_files_for_basename(base: str) -> List[str]: + return [f"{base}_vision.npz", f"{base}_text.npz"] + +def list_models() -> Dict[str, str]: + return dict(MODEL_REGISTRY) + +def ensure_model_files(model_id: str, save_dir: str) -> Dict[str, str]: + if model_id not in MODEL_REGISTRY: + raise KeyError( + f"Unknown model_id '{model_id}'. " + f"Valid options: {', '.join(sorted(MODEL_REGISTRY.keys()))}" + ) + + _require_wget() + + save_path = Path(save_dir).expanduser().resolve() + save_path.mkdir(parents=True, exist_ok=True) + + # 1) Tokenizer + tokenizer_path = save_path / TOKENIZER_FILENAME + if not tokenizer_path.exists(): + _wget(TOKENIZER_URL, tokenizer_path) + + # 2) Model files + base = MODEL_REGISTRY[model_id] + required_files = _model_files_for_basename(base) + + out_paths = {"tokenizer": str(tokenizer_path)} + + for fname in required_files: + local_path = save_path / fname + if not local_path.exists(): + # Compose the correct URL for this file + url = f"{BASE_URL}/{fname}" + _wget(url, local_path) + + # record + if fname.endswith("_vision.npz"): + out_paths["vision"] = str(local_path) + elif fname.endswith("_text.npz"): + out_paths["text"] = str(local_path) + + return out_paths \ No newline at end of file diff --git a/Tipsomaly/model/tips/checkpoints/download_checkpoints.sh b/Tipsomaly/model/tips/checkpoints/download_checkpoints.sh new file mode 100644 index 0000000000000000000000000000000000000000..1ed293e09b878aa58b193301830e0dd7cded3c87 --- /dev/null +++ b/Tipsomaly/model/tips/checkpoints/download_checkpoints.sh @@ -0,0 +1,36 @@ +#!/bin/bash +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 model weights can be found in https://console.cloud.google.com/storage/browser/tips_data +ALL_CHECKPOINTS=( + "tips_oss_s14_highres_distilled" + "tips_oss_b14_highres_distilled" + "tips_oss_l14_highres_distilled" + "tips_oss_so400m14_highres_largetext_distilled" + "tips_oss_g14_lowres" + "tips_oss_g14_highres" +) + +echo "Downloading the tokenizer." +wget https://storage.googleapis.com/tips_data/v1_0/checkpoints/tokenizer.model + +for CHECKPOINT in "${ALL_CHECKPOINTS[@]}"; do + echo "Downloading ${CHECKPOINT} (vision encoder weights)" + wget https://storage.googleapis.com/tips_data/v1_0/checkpoints/pytorch/${CHECKPOINT}_vision.npz + echo "Downloading ${CHECKPOINT} (text encoder weights)" + wget https://storage.googleapis.com/tips_data/v1_0/checkpoints/pytorch/${CHECKPOINT}_text.npz +done diff --git a/Tipsomaly/model/tips/image_encoder.py b/Tipsomaly/model/tips/image_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..a2fd644764c23b674d5e34b3b8da6072a0dcfcaf --- /dev/null +++ b/Tipsomaly/model/tips/image_encoder.py @@ -0,0 +1,1002 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============================================================================== + +"""Vision encoder implementation in PyTorch.""" + +import functools +import math +import os +from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union +import warnings +import torch +from torch import nn +import torch.nn.functional as F +import torch.utils.checkpoint + + +class Mlp(nn.Module): + """Transformer MLP, following DINOv2 implementation.""" + + def __init__( + self, + in_features: int, + hidden_features: Optional[int] = None, + out_features: Optional[int] = None, + act_layer: Callable[..., nn.Module] = nn.GELU, + drop: float = 0.0, + bias: bool = True, + ) -> None: + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features, bias=bias) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features, bias=bias) + self.drop = nn.Dropout(drop) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +def make_2tuple(x): + if isinstance(x, tuple): + assert len(x) == 2 + return x + + assert isinstance(x, int) + return (x, x) + + +class PatchEmbed(nn.Module): + """2D image to patch embedding: (B,C,H,W) -> (B,N,D).""" + + def __init__( + self, + img_size: Union[int, Tuple[int, int]] = 224, + patch_size: Union[int, Tuple[int, int]] = 16, + in_chans: int = 3, + embed_dim: int = 768, + norm_layer: Optional[Callable] = None, # pylint: disable=g-bare-generic + flatten_embedding: bool = True, + ) -> None: + super().__init__() + + image_hw = make_2tuple(img_size) + patch_hw = make_2tuple(patch_size) + patch_grid_size = ( + image_hw[0] // patch_hw[0], + image_hw[1] // patch_hw[1], + ) + + self.img_size = image_hw + self.patch_size = patch_hw + self.patches_resolution = patch_grid_size + self.num_patches = patch_grid_size[0] * patch_grid_size[1] + + self.in_chans = in_chans + self.embed_dim = embed_dim + + self.flatten_embedding = flatten_embedding + + self.proj = nn.Conv2d( + in_chans, embed_dim, kernel_size=patch_hw, stride=patch_hw + ) + self.norm = norm_layer(embed_dim) if norm_layer else nn.Identity() + + def forward(self, x: torch.Tensor) -> torch.Tensor: + _, _, h, w = x.shape + patch_h, patch_w = self.patch_size + + assert ( + h % patch_h == 0 + ), f"Input image height {h} is not a multiple of patch height {patch_h}" + assert ( + w % patch_w == 0 + ), f"Input image width {w} is not a multiple of patch width: {patch_w}" + + x = self.proj(x) # B C H W + h, w = x.size(2), x.size(3) + x = x.flatten(2).transpose(1, 2) # B HW C + x = self.norm(x) + if not self.flatten_embedding: + x = x.reshape(-1, h, w, self.embed_dim) # B H W C + return x + + def flops(self) -> float: + ho, wo = self.patches_resolution + flops = ( + ho + * wo + * self.embed_dim + * self.in_chans + * (self.patch_size[0] * self.patch_size[1]) + ) + if self.norm is not None: + flops += ho * wo * self.embed_dim + return flops + + +class SwiGLUFFN(nn.Module): + """SwiGLU FFN layer, following DINOv2 implementation.""" + + def __init__( + self, + in_features: int, + hidden_features: Optional[int] = None, + out_features: Optional[int] = None, + act_layer: Callable[..., nn.Module] = None, + drop: float = 0.0, + bias: bool = True, + ) -> None: + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.w12 = nn.Linear(in_features, 2 * hidden_features, bias=bias) + self.w3 = nn.Linear(hidden_features, out_features, bias=bias) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + x12 = self.w12(x) + x1, x2 = x12.chunk(2, dim=-1) + hidden = F.silu(x1) * x2 + return self.w3(hidden) + + +XFORMERS_ENABLED = os.environ.get("XFORMERS_DISABLED") is None +try: + if XFORMERS_ENABLED: + from xformers.ops import SwiGLU, memory_efficient_attention, unbind, fmha, scaled_index_add, index_select_cat # pylint: disable=g-multiple-import, g-import-not-at-top + + XFORMERS_AVAILABLE = True + warnings.warn("xFormers is available (SwiGLU)") + else: + warnings.warn("xFormers is disabled (SwiGLU)") + raise ImportError +except ImportError: + SwiGLU = SwiGLUFFN + XFORMERS_AVAILABLE = False + + warnings.warn("xFormers is not available (SwiGLU)") + + +class SwiGLUFFNFused(SwiGLU): + """SwiGLU FFN layer, following DINOv2 implementation.""" + + def __init__( + self, + in_features: int, + hidden_features: Optional[int] = None, + out_features: Optional[int] = None, + act_layer: Callable[..., nn.Module] = None, # pylint: disable=unused-argument + drop: float = 0.0, # pylint: disable=unused-argument + bias: bool = True, + ) -> None: + out_features = out_features or in_features + hidden_features = hidden_features or in_features + hidden_features = (int(hidden_features * 2 / 3) + 7) // 8 * 8 + super().__init__( + in_features=in_features, + hidden_features=hidden_features, + out_features=out_features, + bias=bias, + ) + + +class Attention(nn.Module): + """Attention layer, following DINOv2 implementation.""" + + def __init__( + self, + dim: int, + num_heads: int = 8, + qkv_bias: bool = False, + proj_bias: bool = True, + attn_drop: float = 0.0, + proj_drop: float = 0.0, + ) -> None: + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + self.scale = head_dim**-0.5 + + self.qkv = nn.Linear(dim, dim * 3, bias=qkv_bias) + self.attn_drop = nn.Dropout(attn_drop) + self.proj = nn.Linear(dim, dim, bias=proj_bias) + self.proj_drop = nn.Dropout(proj_drop) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + b_dim, n_dim, c_dim = x.shape + qkv = ( + self.qkv(x) + .reshape(b_dim, n_dim, 3, self.num_heads, c_dim // self.num_heads) + .permute(2, 0, 3, 1, 4) + ) + + q, k, v = qkv[0] * self.scale, qkv[1], qkv[2] + attn = q @ k.transpose(-2, -1) + + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + x = (attn @ v).transpose(1, 2).reshape(b_dim, n_dim, c_dim) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class MemEffAttention(Attention): + """Memory Efficient Attention layer, following DINOv2 implementation.""" + + def forward(self, x: torch.Tensor, attn_bias=None) -> torch.Tensor: + if not XFORMERS_AVAILABLE: + if attn_bias is not None: + raise AssertionError("xFormers is required for using nested tensors") + return super().forward(x) + + b_dim, n_dim, c_dim = x.shape + qkv = self.qkv(x).reshape( + b_dim, n_dim, 3, self.num_heads, c_dim // self.num_heads + ) + + q, k, v = unbind(qkv, 2) + + x = memory_efficient_attention(q, k, v, attn_bias=attn_bias) + x = x.reshape([b_dim, n_dim, c_dim]) + + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class LayerScale(nn.Module): + """Layer scale, following DINOv2 implementation.""" + + def __init__( + self, + dim: int, + init_values: Union[float, torch.Tensor] = 1e-5, + inplace: bool = False, + ) -> None: + super().__init__() + self.inplace = inplace + self.gamma = nn.Parameter(init_values * torch.ones(dim)) + + def forward(self, x: torch.Tensor) -> torch.Tensor: + return x.mul_(self.gamma) if self.inplace else x * self.gamma + + +def drop_path_impl(x, drop_prob: float = 0.0, training: bool = False): + if drop_prob == 0.0 or not training: + return x + keep_prob = 1 - drop_prob + shape = (x.shape[0],) + (1,) * ( + x.ndim - 1 + ) # work with diff dim tensors, not just 2D ConvNets + random_tensor = x.new_empty(shape).bernoulli_(keep_prob) + if keep_prob > 0.0: + random_tensor.div_(keep_prob) + output = x * random_tensor + return output + + +class DropPath(nn.Module): + """Drop paths (Stochastic Depth) per sample (when applied in main path of residual blocks).""" + + def __init__(self, drop_prob=None): + super(DropPath, self).__init__() + self.drop_prob = drop_prob + + def forward(self, x): + return drop_path_impl(x, self.drop_prob, self.training) + + +class Block(nn.Module): + """Transformer Block Implementation, following DINOv2 implementation.""" + + def __init__( + self, + dim: int, + num_heads: int, + mlp_ratio: float = 4.0, + qkv_bias: bool = False, + proj_bias: bool = True, + ffn_bias: bool = True, + drop: float = 0.0, + attn_drop: float = 0.0, + init_values=None, + drop_path: float = 0.0, + act_layer: Callable[..., nn.Module] = nn.GELU, + norm_layer: Callable[..., nn.Module] = nn.LayerNorm, + attn_class: Callable[..., nn.Module] = Attention, + ffn_layer: Callable[..., nn.Module] = Mlp, + ) -> None: + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = attn_class( + dim, + num_heads=num_heads, + qkv_bias=qkv_bias, + proj_bias=proj_bias, + attn_drop=attn_drop, + proj_drop=drop, + ) + self.ls1 = ( + LayerScale(dim, init_values=init_values) + if init_values + else nn.Identity() + ) + self.drop_path1 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = ffn_layer( + in_features=dim, + hidden_features=mlp_hidden_dim, + act_layer=act_layer, + drop=drop, + bias=ffn_bias, + ) + self.ls2 = ( + LayerScale(dim, init_values=init_values) + if init_values + else nn.Identity() + ) + self.drop_path2 = DropPath(drop_path) if drop_path > 0.0 else nn.Identity() + + self.sample_drop_ratio = drop_path + + def forward(self, x: torch.Tensor) -> torch.Tensor: + def attn_residual_func(x: torch.Tensor) -> torch.Tensor: + return self.ls1(self.attn(self.norm1(x))) + + def ffn_residual_func(x: torch.Tensor) -> torch.Tensor: + return self.ls2(self.mlp(self.norm2(x))) + + if self.training and self.sample_drop_ratio > 0.1: + # the overhead is compensated only for a drop path rate larger than 0.1 + x = drop_add_residual_stochastic_depth( + x, + residual_func=attn_residual_func, + sample_drop_ratio=self.sample_drop_ratio, + ) + x = drop_add_residual_stochastic_depth( + x, + residual_func=ffn_residual_func, + sample_drop_ratio=self.sample_drop_ratio, + ) + elif self.training and self.sample_drop_ratio > 0.0: + x = x + self.drop_path1(attn_residual_func(x)) + x = x + self.drop_path1(ffn_residual_func(x)) + else: + x = x + attn_residual_func(x) + x = x + ffn_residual_func(x) + return x + + +def drop_add_residual_stochastic_depth( + x: torch.Tensor, + residual_func: Callable[[torch.Tensor], torch.Tensor], + sample_drop_ratio: float = 0.0, +) -> torch.Tensor: + """This function is taken from the original implementation in DINOv2 to implement stochastic depth in the image encoder.""" + # 1) extract subset using permutation + b, _, _ = x.shape + sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1) + brange = (torch.randperm(b, device=x.device))[:sample_subset_size] + x_subset = x[brange] + + # 2) apply residual_func to get residual + residual = residual_func(x_subset) + + x_flat = x.flatten(1) + residual = residual.flatten(1) + + residual_scale_factor = b / sample_subset_size + + # 3) add the residual + x_plus_residual = torch.index_add( + x_flat, 0, brange, residual.to(dtype=x.dtype), alpha=residual_scale_factor + ) + return x_plus_residual.view_as(x) + + +def get_branges_scales(x, sample_drop_ratio=0.0): + b, _, _ = x.shape + sample_subset_size = max(int(b * (1 - sample_drop_ratio)), 1) + brange = (torch.randperm(b, device=x.device))[:sample_subset_size] + residual_scale_factor = b / sample_subset_size + return brange, residual_scale_factor + + +def add_residual( + x, brange, residual, residual_scale_factor, scaling_vector=None +): + """Implement residual addition in the image encoder.""" + if scaling_vector is None: + x_flat = x.flatten(1) + residual = residual.flatten(1) + x_plus_residual = torch.index_add( + x_flat, + 0, + brange, + residual.to(dtype=x.dtype), + alpha=residual_scale_factor, + ) + else: + x_plus_residual = scaled_index_add( + x, + brange, + residual.to(dtype=x.dtype), + scaling=scaling_vector, + alpha=residual_scale_factor, + ) + return x_plus_residual + + +attn_bias_cache: Dict[Tuple, Any] = {} # pylint: disable=g-bare-generic + + +def get_attn_bias_and_cat(x_list, branges=None): + """this will perform the index select, cat the tensors, and provide the attn_bias from cache.""" + batch_sizes = ( + [b.shape[0] for b in branges] + if branges is not None + else [x.shape[0] for x in x_list] + ) + all_shapes = tuple((b, x.shape[1]) for b, x in zip(batch_sizes, x_list)) + if all_shapes not in attn_bias_cache.keys(): + seqlens = [] + for b, x in zip(batch_sizes, x_list): + for _ in range(b): + seqlens.append(x.shape[1]) + attn_bias = fmha.BlockDiagonalMask.from_seqlens(seqlens) + attn_bias._batch_sizes = batch_sizes # pylint: disable=protected-access + attn_bias_cache[all_shapes] = attn_bias + + if branges is not None: + cat_tensors = index_select_cat( + [x.flatten(1) for x in x_list], branges + ).view(1, -1, x_list[0].shape[-1]) + else: + tensors_bs1 = tuple(x.reshape([1, -1, *x.shape[2:]]) for x in x_list) + cat_tensors = torch.cat(tensors_bs1, dim=1) + + return attn_bias_cache[all_shapes], cat_tensors + + +def drop_add_residual_stochastic_depth_list( + x_list: List[torch.Tensor], + residual_func: Callable[[torch.Tensor, Any], torch.Tensor], + sample_drop_ratio: float = 0.0, + scaling_vector=None, +) -> torch.Tensor: + """Add residual to a list of tensors.""" + # 1) generate random set of indices for dropping samples in the batch. + branges_scales = [ + get_branges_scales(x, sample_drop_ratio=sample_drop_ratio) for x in x_list + ] + branges = [s[0] for s in branges_scales] + residual_scale_factors = [s[1] for s in branges_scales] + + # 2) get attention bias and index+concat the tensors. + attn_bias, x_cat = get_attn_bias_and_cat(x_list, branges) + + # 3) apply residual_func to get residual, and split the result. + residual_list = attn_bias.split(residual_func(x_cat, attn_bias=attn_bias)) # type: ignore + + outputs = [] + for x, brange, residual, residual_scale_factor in zip( + x_list, branges, residual_list, residual_scale_factors + ): + outputs.append( + add_residual( + x, brange, residual, residual_scale_factor, scaling_vector + ).view_as(x) + ) + return outputs + + +class NestedTensorBlock(Block): + """Nested tensor block implementation.""" + + def forward_nested(self, x_list: List[torch.Tensor]) -> List[torch.Tensor]: + """x_list contains a list of tensors to nest together and run.""" + assert isinstance(self.attn, MemEffAttention) + + if self.training and self.sample_drop_ratio > 0.0: + + def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor: + return self.attn(self.norm1(x), attn_bias=attn_bias) + + def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor: + del attn_bias + return self.mlp(self.norm2(x)) + + x_list = drop_add_residual_stochastic_depth_list( + x_list, + residual_func=attn_residual_func, + sample_drop_ratio=self.sample_drop_ratio, + scaling_vector=self.ls1.gamma + if isinstance(self.ls1, LayerScale) + else None, + ) + x_list = drop_add_residual_stochastic_depth_list( + x_list, + residual_func=ffn_residual_func, + sample_drop_ratio=self.sample_drop_ratio, + scaling_vector=self.ls2.gamma + if isinstance(self.ls1, LayerScale) + else None, + ) + return x_list + else: + + def attn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor: + return self.ls1(self.attn(self.norm1(x), attn_bias=attn_bias)) + + def ffn_residual_func(x: torch.Tensor, attn_bias=None) -> torch.Tensor: + del attn_bias + return self.ls2(self.mlp(self.norm2(x))) + + attn_bias, x = get_attn_bias_and_cat(x_list) + x = x + attn_residual_func(x, attn_bias=attn_bias) + x = x + ffn_residual_func(x) + return attn_bias.split(x) + + def forward(self, x): + if isinstance(x, torch.Tensor): + return super().forward(x) + elif isinstance(x, list): + if not XFORMERS_AVAILABLE: + raise AssertionError("xFormers is required for using nested tensors") + return self.forward_nested(x) + else: + raise AssertionError + + +def named_apply( + fn: Callable, # pylint: disable=g-bare-generic + module: nn.Module, + name="", + depth_first=True, + include_root=False, +) -> nn.Module: + """Apply a function to a module and its children.""" + if not depth_first and include_root: + fn(module=module, name=name) + for child_name, child_module in module.named_children(): + child_name = ".".join((name, child_name)) if name else child_name + named_apply( + fn=fn, + module=child_module, + name=child_name, + depth_first=depth_first, + include_root=True, + ) + if depth_first and include_root: + fn(module=module, name=name) + return module + + +class BlockChunk(nn.ModuleList): + + def forward(self, x): + for b in self: + x = b(x) + return x + + +class VisionTransformer(nn.Module): + """Vision Transformer implementation.""" + + def __init__( + self, + img_size=224, + patch_size=16, + in_chans=3, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4.0, + qkv_bias=True, + ffn_bias=True, + proj_bias=True, + drop_path_rate=0.0, + drop_path_uniform=False, + init_values=None, # for layerscale: None or 0 => no layerscale + embed_layer=PatchEmbed, + act_layer=nn.GELU, + block_fn=Block, + ffn_layer="mlp", + block_chunks=1, + num_register_tokens=0, + interpolate_antialias=False, + interpolate_offset=0.1, + ): + """Defines the Vision Transformer model. + + Args: + img_size (int, tuple): input image size + patch_size (int, tuple): patch size + in_chans (int): number of input channels + embed_dim (int): embedding dimension + depth (int): depth of transformer + num_heads (int): number of attention heads + mlp_ratio (int): ratio of mlp hidden dim to embedding dim + qkv_bias (bool): enable bias for qkv if True + ffn_bias (bool): enable bias for ffn if True + proj_bias (bool): enable bias for proj in attn if True + drop_path_rate (float): stochastic depth rate + drop_path_uniform (bool): apply uniform drop rate across blocks + init_values (float): layer-scale init values + embed_layer (nn.Module): patch embedding layer + act_layer (nn.Module): MLP activation layer + block_fn (nn.Module): transformer block class + ffn_layer (str): "mlp", "swiglu", "swiglufused" or "identity" + block_chunks: (int) split block sequence into block_chunks units for FSDP + wrap + num_register_tokens: (int) number of extra cls tokens (so-called + "registers") + interpolate_antialias: (str) flag to apply anti-aliasing when + interpolating positional embeddings + interpolate_offset: (float) work-around offset to apply when interpolating + positional embeddings + """ + super().__init__() + norm_layer = functools.partial(nn.LayerNorm, eps=1e-6) + + self.num_features = self.embed_dim = ( + embed_dim # num_features for consistency with other models + ) + self.num_tokens = 1 + self.n_blocks = depth + self.num_heads = num_heads + self.patch_size = patch_size + self.num_register_tokens = num_register_tokens + self.interpolate_antialias = interpolate_antialias + self.interpolate_offset = interpolate_offset + + self.patch_embed = embed_layer( + img_size=img_size, + patch_size=patch_size, + in_chans=in_chans, + embed_dim=embed_dim, + ) + num_patches = self.patch_embed.num_patches + + self.cls_token = nn.Parameter(torch.zeros(1, 1, embed_dim)) + self.pos_embed = nn.Parameter( + torch.zeros(1, num_patches + self.num_tokens, embed_dim) + ) + assert num_register_tokens >= 0 + self.register_tokens = ( + nn.Parameter(torch.zeros(1, num_register_tokens, embed_dim)) + if num_register_tokens + else None + ) + + if drop_path_uniform: + dpr = [drop_path_rate] * depth + else: + dpr = [ + x.item() for x in torch.linspace(0, drop_path_rate, depth) + ] # stochastic depth decay rule + + if ffn_layer == "mlp": + ffn_layer = Mlp + elif ffn_layer == "swiglufused" or ffn_layer == "swiglu": + ffn_layer = SwiGLUFFNFused + else: + raise NotImplementedError + + blocks_list = [ + block_fn( + dim=embed_dim, + num_heads=num_heads, + mlp_ratio=mlp_ratio, + qkv_bias=qkv_bias, + proj_bias=proj_bias, + ffn_bias=ffn_bias, + drop_path=dpr[i], + norm_layer=norm_layer, + act_layer=act_layer, + ffn_layer=ffn_layer, + init_values=init_values, + ) + for i in range(depth) + ] + if block_chunks > 0: + self.chunked_blocks = True + chunked_blocks = [] + chunksize = depth // block_chunks + for i in range(0, depth, chunksize): + # this is to keep the block index consistent if we chunk the block list + chunked_blocks.append( + [nn.Identity()] * i + blocks_list[i : i + chunksize] + ) + self.blocks = nn.ModuleList([BlockChunk(p) for p in chunked_blocks]) + else: + self.chunked_blocks = False + self.blocks = nn.ModuleList(blocks_list) + + self.norm = norm_layer(embed_dim) + self.head = nn.Identity() + + self.mask_token = nn.Parameter(torch.zeros(1, embed_dim)) + + self.init_weights() + + def init_weights(self): + nn.init.trunc_normal_(self.pos_embed, std=0.02) + nn.init.normal_(self.cls_token, std=1e-6) + if self.register_tokens is not None: + nn.init.normal_(self.register_tokens, std=1e-6) + named_apply(init_weights_vit_timm, self) + + def interpolate_pos_encoding(self, x, w, h): + previous_dtype = x.dtype + npatch = x.shape[1] - 1 + num_patches = self.pos_embed.shape[1] - 1 + if npatch == num_patches and w == h: + return self.pos_embed + pos_embed = self.pos_embed.float() + class_pos_embed = pos_embed[:, 0] + patch_pos_embed = pos_embed[:, 1:] + dim = x.shape[-1] + w0 = w // self.patch_size + h0 = h // self.patch_size + num_patches_dim = int( + math.sqrt(num_patches) + ) # Recover the number of patches in each dimension + assert num_patches == num_patches_dim * num_patches_dim + kwargs = {} + if self.interpolate_offset: + sx = float(w0 + self.interpolate_offset) / num_patches_dim + sy = float(h0 + self.interpolate_offset) / num_patches_dim + kwargs["scale_factor"] = (sx, sy) + else: + # Simply specify an output size instead of a scale factor + kwargs["size"] = (w0, h0) + patch_pos_embed = nn.functional.interpolate( + patch_pos_embed.reshape( + 1, num_patches_dim, num_patches_dim, dim + ).permute(0, 3, 1, 2), + mode="bilinear", + antialias=self.interpolate_antialias, + **kwargs, + ) + assert (w0, h0) == patch_pos_embed.shape[-2:] + patch_pos_embed = patch_pos_embed.permute(0, 2, 3, 1).view(1, -1, dim) + return torch.cat((class_pos_embed.unsqueeze(0), patch_pos_embed), dim=1).to( + previous_dtype + ) + + def prepare_tokens_with_masks(self, x, masks=None): + _, _, w, h = x.shape + x = self.patch_embed(x) + if masks is not None: + x = torch.where( + masks.unsqueeze(-1), self.mask_token.to(x.dtype).unsqueeze(0), x + ) + + x = torch.cat((self.cls_token.expand(x.shape[0], -1, -1), x), dim=1) + x = x + self.interpolate_pos_encoding(x, w, h) + + if self.register_tokens is not None: + x = torch.cat( + ( + x[:, :1], + self.register_tokens.expand(x.shape[0], -1, -1), + x[:, 1:], + ), + dim=1, + ) + + return x + + def forward_features_list(self, x_list, masks_list): + x = [ + self.prepare_tokens_with_masks(x, masks) + for x, masks in zip(x_list, masks_list) + ] + for blk in self.blocks: + x = blk(x) + + all_x = x + output = [] + for x, masks in zip(all_x, masks_list): + x_norm = self.norm(x) + output.append({ + "x_norm_1st_clstoken": x_norm[:, :1], + "x_norm_2nd_clstoken": x_norm[:, 1 : self.num_register_tokens + 1], + "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :], + "x_prenorm": x, + "masks": masks, + }) + return output + + def forward_features(self, x, masks=None): + if isinstance(x, list): + return self.forward_features_list(x, masks) + + x = self.prepare_tokens_with_masks(x, masks) + + for blk in self.blocks: + x = blk(x) + + x_norm = self.norm(x) + return { + "x_norm_1st_clstoken": x_norm[:, :1], + "x_norm_2nd_clstoken": x_norm[:, 1 : self.num_register_tokens + 1], + "x_norm_patchtokens": x_norm[:, self.num_register_tokens + 1 :], + "x_prenorm": x, + "masks": masks, + } + + def _get_intermediate_layers_not_chunked(self, x, n=1): + x = self.prepare_tokens_with_masks(x) + # If n is an int, take the n last blocks. If it's a list, take them + output, total_block_len = [], len(self.blocks) + blocks_to_take = ( + range(total_block_len - n, total_block_len) if isinstance(n, int) else n + ) + for i, blk in enumerate(self.blocks): + x = blk(x) + if i in blocks_to_take: + output.append(x) + assert len(output) == len( + blocks_to_take + ), f"only {len(output)} / {len(blocks_to_take)} blocks found" + return output + + def _get_intermediate_layers_chunked(self, x, n=1): + x = self.prepare_tokens_with_masks(x) + output, i, total_block_len = [], 0, len(self.blocks[-1]) + # If n is an int, take the n last blocks. If it's a list, take them + blocks_to_take = ( + range(total_block_len - n, total_block_len) if isinstance(n, int) else n + ) + for block_chunk in self.blocks: + for blk in block_chunk[i:]: # Passing the nn.Identity() + x = blk(x) + if i in blocks_to_take: + output.append(x) + i += 1 + assert len(output) == len( + blocks_to_take + ), f"only {len(output)} / {len(blocks_to_take)} blocks found" + return output + + def get_intermediate_layers( + self, + x: torch.torch.Tensor, + n: Union[int, Sequence] = 1, # Layers or n last layers to take # pylint: disable=g-bare-generic + reshape: bool = False, + return_class_token: bool = False, + norm=True, + ) -> Tuple[Union[torch.torch.Tensor, Tuple[torch.torch.Tensor]]]: # pylint: disable=g-one-element-tuple + if self.chunked_blocks: + outputs = self._get_intermediate_layers_chunked(x, n) + else: + outputs = self._get_intermediate_layers_not_chunked(x, n) + if norm: + outputs = [self.norm(out) for out in outputs] + class_tokens = [out[:, 0] for out in outputs] + outputs = [out[:, 1 + self.num_register_tokens :] for out in outputs] + if reshape: + batch_size, _, w, h = x.shape + outputs = [ + out.reshape( + batch_size, w // self.patch_size, h // self.patch_size, -1 + ) + .permute(0, 3, 1, 2) + .contiguous() + for out in outputs + ] + if return_class_token: + return tuple(zip(outputs, class_tokens)) + return tuple(outputs) + + def forward(self, *args, is_training=False, **kwargs): + ret = self.forward_features(*args, **kwargs) + if is_training: + return ret + else: + return self.head(ret["x_norm_1st_clstoken"]), self.head( + ret["x_norm_2nd_clstoken"] + ), ret["x_norm_patchtokens"] + + +def init_weights_vit_timm(module: nn.Module, name: str = ""): # pylint: disable=unused-argument + """ViT weight initialization, original timm impl (for reproducibility).""" + if isinstance(module, nn.Linear): + nn.init.trunc_normal_(module.weight, std=0.02) + if module.bias is not None: + nn.init.zeros_(module.bias) + + +def vit_small(patch_size=14, **kwargs): + model = VisionTransformer( + patch_size=patch_size, + embed_dim=384, + depth=12, + num_heads=6, + mlp_ratio=4, + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + **kwargs, + ) + return model + + +def vit_base(patch_size=14, **kwargs): + model = VisionTransformer( + patch_size=patch_size, + embed_dim=768, + depth=12, + num_heads=12, + mlp_ratio=4, + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + **kwargs, + ) + return model + + +def vit_large(patch_size=14, **kwargs): + model = VisionTransformer( + patch_size=patch_size, + embed_dim=1024, + depth=24, + num_heads=16, + mlp_ratio=4, + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + **kwargs, + ) + return model + + +def vit_so400m(patch_size=14, **kwargs): + """SoViT 400M model (https://arxiv.org/abs/2305.13035).""" + model = VisionTransformer( + patch_size=patch_size, + embed_dim=1152, + depth=27, + num_heads=16, + mlp_ratio=4304 / 1152, + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + **kwargs, + ) + return model + + +def vit_giant2(patch_size=14, **kwargs): + model = VisionTransformer( + patch_size=patch_size, + embed_dim=1536, + depth=40, + num_heads=24, + mlp_ratio=4, + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + **kwargs, + ) + return model diff --git a/Tipsomaly/model/tips/load_model.py b/Tipsomaly/model/tips/load_model.py new file mode 100644 index 0000000000000000000000000000000000000000..0accbfdcb4e5f7018198a9f017429d482fffebda --- /dev/null +++ b/Tipsomaly/model/tips/load_model.py @@ -0,0 +1,112 @@ +import io +import numpy as np +import torch +import os + +from .text_encoder import TextEncoder, Tokenizer +# from .image_encoder import vit_small, vit_base, vit_large, vit_so400m, vit_giant2 +from .image_encoder import Block, VisionTransformer, MemEffAttention +import functools + +from .checkpoints import checkpoint + +MAX_LEN = 64 +VOCAB_SIZE = 32000 +PATCH_SIZE = 14 + +vision_models = { + 'S': {'embed_dim': 384, 'depth': 12, 'num_heads': 6, 'mlp_ratio': 4.0}, + 'B': {'embed_dim': 768, 'depth': 12, 'num_heads': 12, 'mlp_ratio': 4.0}, + 'L': {'embed_dim': 1024, 'depth': 24, 'num_heads': 16, 'mlp_ratio': 4.0}, + 'So400m': {'embed_dim': 1152, 'depth': 27, 'num_heads': 16, 'mlp_ratio': 4304/1152}, + 'G': {'embed_dim': 1536, 'depth': 40, 'num_heads': 24, 'mlp_ratio': 4.0}, +} +def build_vision_encoder(cfg: dict, *, img_size, patch_size, ffn_layer): + return VisionTransformer( + patch_size=patch_size, + embed_dim=cfg['embed_dim'], + depth=cfg['depth'], + num_heads=cfg['num_heads'], + mlp_ratio=cfg['mlp_ratio'], + block_fn=functools.partial(Block, attn_class=MemEffAttention), + num_register_tokens=1, + img_size=img_size, + ffn_layer=ffn_layer, + block_chunks=0, + init_values=1.0, + interpolate_antialias=True, + interpolate_offset=0.0, + ) + +def load_image_encoder(model_weights_path, model_variant, is_low_res, patch_size = 14): + img_size = 224 if is_low_res else 448 + ffn_layer = 'swiglu' if model_variant == 'G' else 'mlp' + cfg = vision_models[model_variant] + + checkpoint_np = dict(np.load(model_weights_path, allow_pickle=False)) + checkpoint = {k: torch.tensor(v) for k, v in checkpoint_np.items()} + + with torch.no_grad(): + model = build_vision_encoder(cfg, img_size=img_size, patch_size=patch_size, ffn_layer=ffn_layer) + missing, unexpected = model.load_state_dict(checkpoint, strict=False) + # Optional: sanity logs + if missing: + print(f"[vision:{model_variant}] Missing keys: {len(missing)} (e.g. {missing[:3]})") + if unexpected: + print(f"[vision:{model_variant}] Unexpected keys: {len(unexpected)} (e.g. {unexpected[:3]})") + + return model + +text_models = { + 'S': {'hidden_size': 384, 'mlp_dim': 1536, 'num_heads': 6, 'num_layers': 12}, + 'B': {'hidden_size': 768, 'mlp_dim': 3072, 'num_heads': 12, 'num_layers': 12}, + 'L': {'hidden_size': 1024, 'mlp_dim': 4096, 'num_heads': 16, 'num_layers': 12}, + 'So400m': {'hidden_size': 1152, 'mlp_dim': 4304, 'num_heads': 16, 'num_layers': 27}, + 'G': {'hidden_size': 1536, 'mlp_dim': 6144, 'num_heads': 24, 'num_layers': 12}, +} +def load_text_encoder(model_path, model_variant, tokenizer_path): + with open(model_path, 'rb') as fin: + inbuffer = io.BytesIO(fin.read()) + np_weights_text = np.load(inbuffer, allow_pickle=False) + + weights_text = {} + for key, value in np_weights_text.items(): + weights_text[key] = torch.from_numpy(value) + temperature = weights_text.pop('temperature') + + with torch.no_grad(): + # Define the text model. + model_text = TextEncoder( + text_models[model_variant], + vocab_size=VOCAB_SIZE, + ) + model_text.load_state_dict(weights_text) + + tokenizer_obj = Tokenizer(tokenizer_path=tokenizer_path) + return model_text, tokenizer_obj, temperature + + +CHECKPOINT_TO_VARIANT = { + "s14h": "S", + "b14h": "B", + "l14h": "L", + "so4h": "So400m", # 400m = big SoViT model + "g14l": "G", # Giant low-res + "g14h": "G", # Giant high-res +} +def get_model(model_path, model_checkpoint): + paths = checkpoint.ensure_model_files(model_checkpoint, model_path) + for key, path in paths.items(): + print(f"{key}: {path}") + + tokenizer_path = os.path.join(model_path, 'tokenizer.model') + image_enc_name, text_enc_name = checkpoint._model_files_for_basename(checkpoint.MODEL_REGISTRY[model_checkpoint]) + image_encoder_path = os.path.join(model_path, image_enc_name) + text_encoder_path = os.path.join(model_path, text_enc_name) + is_low_res = model_checkpoint.endswith('l') + + model_variant = CHECKPOINT_TO_VARIANT[model_checkpoint] + image_encoder = load_image_encoder(image_encoder_path, model_variant, is_low_res) + text_encoder, tokenizer, temperature = load_text_encoder(text_encoder_path, model_variant, tokenizer_path) + + return image_encoder, text_encoder, tokenizer, temperature \ No newline at end of file diff --git a/Tipsomaly/model/tips/text_encoder.py b/Tipsomaly/model/tips/text_encoder.py new file mode 100644 index 0000000000000000000000000000000000000000..e4dc1bdea31906db88a56920f7836acd74434e76 --- /dev/null +++ b/Tipsomaly/model/tips/text_encoder.py @@ -0,0 +1,519 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT 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 encoder implementation in PyTorch.""" + +import typing as t + +# import tensorflow as tf +# import tensorflow_text +import torch +from torch import nn +import torch.nn.functional as F + +import torch +import sentencepiece as spm + +class Tokenizer: + """A simple tokenizer using SentencePiece with batch support.""" + + def __init__(self, tokenizer_path: str): + """Initializes the tokenizer.""" + self.tokenizer = spm.SentencePieceProcessor(model_file=tokenizer_path) + + def tokenize(self, input_texts, max_len=64): + """Tokenizes a batch of input texts and pads/clips them to max_len.""" + if isinstance(input_texts, str): + input_texts = [input_texts] # Convert single string to list + + input_texts = [text.lower() for text in input_texts] # Lowercasing batch + tokenized = [self.tokenizer.encode(text) for text in input_texts] # Tokenize batch + + # Pad or truncate to max_len + padded_tokens = [] + padding_masks = [] + for tokens in tokenized: + curr_len = len(tokens) + if curr_len > max_len: + tokens = tokens[:max_len] # Truncate + else: + tokens += [0] * (max_len - curr_len) # Pad + + padded_tokens.append(tokens) + padding_masks.append([0] * curr_len + [1] * (max_len - curr_len)) # Mask (1 for real tokens, 0 for padding) + + # Convert to PyTorch tensors + tokens_tensor = torch.tensor(padded_tokens, dtype=torch.int32) + padding_mask_tensor = torch.tensor(padding_masks, dtype=torch.int32) + + return tokens_tensor, padding_mask_tensor + + +# class Tokenizer(object): +# """A simple tokenizer.""" + +# def __init__(self, tokenizer_path: str): +# """Initializes the tokenizer.""" +# with open(tokenizer_path, 'rb') as f: +# model = f.read() +# self.tokenizer = tensorflow_text.SentencepieceTokenizer( +# model=model, add_eos=False, add_bos=False +# ) + +# def tokenize(self, input_text, max_len=64): +# tokens = self.tokenizer.tokenize(tf.strings.lower(input_text)).to_tensor() +# curr_len = tokens.shape[1] +# is_padding = tf.zeros((tokens.shape[0], max_len)) +# if curr_len > max_len: +# tokens = tokens[:, :max_len] +# else: +# padding_len = max_len - curr_len +# tokens = tf.pad(tokens, [[0, 0], [0, padding_len]], constant_values=0) +# is_padding = tf.cast(tokens == 0, tf.int32) +# return tokens.numpy(), is_padding.numpy() + + +class PositionalEmbedding(nn.Module): + """Generates position embedding for a given 1-d sequence. + + Attributes: + min_timescale: Start of the geometric index. Determines the periodicity of + the added signal. + max_timescale: End of the geometric index. Determines the frequency of the + added signal. + embedding_dim: Dimension of the embedding to be generated. + """ + + min_timescale: int = 1 + max_timescale: int = 10_000 + embedding_dim: int = 0 + + def __init__(self, embedding_dim: int): + super().__init__() + self.embedding_dim = embedding_dim + + def __call__(self, seq_length: int = None, position: torch.tensor = None): + """Generates a torch.tensor of sinusoids with different frequencies. + + Args: + seq_length: an optional Python int defining the output sequence length. + if the `position` argument is specified. + position: [B, seq_length], optional position for each token in the + sequence, only required when the sequence is packed. + + Returns: + [B, seqlen, D] if `position` is specified, else [1, seqlen, D] + """ + if position is None: + assert seq_length is not None + # [1, seqlen] + position = torch.arange(seq_length, dtype=torch.float32)[None, :] + else: + assert position.ndim == 2, position.shape + + num_timescales = self.embedding_dim // 2 + log_timescale_increment = torch.log( + torch.tensor(float(self.max_timescale) / float(self.min_timescale)) + ) / torch.maximum( + torch.tensor(num_timescales, dtype=torch.float32) - 1, torch.tensor(1) + ) + inv_timescales = self.min_timescale * torch.exp( + torch.arange(num_timescales, dtype=torch.float32) + * -log_timescale_increment + ) + scaled_time = position[:, :, None] * inv_timescales[None, None, :] + signal = torch.cat((torch.sin(scaled_time), torch.cos(scaled_time)), dim=2) + # Force usage of `np` rather than `jnp` to compute static values at trace + # time. + signal = F.pad(signal, (0, self.embedding_dim % 2, 0, 0, 0, 0)) + return signal + + +class MlpBlockWithMask(nn.Module): + """Transformer MLP / feed-forward block that supports masking.""" + + def __init__( + self, + mlp_dim: int, + d_model: int, + use_bias: bool = True, + dtype: torch.dtype = torch.float32, + activation_fn: nn.Module = nn.GELU, + ): + super().__init__() + + self.mlp_dim = mlp_dim + self.d_model = d_model + self.use_bias = use_bias + self.dtype = dtype + self.activation_fn = activation_fn + + self.c_fc = nn.Linear( + in_features=self.d_model, + out_features=self.mlp_dim, + dtype=self.dtype, + bias=self.use_bias, + ) + self.c_proj = nn.Linear( + in_features=self.mlp_dim, + out_features=self.d_model, + dtype=self.dtype, + bias=self.use_bias, + ) + + def __call__( + self, inputs: torch.Tensor, mlp_mask: torch.Tensor + ) -> torch.Tensor: + """Applies Transformer MlpBlock with mask module.""" + x = self.c_fc(inputs) + x = self.activation_fn()(x) + x = x * mlp_mask[..., None] # First masking. + x = self.c_proj(x) + x = x * mlp_mask[..., None] # Second masking. + return x + + +class ResidualAttentionBlock(nn.Module): + """Transformer residual attention block.""" + + def __init__( + self, + d_model: int, + n_head: int, + mlp_dim: int, + dtype: torch.dtype = torch.float32, + ): + super().__init__() + self.d_model = d_model + self.n_head = n_head + self.mlp_dim = mlp_dim + self.dtype = dtype + + self.attn = nn.MultiheadAttention(d_model, n_head, dtype=self.dtype) + self.ln_1 = nn.LayerNorm(d_model, dtype=self.dtype) + self.mlp = MlpBlockWithMask( + self.mlp_dim, + d_model, + use_bias=True, + dtype=self.dtype, + activation_fn=nn.ReLU, + ) + self.ln_2 = nn.LayerNorm(d_model, dtype=self.dtype) + + def attention(self, x: torch.Tensor, mask: torch.Tensor): + attn_mask = ( + mask[:, None, None, :] + .repeat(1, self.n_head, x.shape[0], 1) + .flatten(0, 1) + ) + attn_mask[attn_mask == 0] = float('-inf') + attn_mask[attn_mask == 1] = 0 + return self.attn(x, x, x, need_weights=False, attn_mask=attn_mask)[0] + + def forward(self, x: torch.Tensor, mask: torch.Tensor): + x = x + self.attention(self.ln_1(x), mask.permute(1, 0)) + x = x + self.mlp(self.ln_2(x), mask) + return x, mask + + +class SequentialMultiInput(nn.Sequential): + """Sequential module that can take multiple inputs.""" + + def forward(self, *inputs): + for module in self._modules.values(): + if isinstance(inputs, tuple): + inputs = module(*inputs) + else: + inputs = module(inputs) + return inputs + + +class Transformer(nn.Module): + """Transformer implementation.""" + + def __init__( + self, + width: int, + layers: int, + heads: int, + mlp_dim: int, + dtype: torch.dtype = torch.float32, + ): + super().__init__() + self.width = width + self.layers = layers + self.heads = heads + self.mlp_dim = mlp_dim + self.dtype = dtype + + self.resblocks = SequentialMultiInput(*[ + ResidualAttentionBlock(self.width, self.heads, self.mlp_dim, self.dtype) + for _ in range(self.layers) + ]) + + + def _concat_mask(self, mask: torch.Tensor, n_deep: int, after_bos: bool) -> torch.Tensor: + if mask is None: + return None + if mask.dim() == 2: # (B, L), 1/True = keep + B, L = mask.shape + deep_vis = torch.ones(B, n_deep, dtype=mask.dtype, device=mask.device) + if after_bos and L >= 1: + return torch.cat([mask[:, :1], deep_vis, mask[:, 1:]], dim=1) + else: + return torch.cat([deep_vis, mask], dim=1) + elif mask.dim() == 4: # (B, 1, 1, L), additive mask (0 keep) + B, _, _, L = mask.shape + deep_vis = torch.zeros(B, 1, 1, n_deep, dtype=mask.dtype, device=mask.device) + if after_bos and L >= 1: + return torch.cat([mask[..., :1], deep_vis, mask[..., 1:]], dim=-1) + else: + return torch.cat([deep_vis, mask], dim=-1) + else: + raise ValueError(f"Unsupported mask shape: {mask.shape}") + + def forward( + self, + x: torch.Tensor, + mask: torch.Tensor, + deep_tokens: nn.ParameterList = None, + *, + after_bos: bool = False # place prompts after BOS/CLS at position 0 + ) -> torch.Tensor: + """ + If deep_tokens is provided, insert n_deep tokens at the input of layers + [insert_start_layer, insert_start_layer + len(deep_tokens)) and strip them after each block. + """ + # fast path: no deep tokens + if not deep_tokens: + for block in self.resblocks: + x, mask = block(x, mask) + return x + + # shapes/sizes + B, L, D = x.shape + n_deep = deep_tokens[0].shape[0] # number of deep tokens per tuned layer + d_deep = len(deep_tokens) # number of layers to tune + start = 1 # max(0, int(self.insert_start_layer)) + end = min(start + d_deep, len(self.resblocks)) + + # sanity checks + for t in deep_tokens: + assert t.shape == (n_deep, D), f"Each deep token tensor must be (n_deep={n_deep}, D={D}), got {t.shape}" + + for i, block in enumerate(self.resblocks): + if start <= i < end: + # select layer-specific deep tokens and expand across batch + pt = deep_tokens[i - start].unsqueeze(0).expand(B, -1, -1) # (B, n_deep, D) + + # place after BOS/CLS if present, otherwise at pure prefix + if after_bos and L >= 1: + x_ext = torch.cat([x[:, :1], pt, x[:, 1:]], dim=1) # [BOS] + deep + tokens + else: + x_ext = torch.cat([pt, x], dim=1) # deep + tokens + + mask_ext = self._concat_mask(mask, n_deep, after_bos=(after_bos and L >= 1)) + y, _ = block(x_ext, mask_ext) + if after_bos and L >= 1: + x = torch.cat([y[:, :1], y[:, 1 + n_deep:]], dim=1) + else: + x = y[:, n_deep:, :] + else: + x, _ = block(x, mask) + + return x + +def squeeze_multiple(tensor: torch.Tensor, dims: list) -> torch.Tensor: + """Dynamically squeezes specified singleton dimensions from a tensor. + + Args: + tensor (torch.Tensor): Input tensor. + dims (list): List of dimensions to squeeze. + + Returns: + torch.Tensor: Squeezed tensor. + """ + for dim in sorted(dims, reverse=True): # Sort in reverse to avoid shifting indices + if tensor.shape[dim] == 1: # Only squeeze if dim is 1 + tensor = tensor.squeeze(dim=dim) + return tensor + +class GlobalAvgPooling(nn.Module): + """Performs a simple global pooling over the input with optional paddings. + + Attributes: + pooling_dims: A list of dims to perform pooling over. + keepdims: If True, keep dimension of inputs after pooling. + """ + + pooling_dims: t.Sequence[int] + epsilon: float = 1e-8 + + def __init__( + self, pooling_dims: t.Sequence[int], epsilon: float = 1e-8 + ): + super().__init__() + self.pooling_dims = pooling_dims + self.epsilon = epsilon + + if not all([p_dims >= 0 for p_dims in self.pooling_dims]): + raise ValueError('pooling_dims must be non-negative integers.') + + def __call__( + self, + inputs: torch.tensor, + compatible_paddings: torch.tensor, + ): + """Applies global average spatial pooling to inputs. + + Args: + inputs: An input tensor. + compatible_paddings: paddings of inputs with shapes compatible with + inputs, e.g. compatible_paddings with shape [B, 1] for inputs with shape + [B, D]. + + Returns: + Output tensor with global pooling applied. + """ + padded_value = torch.zeros_like(inputs) + padded_value = torch.ones_like(inputs) * padded_value + inputs = torch.where(compatible_paddings > 0, padded_value, inputs) + valid_inputs = ( + torch.sum( + 1.0 - compatible_paddings, + self.pooling_dims, + keepdims=True, + dtype=inputs.dtype, + ) + + self.epsilon + ) + inputs_sum = torch.sum(inputs, self.pooling_dims, keepdims=True) + outputs = torch.divide(inputs_sum, valid_inputs).type(inputs.dtype) + outputs = torch.squeeze(outputs, axis=self.pooling_dims) + # outputs = squeeze_multiple(outputs, self.pooling_dims) + return outputs + + +class TextEncoder(nn.Module): + """Text encoder implementation.""" + + def __init__( + self, + config: t.Dict[str, int], + vocab_size: int, + dtype: torch.dtype = torch.float32, + scale_sqrt_depth: bool = True, + ): + super().__init__() + self.vocab_size = vocab_size + self.dtype = dtype + self.scale_sqrt_depth = scale_sqrt_depth + + # The text tower layers are fixed independent of vision tower size. + self.transformer_layers = config['num_layers'] + self.embedding_dim = config['hidden_size'] + self.transformer_width = config['hidden_size'] + self.mlp_dim = config['mlp_dim'] + self.transformer_heads = config['num_heads'] + + self.token_embedding = nn.Embedding( + self.vocab_size, self.embedding_dim, dtype=self.dtype + ) + self.pos_embedder = PositionalEmbedding(embedding_dim=self.embedding_dim) + self.transformer = Transformer( + width=self.transformer_width, + layers=self.transformer_layers, + heads=self.transformer_heads, + mlp_dim=self.mlp_dim, + dtype=self.dtype, + ) + self.pooling = GlobalAvgPooling(pooling_dims=[1]) + self.ln_final = nn.LayerNorm(self.transformer_width, dtype=self.dtype) + + def __call__( + self, + ids: torch.tensor, + paddings: torch.tensor, + learnable_prompts: torch.Tensor = None, # New parameter for learnable prompts + learning_method: str = None, # But addition can also be used [concat, sumate, entire_learnable, None] + deep_parameters: torch.nn.ParameterList = None, + device = 'cuda', + ): + # """Applies TextEncoder module.""" + # _, seq_length = ids.shape + + # x = self.token_embedding(ids) + # if self.scale_sqrt_depth: + # x = x * (self.embedding_dim**0.5) + + # mask = (paddings == 0).type(torch.float32) + # mask = mask.permute(1, 0) # NL -> LN + + # x = x + self.pos_embedder(seq_length=seq_length).to(device) + # x = x.permute(1, 0, 2) # NLD -> LND + # x = self.transformer(x, mask) + # x = x.permute(1, 0, 2) # LND -> NLD + # x = self.ln_final(x) + # x = self.pooling(x, compatible_paddings=paddings[:, :, None]) + # return x + + """Applies TextEncoder module with optional prompt learning (fixed input length 64).""" + batch_size, original_seq_length = ids.shape + + x = self.token_embedding(ids) # [B, L, D] + + if self.scale_sqrt_depth: + x = x * (self.embedding_dim ** 0.5) + + x = x.to(device) + if learnable_prompts is not None: + if learning_method == 'concat': + prompts = learnable_prompts.unsqueeze(0).expand(batch_size, -1, -1).to(x.device) # [B, P, D] + x = torch.cat([prompts, x], dim=1) # [B, P+L, D] + paddings = torch.cat([ + torch.zeros((batch_size, prompts.size(1)), device=x.device), + paddings + ], dim=1) + + elif learning_method == 'sumate': + prompt_len = learnable_prompts.size(0) + x[:, :prompt_len, :] += learnable_prompts.unsqueeze(0) + + elif learning_method == 'entire_learnable': + x = learnable_prompts.unsqueeze(0).expand(batch_size, -1, -1) + paddings = torch.zeros((batch_size, x.size(1)), device=x.device) + + # ๐Ÿ”’ Ensure fixed sequence length (truncate or pad) + current_len = x.size(1) + if current_len > original_seq_length: + x = x[:, :original_seq_length, :] + paddings = paddings[:, :original_seq_length] + elif current_len < original_seq_length: + pad_len = original_seq_length - current_len + pad_embed = torch.zeros((batch_size, pad_len, x.size(2)), device=x.device) + pad_mask = torch.ones((batch_size, pad_len), device=x.device) # masked-out padding + x = torch.cat([x, pad_embed], dim=1) + paddings = torch.cat([paddings, pad_mask], dim=1) + + # Positional encoding and transformer + x = x + self.pos_embedder(seq_length=original_seq_length).to(device) + x = x.permute(1, 0, 2) # [L, B, D] + mask = (paddings == 0).float().permute(1, 0) # [L, B] + x = self.transformer(x, mask, deep_tokens=deep_parameters) + x = x.permute(1, 0, 2) # [B, L, D] + x = self.ln_final(x) + x = self.pooling(x, compatible_paddings=paddings[:, :, None]) + + return x diff --git a/Tipsomaly/reproduce.sh b/Tipsomaly/reproduce.sh new file mode 100644 index 0000000000000000000000000000000000000000..352f0c5630d632dfb48d7cd4a3bc2ea3d2c20ac3 --- /dev/null +++ b/Tipsomaly/reproduce.sh @@ -0,0 +1,22 @@ +# This script provides a sample command to test given checkpoints on a dataset +# For testing multiple datasets in a loop, refer to script train_test.sh + +models_dir="/kaggle/working/tips" +data_root_dir="/kaggle/working/datasets" +model_version='l14h' # model version name, like s14h, l14h, g14h for TIPS model and google/siglip2-large-patch16-512 for SigLIP2 model +checkpoint_path="./workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints" +# visa checkpoint at: './workspaces/trained_on_visa_default/vegan-arkansas/checkpoints' + +### Test using industrial fixed prompts + +# test on VisA +python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset visa --epoch 2 --model_version $model_version --fixed_prompt_type industrial + +# test on MVTec +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset mvtec --epoch 2 --model_version $model_version --fixed_prompt_type industrial + +# test on a medical dataset with industrial prompts +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset cvc-colondb --fixed_prompt_type industrial --epoch 2 --model_version $model_version + +# test on a medical dataset with medical prompts +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset headct --fixed_prompt_type medical --epoch 2 --model_version $model_version \ No newline at end of file diff --git a/Tipsomaly/requirements.txt b/Tipsomaly/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..8a10303eb945be71536cbf368f239e620348985b --- /dev/null +++ b/Tipsomaly/requirements.txt @@ -0,0 +1,42 @@ +accelerate==1.11.0 +humanhash3==0.0.6 +humanize==4.14.0 +torch==2.8.0+cu126 +torchao==0.10.0 +torchaudio==2.8.0+cu126 +torchdata==0.11.0 +torchinfo==1.8.0 +torchmetrics==1.8.2 +torchsummary==1.5.1 +torchtune==0.6.1 +torchvision==0.23.0+cu126 +transformers==5.0.0 # NOTE: This version is required only for SigLIP2 experiments +opencv-contrib-python==4.12.0.88 +opencv-python==4.12.0.88 +opencv-python-headless==4.12.0.88 +sentencepiece==0.2.1 +timm==1.0.20 +pillow==11.3.0 +scikit-image==0.25.2 +scikit-learn==1.6.1 +scikit-learn-intelex==2025.10.0 +scikit-multilearn==0.2.0 +scikit-optimize==0.10.2 +scikit-plot==0.3.7 +scikit-surprise==1.1.4 +scipy==1.15.3 +sklearn-pandas==2.2.0 +numpy==2.0.2 +pandas==2.2.2 +pandas-datareader==0.10.0 +pandas-gbq==0.29.2 +pandas-profiling==3.6.6 +pandas-stubs==2.2.2.240909 +pandasql==0.7.3 +tqdm==4.67.1 +PyYAML==6.0.3 +einops==0.8.1 +huggingface_hub==1.4.0 + +# this is a curated and condensed list of the result of pip freeze > requirements.txt +# most of our experiments was conducted on Kaggle on Python 3.11 and 3.12 \ No newline at end of file diff --git a/Tipsomaly/test.py b/Tipsomaly/test.py new file mode 100644 index 0000000000000000000000000000000000000000..7be0ec87a3029206f8a0fa884c868ecbb68fbf39 --- /dev/null +++ b/Tipsomaly/test.py @@ -0,0 +1,336 @@ +import hashlib +import humanhash +import os, sys +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "model")) + +import torch +from torch.utils.data import DataLoader +import torch.nn.functional as F +from torchvision import transforms + +import random +import numpy as np +import pandas as pd +from pathlib import Path + +from tabulate import tabulate +from tqdm import tqdm +import subprocess +import argparse +from scipy.ndimage import gaussian_filter + +from model import tips, omaly +from datasets import input_transforms, dataset +from utils.metrics import image_level_metrics, pixel_level_metrics +from utils.visualize import visualizer +from utils.logger import get_logger, read_train_args +from transformers import AutoProcessor, AutoModel, AutoTokenizer, SiglipTextModel, SiglipVisionModel +from model.siglip2.siglip2_prompt_learnable import SiglipTextModelWithPromptLearning + +#################### + +from model.big_vision import load_siglip + +def setup_seed(seed): + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + np.random.seed(seed) + random.seed(seed) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + +def calc_soft_score(vis_feat, txt_feat, temp): + return F.softmax((vis_feat @ txt_feat.permute(0, 2, 1))/temp, dim=-1) + +def calc_sigm_score(vis_feat, txt_feat, temp, bias): + if vis_feat.dim() < 3: + vis_feat = vis_feat.unsqueeze(dim=1) + tempered_logits = vis_feat @ txt_feat.permute(0, 2, 1) * temp + probs = 1 / (1 + np.exp(-tempered_logits - bias)) + return F.softmax(probs, dim=-1) + +def calc_sigm_score_hf(vis_feat, txt_feat, temp, bias): + if vis_feat.dim() < 3: + vis_feat = vis_feat.unsqueeze(dim=1) + logits = vis_feat @ txt_feat.permute(0, 2, 1) * temp + bias + probs = torch.sigmoid(logits) + return probs + +def regrid_upsample_smooth(flat_scores, size, sigma): + h_w = int(flat_scores.shape[1] ** 0.5) + regrided = flat_scores.reshape(flat_scores.shape[0], h_w, h_w, -1).permute(0, 3, 1, 2) + upsampled = torch.nn.functional.interpolate(regrided, (size, size), mode='bilinear').permute(0, 2, 3, 1) + rough_maps = (1-upsampled[..., 0] + upsampled[..., 1])/2 + assert (rough_maps >= 0).all() and (rough_maps <= 1).all(), "All elements of rough_maps must be between 0 and 1" + anomaly_map = torch.stack([torch.from_numpy(gaussian_filter(map, sigma=sigma)) for map in rough_maps.detach().cpu()], dim=0) + return anomaly_map + +def create_tips(args, device): + # load dataset + transform, target_transform = input_transforms.create_transforms_tips(args.image_size) + + # load model + vision_encoder, text_encoder, tokenizer, temperature = tips.load_model.get_model(args.models_dir, args.model_version) + return vision_encoder.to(device), text_encoder.to(device), text_encoder.transformer.width, tokenizer, transform, target_transform, temperature + +# L/14, 512 +def create_siglip2(args, device): + transform, target_transform = load_siglip.create_preprocessors_siglip2(args.image_size) + vision_encoder, text_encoder, tokenizer = load_siglip.build_siglip_modules(args.model_version, args.image_size) + # model.to(device) + + temperature, bias = text_encoder.params['t'], text_encoder.params['b'] + temperature = np.exp(torch.from_numpy(np.array(temperature))) + return vision_encoder, text_encoder, text_encoder.model.out_dim[1], tokenizer, transform, target_transform, temperature, bias + +def create_siglip2_hf(args, device): + tokenizer = AutoTokenizer.from_pretrained(args.model_version) + model = AutoModel.from_pretrained(args.model_version) + text_encoder = SiglipTextModelWithPromptLearning.from_pretrained(args.model_version).to(device) + vision_encoder = SiglipVisionModel.from_pretrained(args.model_version).to(device) + processor = AutoProcessor.from_pretrained(args.model_version) + def transform(x): + d = processor(images=x, return_tensors="pt") + return d['pixel_values'].squeeze(0) + target_transform = transforms.Compose([ + transforms.Resize((args.image_size, args.image_size)), + transforms.ToTensor(), + ]) + bias = model.logit_bias.to(device) + temperature = model.logit_scale.to(device).exp() + return vision_encoder, text_encoder, model.text_model.embeddings.token_embedding.embedding_dim, tokenizer, transform, target_transform, temperature, bias + +def test(args): + logger = get_logger(args.save_path) + # load dataset + + device = args.device + if args.backbone_name == 'tips': + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature = create_tips(args, device) + calc_score = lambda vis_feat, txt_feat: calc_soft_score(vis_feat, txt_feat, temperature) + + elif args.backbone_name == 'siglip2': + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature, bias = create_siglip2(args, device) + calc_score = lambda vis_feat, txt_feat: calc_sigm_score(vis_feat, txt_feat, temperature, bias) + + elif args.backbone_name == 'siglip2-hf': + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature, bias = create_siglip2_hf(args, device) + calc_score = lambda vis_feat, txt_feat: calc_sigm_score_hf(vis_feat, txt_feat, temperature, bias) + + text_encoder = omaly.text_encoder(tokenizer, bb_text_encoder, args.backbone_name, text_embd_dim, 64, args.prompt_learn_method, args.fixed_prompt_type, args.n_prompt, args.n_deep_tokens, args.d_deep_tokens) + vision_encoder = omaly.vision_encoder(bb_vision_encoder, args.backbone_name) + + # class_names = desc.dataset_dict[args.dataset] + test_data = dataset.Dataset(args.data_path, transform, target_transform, args) + test_loader = DataLoader(test_data, batch_size=args.batch_size, num_workers=4, shuffle=False) + # test_loader = DataLoader(test_data, batch_size=8, shuffle=False, num_workers=1, prefetch_factor=2, pin_memory=True) + fixed_class_names = [clss.replace('_', ' ') for clss in test_data.cls_names] + + # extract features + with torch.no_grad(): + # Fixed prototypes + fixed_text_features = text_encoder(fixed_class_names, device, learned=False) + fixed_text_features = fixed_text_features / fixed_text_features.norm(dim=-1, keepdim=True) # NOTE: For test also + cls_text_features, seg_text_features = fixed_text_features, fixed_text_features + + if args.checkpoint_path: + assert not args.prompt_learn_method == 'none', 'The prompt_learn_method should not be none' + checkpoint = torch.load(args.params_path, weights_only=False) + text_encoder.learnable_prompts = checkpoint["learnable_prompts"] if isinstance(checkpoint, dict) else checkpoint + + # text_encoder.learnable_prompts = chekpoint + # text_encoder.deep_parameters = chekpoint["deep_parameters"] + + learnable_class_names = ['object'] + learnable_class_ids = torch.tensor([0]) + print('The learnable prompts are read') + + # extract features + with torch.no_grad(): + # Learnable prototypes + learnable_text_features = text_encoder(learnable_class_names, device, learned=True) # NOTE: important learned=True + learnable_text_features = learnable_text_features / learnable_text_features.norm(dim=-1, keepdim=True) # NOTE: For test also + cls_text_features, seg_text_features = learnable_text_features, learnable_text_features + + if args.checkpoint_path and args.decoupled_prompt: + cls_text_features, seg_text_features = fixed_text_features, learnable_text_features + + dataset_preds = {cls_id: {'name': test_loader.dataset.cls_names[cls_id], 'img_scrs': [], 'img_lbls': [], 'pxl_scrs': [], 'pxl_lbls': [], 'paths': []} for cls_id in test_loader.dataset.class_ids} + for batch in tqdm(test_loader, desc="Extracting features", unit="batch"): + image = batch['img'].to(device) + label = batch['anomaly'].long().to(device) + abnorm_mask = batch['abnorm_mask'].squeeze(dim=1).to(device) + path = batch['img_path'] + + # Indecies + cls_class_ids, seg_class_ids = batch['cls_id'], batch['cls_id'] + if args.checkpoint_path and args.decoupled_prompt: + seg_class_ids = learnable_class_ids + elif args.checkpoint_path and not args.decoupled_prompt: + cls_class_ids, seg_class_ids = learnable_class_ids, learnable_class_ids + + with torch.no_grad(): + vision_features = vision_encoder(image) + vision_features = [feature / feature.norm(dim=-1, keepdim=True) for feature in vision_features] # NOTE: for test also + + # calculate normal/abnormal scores + img_scr0 = calc_score(vision_features[0], cls_text_features[cls_class_ids]).squeeze(dim=1).detach() # prompt_class_ids cls_ids + img_scr1 = calc_score(vision_features[1], cls_text_features[cls_class_ids]).squeeze(dim=1).detach() + + img_map = calc_score(vision_features[2], seg_text_features[seg_class_ids]) + if args.aggregate_local2global: + max_local = torch.max(img_map, dim=1)[0] + img_scr0 = img_scr0 + max_local + img_scr1 = img_scr1 + max_local + + pxl_scr = regrid_upsample_smooth(img_map.detach(), args.image_size, args.sigma) + + for idx, cls_id in enumerate(batch['cls_id'].cpu().numpy()): + dataset_preds[cls_id]['img_scrs'].append([img_scr0[idx][1].cpu(), img_scr1[idx][1].cpu()]) + dataset_preds[cls_id]['img_lbls'].append(label[idx].cpu()) + dataset_preds[cls_id]['pxl_scrs'].append(pxl_scr[idx].cpu()) + dataset_preds[cls_id]['pxl_lbls'].append(abnorm_mask[idx].cpu()) + dataset_preds[cls_id]['paths'].append(path[idx]) + + # calculate metrics + header = ['objects']+args.pixel_metrics+[mtr for mtr in args.image_metrics for _ in range(2)] + dataset_results = [] + for cls_id in dataset_preds.keys(): + cls_results = [dataset_preds[cls_id]['name']] + img_prds = np.array(dataset_preds[cls_id]['img_scrs']) + img_lbls = np.array(dataset_preds[cls_id]['img_lbls']) + pxl_prds = torch.stack(dataset_preds[cls_id]['pxl_scrs'], dim=0) + pxl_lbls = torch.stack(dataset_preds[cls_id]['pxl_lbls'], dim=0) + print(f'pxl_prds: ({pxl_prds.max()}, {pxl_prds.min()})') + print(f'img_prds: ({img_prds.max()}, {img_prds.min()})') + + for px_mtr in args.pixel_metrics: + if not px_mtr == '': + cls_results.append(pixel_level_metrics(device, pxl_prds, pxl_lbls, px_mtr)*100) + + for im_mtr in args.image_metrics: + for col in range(img_prds.shape[1]): + cls_results.append(image_level_metrics(img_prds[:, col], img_lbls, im_mtr)*100) + + if args.visualize: + img_path = f"{args.dataset}/{dataset_preds[cls_id]['name']}" + visualizer(dataset_preds[cls_id]['paths'], pxl_prds.cpu().numpy(), pxl_lbls.cpu().numpy(), args.image_size, img_path, save_path=f'{args.save_path}/img/', draw_contours=True) + + dataset_results.append(cls_results) + + df = pd.DataFrame(dataset_results, columns=header) + mean_values = ['Mean'] + df.iloc[:, 1:].mean().tolist() + # df = df.append(mean_values) + df.loc[len(df)] = mean_values + df = df.round(2) + + # store the results + results_text = tabulate(df, headers='keys', tablefmt='pretty') + logger.info(results_text) + +def make_human_readable_name(args, exclude=['model_name', 'dataset', 'dataset_category', 'data_path', + 'checkpoint_path', 'training_path', "Timestamp", + "metrics", "device", "available_devices", "epochs", "visualize", 'help', None]): + args=vars(args) + name_value_pairs = [ + f"{k}_{v}" + for k,v in args.items() + if k not in exclude # Exclude "help" or invalid arguments + ] + combined = ",".join(sorted(name_value_pairs)) # Sorting ensures consistent order + hash_value = hashlib.sha256(combined.encode()).hexdigest() + human_hash = humanhash.humanize(hash_value, words=2) # 'hash' # + return human_hash + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +if __name__ == '__main__': + parser = argparse.ArgumentParser("TIPSomaly", add_help=True) + # model + parser.add_argument("--image_size", type=int, default=518, help="image size") #224 if is_low_res else 448 + parser.add_argument("--seed", type=int, default=111, help="random seed") + + parser.add_argument("--metrics", type=str, default='image-pixel-level') + parser.add_argument("--device", type=str, default="cuda", help="type of device, can be cuda or cpu") + parser.add_argument("--available_devices", type=int, nargs='+', default=[0, 1, 2, 3, 4, 5, 6, 7], help="array of possible cuda devices") + parser.add_argument("--model_name", type=str, default="trained_on_visa_siglip2_both_mvtec", help="") + parser.add_argument("--models_dir", type=str, default="./tips", help="directory of the base model of tips") + parser.add_argument("--data_root_dir", type=str, default="./datasets", help="root directory for all datasets to be placed in") + parser.add_argument("--checkpoint_path", type=str, default='None', help="") + parser.add_argument("--epoch", type=int, default=1, help="") + parser.add_argument("--batch_size", type=int, default=8) + + parser.add_argument("--sigma", type=int, default=4, help="zero shot") + + parser.add_argument("--dataset", type=str, default="visa") + parser.add_argument("--dataset_category", type=str, default='', help="train dataset categories") + + parser.add_argument("--class_name", type=str, nargs='+', default=['all'], help="train class name") + + parser.add_argument("--image_metrics", type=str, nargs='+', default=['auroc', 'ap', 'f1-max'], help="") + parser.add_argument("--pixel_metrics", type=str, nargs='+', default=['auroc', 'aupro', 'f1-max'], help="") + + parser.add_argument("--k_shot", type=int, default=0, help="number of samples per class for few-shot learning. 0 means use all data.") + + parser.add_argument("--type", type=str, default='test') + parser.add_argument("--visualize", type=str2bool, default=False) + parser.add_argument("--log_dir", type=str, default="") + + ########################## + ### Method Arguements #### + parser.add_argument("--backbone_name", type=str, default='tips', choices=["tips", "siglip2", "siglip2-hf"]) + parser.add_argument("--model_version", type=str, default='l14h', choices=["s14h","b14h","l14h","so4h","g14l","g14h", \ + "B/16", "L/16", "So400m/14", "So400m/16", "g-opt/16", \ + "google/siglip2-so400m-patch16-256", "google/siglip2-large-patch16-512"]) + + parser.add_argument("--n_deep_tokens", type=int, default=0) + parser.add_argument("--d_deep_tokens", type=int, default=0) + parser.add_argument("--n_prompt", type=int, default=8) + parser.add_argument("--fixed_prompt_type", type=str, default='industrial', choices=['industrial', 'medical', 'object_agnostic']) + + parser.add_argument("--prompt_learn_method", type=str, default='concat', choices=['concat', 'sumate', 'entire_learnable', 'none']) + parser.add_argument("--decoupled_prompt", type=str2bool, default=True) + parser.add_argument("--aggregate_local2global", type=str2bool, default=True) + + args = parser.parse_args() + + if 'CUDA_VISIBLE_DEVICES' not in os.environ: + os.environ['CUDA_VISIBLE_DEVICES'] = ','.join(map(str, args.available_devices)) if len(args.available_devices) > 1 else str(args.available_devices[0]) + command = [sys.executable, __file__, ] + sys.argv[1:] + process = subprocess.Popen(command, env=os.environ) + process.wait() + + else: + setup_seed(args.seed) + + #### ONLY KAGGLE + # args.dataset = f'{args.dataset}-ad' + # base_paths = [Path(p) for p in [f'{DATA_ROOT_DIR}/{args.dataset_category}/{args.dataset}/']] + # args.data_path = [str(next(p.iterdir())) for p in base_paths] + + args.data_path = [f'{args.data_root_dir}/{args.dataset_category}/{args.dataset}/'] + if not args.checkpoint_path: + args.log_dir = make_human_readable_name(args) + args.save_path = f'./workspaces/{args.model_name}/{args.log_dir}/quantative/NoTrain/{args.dataset}' + else: # ./workspaces/test/blah-blah/checkpoints/ + splits = args.checkpoint_path.split('/') + args.params_path = f'{args.checkpoint_path}/learnable_params_{args.epoch}.pth' + args.model_name = splits[-3] + args.log_dir = splits[-2] + args.save_path = f'{"/".join(splits[:-1])}/quantative/epoch_{args.epoch}/{args.dataset}' + ### + train_args = read_train_args(args.checkpoint_path) # ./workspaces/{args.model_name}/{args.log_dir}/args.txt + args.prompt_learn_method = train_args['prompt_learn_method'] + assert not train_args['prompt_learn_method'] is None, 'prompt_learn_method should not be none' + + print(args) + print(f"Data Path: {args.data_path}, Log Directory: {args.log_dir}, Save Path: {args.save_path}") + test(args) \ No newline at end of file diff --git a/Tipsomaly/train.py b/Tipsomaly/train.py new file mode 100644 index 0000000000000000000000000000000000000000..5360e40fb24483f64668974453699115cf746afe --- /dev/null +++ b/Tipsomaly/train.py @@ -0,0 +1,353 @@ +import os, sys +sys.path.insert(0, os.path.join(os.path.dirname(__file__), "model")) +import subprocess +import argparse +import hashlib +import humanhash +from collections import defaultdict +from tqdm import tqdm + +import random +import numpy as np +from scipy.ndimage import gaussian_filter + +import torch +import torch.nn.functional as F +from torchvision import transforms +from torch.utils.data import DataLoader +from transformers import AutoProcessor, AutoModel, AutoTokenizer, SiglipTextModel, SiglipVisionModel + +from datasets import input_transforms, dataset +from utils.loss import FocalLoss, BinaryDiceLoss +from utils.logger import save_args_to_file, get_logger +from torch.utils.tensorboard import SummaryWriter + +from model import tips +from model import omaly +from model.big_vision import load_siglip +from model.siglip2.siglip2_prompt_learnable import SiglipTextModelWithPromptLearning + +loss_names = {'img_ls_ce': 'LS CE', 'pxl_ls_fc': 'LS FC', \ + 'plx_ls_dc_p': 'LS DC P', 'plx_ls_dc_n': 'LS DC N', \ + 'emb_l1_nrm': 'LS L1 NRM', 'epc_ls': 'total'} + +def setup_seed(seed): + torch.manual_seed(seed) + torch.cuda.manual_seed_all(seed) + np.random.seed(seed) + random.seed(seed) + torch.backends.cudnn.deterministic = True + torch.backends.cudnn.benchmark = False + +def seed_worker(worker_id): + worker_seed = 111 + worker_id + np.random.seed(worker_seed) + random.seed(worker_seed) + +def calc_soft_score(vis_feat, txt_feat, temp): + return F.softmax((vis_feat @ txt_feat.permute(0, 2, 1))/temp, dim=-1) + +def calc_sigm_score(vis_feat, txt_feat, temp, bias): + if vis_feat.dim() < 3: + vis_feat = vis_feat.unsqueeze(dim=1) + tempered_logits = vis_feat @ txt_feat.permute(0, 2, 1) * temp + probs = 1 / (1 + np.exp(-tempered_logits - bias)) + return F.softmax(probs, dim=-1) + +def calc_sigm_score_hf(vis_feat, txt_feat, temp_non_exp, bias): + if vis_feat.dim() < 3: + vis_feat = vis_feat.unsqueeze(dim=1) + logits = vis_feat @ txt_feat.permute(0, 2, 1) * temp_non_exp.exp() + bias + probs = torch.sigmoid(logits) + return probs + +def create_tips(args, device): + # load dataset + transform, target_transform = input_transforms.create_transforms_tips(args.image_size) + + # load model + vision_encoder, text_encoder, tokenizer, temperature = tips.load_model.get_model(args.models_dir, args.model_version) + return vision_encoder.to(device), text_encoder.to(device), text_encoder.transformer.width, tokenizer, transform, target_transform, temperature + +def create_siglip2(args, device): + transform, target_transform = load_siglip.create_preprocessors_siglip2(args.image_size) + vision_encoder, text_encoder, tokenizer = load_siglip.build_siglip_modules(args.model_version, args.image_size) + # model.to(device) + + temperature, bias = text_encoder.params['t'], text_encoder.params['b'] + temperature = np.exp(torch.from_numpy(np.array(temperature))) + return vision_encoder, text_encoder, text_encoder.model.out_dim[1], tokenizer, transform, target_transform, temperature, bias + +def create_siglip2_hf(args, device): + tokenizer = AutoTokenizer.from_pretrained(args.model_version) + model = AutoModel.from_pretrained(args.model_version) + text_encoder = SiglipTextModelWithPromptLearning.from_pretrained(args.model_version).to(device) + vision_encoder = SiglipVisionModel.from_pretrained(args.model_version).to(device) + processor = AutoProcessor.from_pretrained(args.model_version) + def transform(x): + d = processor(images=x, return_tensors="pt") + return d['pixel_values'].squeeze(0) + target_transform = transforms.Compose([ + transforms.Resize((args.image_size, args.image_size)), + transforms.ToTensor(), + ]) + bias = model.logit_bias.to(device) + temp_non_exp = model.logit_scale.to(device) + return vision_encoder, text_encoder, model.text_model.embeddings.token_embedding.embedding_dim, tokenizer, transform, target_transform, temp_non_exp, bias + +def regrid_upsample_smooth(flat_scores, size, sigma): + upsampled = regrid_upsample(flat_scores, size) + anomaly_map = torch.stack([torch.from_numpy(gaussian_filter(map, sigma=sigma)) for map in upsampled.detach().cpu()], dim=0) + return anomaly_map + +def regrid_upsample(flat_scores, size): + h_w = int(flat_scores.shape[1] ** 0.5) + regrided = flat_scores.reshape(flat_scores.shape[0], h_w, h_w, -1).permute(0, 3, 1, 2) + upsampled = torch.nn.functional.interpolate(regrided, (size, size), mode='bilinear').permute(0, 2, 3, 1) + return upsampled + +def turn_gradient_off(model): + print("Turning off gradients in both the image and the text encoder") + for _, param in model.named_parameters(): + param.requires_grad_(False) + + enabled = set() + for name, param in model.named_parameters(): + if param.requires_grad: + enabled.add(name) + # print(f"Parameters to be updated: {enabled}") + + model.eval() + return model + +def train(args): + epochs = args.epoch + device = args.device + + writer = SummaryWriter(log_dir=args.experiment_root) + logger = get_logger(args.experiment_root) + + if args.backbone_name == 'tips': + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature = create_tips(args, device) + calc_score = lambda vis_feat, txt_feat: calc_soft_score(vis_feat, txt_feat, temperature) + elif args.backbone_name == "siglip2": + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature, bias = create_siglip2(args, device) + calc_score = lambda vis_feat, txt_feat: calc_sigm_score(vis_feat, txt_feat, temperature, bias) + elif args.backbone_name == 'siglip2-hf': + bb_vision_encoder, bb_text_encoder, text_embd_dim, tokenizer, transform, target_transform, temperature, bias = create_siglip2_hf(args, device) + calc_score = lambda vis_feat, txt_feat: calc_sigm_score_hf(vis_feat, txt_feat, temperature, bias) + + bb_text_encoder = bb_text_encoder.to(device) + bb_vision_encoder = bb_vision_encoder.to(device) + bb_text_encoder = turn_gradient_off(bb_text_encoder) + bb_vision_encoder = turn_gradient_off(bb_vision_encoder) + text_encoder = omaly.text_encoder(tokenizer, bb_text_encoder, args.backbone_name, text_embd_dim, 64, args.prompt_learn_method, args.fixed_prompt_type, args.n_prompt, args.n_deep_tokens, args.d_deep_tokens) + vision_encoder = omaly.vision_encoder(bb_vision_encoder, args.backbone_name) + + + # load dataset + # class_names = desc.dataset_dict[args.dataset] + train_data = dataset.Dataset(args.data_path, transform, target_transform, args) + + g = torch.Generator() + g.manual_seed(args.seed) + train_loader = DataLoader(train_data, batch_size=args.batch_size, num_workers=4, shuffle=False) + + # class_names = [clss.replace('_', ' ') for clss in train_data.cls_names] + # class_ids = train_data.class_ids + class_names = ['object'] + class_ids = torch.tensor([0]) + + # Define losses + bce_loss = torch.nn.CrossEntropyLoss() + loss_focal = FocalLoss() + loss_dice = BinaryDiceLoss() + + # Define optimizer + optimizer = torch.optim.Adam( + list(text_encoder.learnable_prompts),# + list(text_encoder.deep_parameters), + lr=args.learning_rate, + betas=(0.5, 0.999) + ) + train_stats = defaultdict(list) + + torch.autograd.set_detect_anomaly(True) + train_loader_cpu = [bat for bat in train_loader] + + global_step = 0 + + text_encoder.train() + text_encoder.to(device) + vision_encoder.train() + vision_encoder.to(device) + for epoch in range(epochs): # Add epoch loop + print(f"Epoch {epoch + 1}/{epochs}") + epoch_loss = defaultdict(int) + + for batch in tqdm(train_loader_cpu, desc="Train", unit="batch"): + image = batch['img'].to(device) + # cls_ids = batch['cls_id'] + label = batch['anomaly'].long().to(device) + abnorm_mask = batch['abnorm_mask'].squeeze(dim=1).to(device) + + # extract features + text_features = text_encoder(class_names, device, learned=True) + text_features = text_features / text_features.norm(dim=-1, keepdim=True) # NOTE: For test also + with torch.no_grad(): + vision_features = vision_encoder(image) + vision_features = [feature / feature.norm(dim=-1, keepdim=True) for feature in vision_features] # NOTE: for test also + + # calculate normal/abnormal scores (since TIPS has two global visual embeddings we have two calculated image-level scores) + img_scr0 = calc_score(vision_features[0], text_features[class_ids]).squeeze(dim=1) + img_scr1 = calc_score(vision_features[1], text_features[class_ids]).squeeze(dim=1) + + img_map = calc_score(vision_features[2], text_features[class_ids]) + anomaly_map = regrid_upsample(img_map, args.image_size) + abnorm_mask[abnorm_mask > 0.5], abnorm_mask[abnorm_mask< 0.5] = 1, 0 + + # Calculate loss + anomaly_map = anomaly_map.permute(0, 3, 1, 2) + ls_fc = loss_focal(anomaly_map, abnorm_mask) + ls_dc_p = loss_dice(anomaly_map[:, 1, :, :], abnorm_mask) + ls_dc_n = loss_dice(anomaly_map[:, 0, :, :], 1-abnorm_mask) + + ls_cls = bce_loss(img_scr0, label) + bce_loss(img_scr1, label) + ls_seg = ls_fc + ls_dc_p + ls_dc_n # (pixel loss) + if args.cls_seg_los == 'both': # (image loss) + loss_total = ls_cls + ls_seg + elif args.cls_seg_los == 'seg': + loss_total = ls_seg + elif args.cls_seg_los == 'cls': + loss_total = ls_cls + + # L1 Regularization term + l1_norm = torch.sum(torch.abs(text_features)) + loss_total = loss_total + l1_norm * args.l1_lambda + + # Train + optimizer.zero_grad() + loss_total.backward() + optimizer.step() + + # log + epoch_loss['img_ls_ce'] += ls_cls.item() + epoch_loss['pxl_ls_fc'] += ls_fc.item() + epoch_loss['plx_ls_dc_p'] += ls_dc_p.item() + epoch_loss['plx_ls_dc_n'] += ls_dc_n.item() + epoch_loss['epc_ls'] += loss_total.item() + epoch_loss['emb_l1_nrm'] += l1_norm.item() + + # Tensorboard update for each batch + writer.add_scalar(f"Loss/img_ls_ce", ls_cls.item(), global_step) + writer.add_scalar(f"Loss/pxl_ls_fc", ls_fc.item(), global_step) + writer.add_scalar(f"Loss/plx_ls_dc_p", ls_dc_p.item(), global_step) + writer.add_scalar(f"Loss/plx_ls_dc_n", ls_dc_n.item(), global_step) + writer.add_scalar(f"Loss/epc_ls", loss_total.item(), global_step) + writer.add_scalar(f"Loss/emb_l1_nrm", l1_norm.item(), global_step) + global_step += 1 + + # Calc epoch mean loss + num_batches = len(train_loader) + for key, val in epoch_loss.items(): + train_stats[key].append(val / num_batches) + + # Print mean losses at the end of the epoch + epoch_details = f"Epoch {epoch + 1} Mean Losses: " + for key, val in epoch_loss.items(): + epoch_details = epoch_details + f"{loss_names[key]}: {train_stats[key][-1]:.4f}, " + logger.info(epoch_details[:-2]) + + torch.save({"learnable_prompts":text_encoder.learnable_prompts}, + f'{args.save_path}/learnable_params_{epoch+1}.pth') + # "deep_parameters":text_encoder.deep_parameters}, + print(f'checkpoints saved for epoch {epoch+1}.') + +def make_human_readable_name(args, exclude=['model_name', 'dataset', 'dataset_category', 'epoch', 'data_path', + 'checkpoint_path', 'training_path', "Timestamp", + "metrics", "device", "available_devices", "epochs", "visualize", 'help', None]): + args=vars(args) + name_value_pairs = [ + f"{k}_{v}" + for k,v in args.items() + if k not in exclude # Exclude "help" or invalid arguments + ] + combined = ",".join(sorted(name_value_pairs)) # Sorting ensures consistent order + hash_value = hashlib.sha256(combined.encode()).hexdigest() + human_hash = humanhash.humanize(hash_value, words=2) + return human_hash.replace('-', '_') + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') + +if __name__ == '__main__': + + dss = ['mvtec'] + + parser = argparse.ArgumentParser("TIPSomaly", add_help=True) + # model + parser.add_argument("--image_size", type=int, default=518, help="image size") + parser.add_argument("--seed", type=int, default=111, help="random seed") + + parser.add_argument("--epoch", type=int, default=5, help="epochs") + parser.add_argument("--learning_rate", type=float, default=0.001) + + parser.add_argument("--metrics", type=str, default='image-pixel-level') + parser.add_argument("--device", type=str, default="cuda", help="type of device, can be cuda or cpu") + parser.add_argument("--available_devices", type=int, nargs='+', default=[0, 1, 2, 3, 4, 5, 6, 7], help="array of possible cuda devices") + parser.add_argument("--model_name", type=str, default="tips_test", help="cuda device") + parser.add_argument("--models_dir", type=str, default="./tips", help="directory of the base model of tips") + parser.add_argument("--data_root_dir", type=str, default="./datasets", help="root directory for all datasets to be placed in") + parser.add_argument("--batch_size", type=int, default=8) + + parser.add_argument("--sigma", type=int, default=4, help="zero shot") + + parser.add_argument("--dataset", type=str, default="visa") + parser.add_argument("--dataset_category", type=str, default='', help="train dataset categories") + + parser.add_argument("--type", type=str, default='train') + parser.add_argument("--class_name", type=str, nargs='+', default=['all'], help="train class name") + parser.add_argument("--k_shot", type=int, default=0, help="number of samples per class for few-shot learning. 0 means use all data.") + + ########################## + ### Method Arguements #### + parser.add_argument("--model_version", type=str, default='l14h', choices=["s14h","b14h","l14h","so4h","g14l","g14h", \ + "B/16", "L/16", "So400m/14", "So400m/16", "g-opt/16", \ + "google/siglip2-so400m-patch16-256", "google/siglip2-large-patch16-512"]) + parser.add_argument("--n_deep_tokens", type=int, default=0) + parser.add_argument("--d_deep_tokens", type=int, default=0) + parser.add_argument("--n_prompt", type=int, default=8) + parser.add_argument("--fixed_prompt_type", type=str, default='industrial') + + parser.add_argument("--prompt_learn_method", type=str, default='concat', choices=['concat', 'sumate', 'entire_learnable', 'none']) + parser.add_argument("--cls_seg_los", type=str, default='seg', choices=['both', 'seg', 'cls']) + parser.add_argument("--l1_lambda", type=float, default=0.0) + parser.add_argument("--backbone_name", type=str, default='tips', choices=["tips", "siglip2", "siglip2-hf"]) + + args = parser.parse_args() + + command = [sys.executable, __file__, ] + sys.argv[1:] + if 'CUDA_VISIBLE_DEVICES' not in os.environ: + os.environ['CUDA_VISIBLE_DEVICES'] = ','.join(map(str, args.available_devices)) if len(args.available_devices) > 1 else str(args.available_devices[0]) + process = subprocess.Popen(command, env=os.environ) + process.wait() + + else: + print(args) + setup_seed(args.seed) + args.log_dir = make_human_readable_name(args) + args.data_path = [f'{args.data_root_dir}/{args.dataset_category}/{args.dataset}/'] + args.experiment_root = f'./workspaces/trained_on_{args.dataset}_{args.model_name}/{args.log_dir}' + args.save_path = f'{args.experiment_root}/checkpoints' + os.makedirs(args.save_path, exist_ok=True) + + save_args_to_file(args, command) # ./workspaces/{args.model_name}/{args.log_dir}/args.txt + + print(f"Data Path: {args.data_path}, Log Directory: {args.log_dir}, Save Path: {args.save_path}") + train(args) \ No newline at end of file diff --git a/Tipsomaly/train_test.sh b/Tipsomaly/train_test.sh new file mode 100644 index 0000000000000000000000000000000000000000..a8c884f6cfb77f36bae5794df5fa346d5d24aea3 --- /dev/null +++ b/Tipsomaly/train_test.sh @@ -0,0 +1,34 @@ +# This script provides the command to train your model + +# By setting "model_name" and "log_dir" you define the path for the checkpoints to be saved and +# you can use the same values later to test on other datasets in a loop easily +model_version='l14h' # l14h or l14 +model_name=$1 +epoch=$2 + +models_dir="/kaggle/working/tips" +data_root_dir="/kaggle/working/datasets" +# checkpoint_path="/kaggle/working/checkpoints" # uncomment for testing results + +# Train on MVTec +python train.py --models_dir $models_dir --model_name $model_name --data_root_dir $data_root_dir --dataset mvtec --cls_seg_los seg --l1_lambda 0.0 --d_deep_tokens 0 --n_deep_tokens 0 --epoch $epoch --model_version $model_version --fixed_prompt_type industrial + +# Train on VisA +# python train.py --models_dir $models_dir --model_name $model_name --data_root_dir $data_root_dir --dataset visa --cls_seg_los seg --l1_lambda 0.0 --d_deep_tokens 0 --n_deep_tokens 0 --epoch $epoch --model_version $model_version --fixed_prompt_type industrial + + +# Test multiple datasets in a loop + +# for dataset in visa mpdd btad sdd dagm dtd; do +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset $dataset --epoch $epoch --model_version $model_version --fixed_prompt_type industrial +# done + +# Medical segmentation datasets - using learned prompts +# for dataset in isic tn3k cvc-colondb cvc-clinicdb; do +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset $dataset --fixed_prompt_type industrial --epoch $epoch --model_version $model_version +# done + +# Medical classification datasets - using medical prompts +# for dataset in headct brainmri br35h; do +# python test.py --models_dir $models_dir --checkpoint_path $checkpoint_path --data_root_dir $data_root_dir --dataset $dataset --fixed_prompt_type medical --epoch $epoch --model_version $model_version +# done diff --git a/Tipsomaly/utils/logger.py b/Tipsomaly/utils/logger.py new file mode 100644 index 0000000000000000000000000000000000000000..87b40a32613f39b434bd3c3e96c18ab1e7971592 --- /dev/null +++ b/Tipsomaly/utils/logger.py @@ -0,0 +1,72 @@ + +import logging +import os +import datetime + +def read_train_args(training_path): + # Check if args.txt exists in the training_path + args_file_path = os.path.join(training_path, 'args.txt') + configurations_dict = {} # Dictionary to store configurations + last_config = {} + if os.path.exists(args_file_path): + with open(args_file_path, 'r') as f: + # Read the entire content of the file + file_content = f.read().strip() + + # Split the content into different configurations based on the 'Timestamp' keyword + configurations = file_content.split('Timestamp:') + + # Iterate over each configuration to populate the dictionary + for config in configurations: + if config.strip(): + # Convert the configuration to a dictionary + file_args_dict = {} + for line in config.strip().split('\n'): + if ':' in line: + key, value = line.split(':', 1) + file_args_dict[key.strip()] = value.strip() + + # Store the configuration in the dictionary with a unique key + timestamp = file_args_dict.get('Timestamp', 'Unknown') + configurations_dict[timestamp] = file_args_dict + + if configurations_dict: + last_timestamp = max(configurations_dict.keys()) + last_config = configurations_dict[last_timestamp] + else: + print('train args does not exists') + return last_config + +def save_args_to_file(args, command, log_dir=''): + args_file_path = os.path.join(args.save_path, log_dir, 'args.txt') + os.makedirs(os.path.dirname(args_file_path), exist_ok=True) + if os.path.exists(args_file_path): + print(f"Warning: The file {args_file_path} already exists and will be overwritten.") + with open(args_file_path, 'a') as f: # Change 'w' to 'a' to append to the file + f.write("\n") # Add new line before writing to the file + f.write(f"Timestamp: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") # Add timestamp + for arg, value in vars(args).items(): + f.write(f"{arg}: {value}\n") + f.write(f"Command arguments: {' '.join(command)}\n") # Add the command arguments to the file + +def get_logger(save_path): + if not os.path.exists(save_path): + os.makedirs(save_path) + + txt_path = os.path.join(save_path, 'log.txt') + # logger + root_logger = logging.getLogger() + for handler in root_logger.handlers[:]: + root_logger.removeHandler(handler) + root_logger.setLevel(logging.WARNING) + logger = logging.getLogger('test') + formatter = logging.Formatter('%(asctime)s.%(msecs)03d - %(levelname)s: %(message)s', + datefmt='%y-%m-%d %H:%M:%S') + logger.setLevel(logging.INFO) + file_handler = logging.FileHandler(txt_path, mode='a') + file_handler.setFormatter(formatter) + logger.addHandler(file_handler) + console_handler = logging.StreamHandler() + console_handler.setFormatter(formatter) + logger.addHandler(console_handler) + return logger \ No newline at end of file diff --git a/Tipsomaly/utils/loss.py b/Tipsomaly/utils/loss.py new file mode 100644 index 0000000000000000000000000000000000000000..14712b14dedcc1bcfe98d02530dd28b38ffacbcd --- /dev/null +++ b/Tipsomaly/utils/loss.py @@ -0,0 +1,107 @@ +import numpy as np +import torch +import torch.nn as nn +import torch.nn.functional as F +from math import exp + +class FocalLoss(nn.Module): + """ + copy from: https://github.com/Hsuxu/Loss_ToolBox-PyTorch/blob/master/FocalLoss/FocalLoss.py + This is a implementation of Focal Loss with smooth label cross entropy supported which is proposed in + 'Focal Loss for Dense Object Detection. (https://arxiv.org/abs/1708.02002)' + Focal_Loss= -1*alpha*(1-pt)*log(pt) + :param alpha: (tensor) 3D or 4D the scalar factor for this criterion + :param gamma: (float,double) gamma > 0 reduces the relative loss for well-classified examples (p>0.5) putting more + focus on hard misclassified example + :param smooth: (float,double) smooth value when cross entropy + :param balance_index: (int) balance class index, should be specific when alpha is float + :param size_average: (bool, optional) By default, the losses are averaged over each loss element in the batch. + """ + + def __init__(self, apply_nonlin=None, alpha= None, gamma=2, balance_index=1, smooth=1e-5, size_average=True): + super(FocalLoss, self).__init__() + self.apply_nonlin = apply_nonlin + self.alpha = alpha + self.gamma = gamma + self.balance_index = balance_index + self.smooth = smooth + self.size_average = size_average + + if self.smooth is not None: + if self.smooth < 0 or self.smooth > 1.0: + raise ValueError('smooth value should be in [0,1]') + + def forward(self, logit, target): + if self.apply_nonlin is not None: + logit = self.apply_nonlin(logit) + num_class = logit.shape[1] + + if logit.dim() > 2: + # N,C,d1,d2 -> N,C,m (m=d1*d2*...) + logit = logit.view(logit.size(0), logit.size(1), -1) + logit = logit.permute(0, 2, 1).contiguous() + logit = logit.view(-1, logit.size(-1)) + target = torch.squeeze(target, 1) + target = target.view(-1, 1) + alpha = self.alpha + + if alpha is None: + alpha = torch.ones(num_class, 1) + elif isinstance(alpha, (list, np.ndarray)): + assert len(alpha) == num_class + alpha = torch.FloatTensor(alpha).view(num_class, 1) + alpha = alpha / alpha.sum() + elif isinstance(alpha, float): + alpha = torch.ones(num_class, 1) + alpha = alpha * (1 - self.alpha) + alpha[self.balance_index] = self.alpha + + else: + raise TypeError('Not support alpha type') + + if alpha.device != logit.device: + alpha = alpha.to(logit.device) + + idx = target.cpu().long() + + one_hot_key = torch.FloatTensor(target.size(0), num_class).zero_() + one_hot_key = one_hot_key.scatter_(1, idx, 1) + if one_hot_key.device != logit.device: + one_hot_key = one_hot_key.to(logit.device) + + if self.smooth: + one_hot_key = torch.clamp( + one_hot_key, self.smooth / (num_class - 1), 1.0 - self.smooth) + pt = (one_hot_key * logit).sum(1) + self.smooth + logpt = pt.log() + + gamma = self.gamma + + alpha = alpha[idx] + alpha = torch.squeeze(alpha) + loss = -1 * alpha * torch.pow((1 - pt), gamma) * logpt + + if self.size_average: + loss = loss.mean() + return loss + + +class BinaryDiceLoss(nn.Module): + def __init__(self): + super(BinaryDiceLoss, self).__init__() + + def forward(self, input, targets): + # ่Žทๅ–ๆฏไธชๆ‰นๆฌก็š„ๅคงๅฐ N + N = targets.size()[0] + # ๅนณๆป‘ๅ˜้‡ + smooth = 1 + # ๅฐ†ๅฎฝ้ซ˜ reshape ๅˆฐๅŒไธ€็บฌๅบฆ + input_flat = input.view(N, -1) + targets_flat = targets.view(N, -1) + + # ่ฎก็ฎ—ไบค้›† + intersection = input_flat * targets_flat + N_dice_eff = (2 * intersection.sum(1) + smooth) / (input_flat.sum(1) + targets_flat.sum(1) + smooth) + # ่ฎก็ฎ—ไธ€ไธชๆ‰นๆฌกไธญๅนณๅ‡ๆฏๅผ ๅ›พ็š„ๆŸๅคฑ + loss = 1 - N_dice_eff.sum() / N + return loss diff --git a/Tipsomaly/utils/metrics.py b/Tipsomaly/utils/metrics.py new file mode 100644 index 0000000000000000000000000000000000000000..f77e5e6ef4068c943020c9ec240b08667959e852 --- /dev/null +++ b/Tipsomaly/utils/metrics.py @@ -0,0 +1,84 @@ +import torch +import numpy as np +from skimage import measure +from torchmetrics import AUROC, AveragePrecision +from sklearn.metrics import auc, roc_auc_score, average_precision_score, precision_recall_curve + +def calc_f1_max(gt, pr): + precisions, recalls, _ = precision_recall_curve(gt, pr) + f1_scores = (2 * precisions * recalls) / (precisions + recalls) + return np.max(f1_scores[np.isfinite(f1_scores)]) + + +def cal_pro_score_gpu(device, masks, amaps, max_step=200, expect_fpr=0.3): + # GPU implementation using PyTorch + if not torch.is_tensor(amaps): + amaps = torch.tensor(amaps) + amaps = amaps.to(device) + masks = masks.to(device) + + binary_amaps = torch.zeros_like(amaps, dtype=torch.bool, device=device) + min_th, max_th = amaps.min().item(), amaps.max().item() + delta = (max_th - min_th) / max_step + pros, fprs, ths = [], [], [] + + regionprops_list = [measure.regionprops(measure.label(mask.cpu().numpy())) for mask in masks] + coords_list = [[(region.coords[:, 0], region.coords[:, 1], len(region.coords)) for region in regionprops] for regionprops in regionprops_list] + inverse_masks = 1 - masks + tn_pixel = inverse_masks.sum().item() # Pixels that truly has the label of 0 + for th in torch.arange(min_th, max_th, delta, device=device): + binary_amaps[amaps <= th], binary_amaps[amaps > th] = 0, 1 + pro = [] + + for binary_amap, regions_coords in zip(binary_amaps, coords_list): + for coords in regions_coords: + tp_pixels = binary_amap[coords[0], coords[1]].sum().item() + pro.append(tp_pixels / coords[2]) + + fp_pixels = torch.logical_and(inverse_masks, binary_amaps).sum().item() + fpr = fp_pixels / tn_pixel + pros.append(np.mean(pro)) + fprs.append(fpr) + ths.append(th.item()) + + pros, fprs, ths = torch.tensor(pros, device=device), torch.tensor(fprs, device=device), torch.tensor(ths, device=device) + idxes = fprs < expect_fpr + fprs = fprs[idxes] + fprs = (fprs - fprs.min()) / (fprs.max() - fprs.min()) + pro_auc = auc(fprs.cpu().numpy(), pros[idxes].cpu().numpy()) + return pro_auc + +def image_level_metrics(prd, lbl, metric): + if len(np.unique(lbl)) < 2: + print("only one class present, can not calculate image metrics") + return 0 + + if metric == 'auroc': + performance = roc_auc_score(lbl, prd) + elif metric == 'ap': + performance = average_precision_score(lbl, prd) + elif metric == 'f1-max': + performance = calc_f1_max(lbl, prd) + return performance + +def pixel_level_metrics(device, prd, lbl, metric): + if torch.unique(lbl).numel() < 2: + print("only one class present, can not calculate pixel metrics") + return 0 + + if metric == 'auroc': + performance = AUROC(task="binary")(prd, lbl.to(dtype=torch.long)).item() + + elif metric == 'aupro': + if len(lbl.shape) == 4: + lbl = lbl.squeeze(1) + if len(prd.shape) == 4: + prd = prd.squeeze(1) + performance = cal_pro_score_gpu(device, lbl, prd) + + elif metric == 'ap': + performance = AveragePrecision(task="binary")(prd, lbl.to(dtype=torch.long)).item() + + elif metric == 'f1-max': + performance = calc_f1_max(lbl.cpu().ravel(), prd.cpu().ravel()) + return performance diff --git a/Tipsomaly/utils/visualize.py b/Tipsomaly/utils/visualize.py new file mode 100644 index 0000000000000000000000000000000000000000..a7afd52f4a9fe223d2a14535a0c0ebf464e68295 --- /dev/null +++ b/Tipsomaly/utils/visualize.py @@ -0,0 +1,55 @@ +import cv2 +import os +import numpy as np +import torch +import matplotlib.pyplot as plt +from PIL import Image + +def normalize(pred, max_value=None, min_value=None): + if max_value is None or min_value is None: + return (pred - pred.min()) / (pred.max() - pred.min()) + else: + return (pred - min_value) / (max_value - min_value) + +def apply_ad_scoremap(image, scoremap, alpha=0.5): + np_image = np.asarray(image, dtype=float) + # Convert scoremap from a PyTorch tensor to a NumPy array + if isinstance(scoremap, torch.Tensor): + scoremap = scoremap.detach().cpu().numpy() # Convert tensor to NumPy array + scoremap = (scoremap * 255).astype(np.uint8) + scoremap = cv2.applyColorMap(scoremap, cv2.COLORMAP_JET) + scoremap = cv2.cvtColor(scoremap, cv2.COLOR_BGR2RGB) + return (alpha * np_image + (1 - alpha) * scoremap).astype(np.uint8) + +def visualizer(pathes, anomaly_map, masks, img_size, cls_name, save_path='./vis_img/', draw_contours=True): + for idx, path in enumerate(pathes): + cls = path.split('/')[-2] + filename = path.split('/')[-1] + + # Modify filename if contours are enabled + if draw_contours: + filename_ctr = filename.split('.')[0] + "_cntr." + filename.split('.')[-1] # Append '_cntr' before file extension + + # Load original image and resize + vis = cv2.cvtColor(cv2.resize(cv2.imread(path), (img_size, img_size)), cv2.COLOR_BGR2RGB) + + # Use the provided mask (it's guaranteed to be available) + gt_mask = (masks[idx] > 0).astype(np.uint8) * 255 # Convert to binary (0 or 255) + + # Normalize and apply anomaly map + mask = normalize(anomaly_map[idx]) + vis = apply_ad_scoremap(vis, mask) + + # Convert back to BGR for OpenCV + vis = cv2.cvtColor(vis, cv2.COLOR_RGB2BGR) + + # Save the final visualization + save_vis = os.path.join(save_path, str(cls_name), str(cls)) + os.makedirs(save_vis, exist_ok=True) + cv2.imwrite(os.path.join(save_vis, filename), vis) + + # Find and overlay contours (only if draw_contours is True) + if draw_contours: + contours, _ = cv2.findContours(gt_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + cv2.drawContours(vis, contours, -1, (120, 251, 120), 2) # Pale green contours + cv2.imwrite(os.path.join(save_vis, filename_ctr), vis) diff --git a/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/args.txt b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/args.txt new file mode 100644 index 0000000000000000000000000000000000000000..386fc1f30fca76e21331637e9fbc29b2f687742e --- /dev/null +++ b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/args.txt @@ -0,0 +1,46 @@ + +Timestamp: 2025-08-24 02:23:07 +image_size: 518 +seed: 111 +metrics: image-pixel-level +devices: [3] +model_name: prompt_learning_segonly_518 +sigma: 4 +dataset: ['mvtec'] +dataset_category: +class_name: ['all'] +k_shot: 0 +type: train +log_dir: vegan-arkansas +image_metrics: ['auroc', 'ap', 'f1-max'] +pixel_metrics: ['auroc', 'aupro', 'f1-max'] +visualize: True +prompt_learn_method: concat +cls_seg_los: seg +data_path: ['/data/alireza/datasets//mvtec/'] +experiment_root: ./workspaces/trained_on_mvtec_prompt_learning_segonly_518/vegan-arkansas +save_path: ./workspaces/trained_on_mvtec_prompt_learning_segonly_518/vegan-arkansas/checkpoints +Command arguments: /home/alireza/miniconda3/envs/cuda11_8torch2_5/bin/python /home/alireza/KyotoServer/CLIP-AD/tipsomaly/train.py --model_name prompt_learning_segonly_518 --dataset mvtec --device 3 --cls_seg_los seg + +Timestamp: 2025-08-24 02:23:50 +image_size: 518 +seed: 111 +metrics: image-pixel-level +devices: [3] +model_name: prompt_learning_segonly_518 +sigma: 4 +dataset: ['mvtec'] +dataset_category: +class_name: ['all'] +k_shot: 0 +type: train +log_dir: vegan-arkansas +image_metrics: ['auroc', 'ap', 'f1-max'] +pixel_metrics: ['auroc', 'aupro', 'f1-max'] +visualize: True +prompt_learn_method: concat +cls_seg_los: seg +data_path: ['/data/alireza/datasets//mvtec/'] +experiment_root: ./workspaces/trained_on_mvtec_prompt_learning_segonly_518/vegan-arkansas +save_path: ./workspaces/trained_on_mvtec_prompt_learning_segonly_518/vegan-arkansas/checkpoints +Command arguments: /home/alireza/miniconda3/envs/cuda11_8torch2_5/bin/python /home/alireza/KyotoServer/CLIP-AD/tipsomaly/train.py --model_name prompt_learning_segonly_518 --dataset mvtec --device 3 --cls_seg_los seg diff --git a/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_1.pth b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_1.pth new file mode 100644 index 0000000000000000000000000000000000000000..811cbeb6372d7c713617179a261000bfd8527159 Binary files /dev/null and b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_1.pth differ diff --git a/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_2.pth b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_2.pth new file mode 100644 index 0000000000000000000000000000000000000000..54dd256efc3eb8eb3e86ac7b22a8a310bf464ee7 Binary files /dev/null and b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints/learnable_params_2.pth differ diff --git a/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/log.txt b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/log.txt new file mode 100644 index 0000000000000000000000000000000000000000..fef56156e5a40c04c3b42c37fa5eac905d1f9acb --- /dev/null +++ b/Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/log.txt @@ -0,0 +1,2 @@ +25-08-24 02:27:58.439 - INFO: Epoch 1 Mean Losses: LS CE: 1.6996, LS FC: 0.0422, LS DC P: 0.8976, LS DC N: 0.0594, Loss: 0.9993 +25-08-24 02:31:43.275 - INFO: Epoch 2 Mean Losses: LS CE: 1.8427, LS FC: 0.0470, LS DC P: 0.7758, LS DC N: 0.0271, Loss: 0.8499 diff --git a/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/args.txt b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/args.txt new file mode 100644 index 0000000000000000000000000000000000000000..0a5158b6a4bb15bc6113fb0a9105a3f4aa2d9f4d --- /dev/null +++ b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/args.txt @@ -0,0 +1,23 @@ + +Timestamp: 2025-08-24 02:44:06 +image_size: 518 +seed: 111 +metrics: image-pixel-level +devices: [3] +model_name: prompt_learning_segonly_518 +sigma: 4 +dataset: ['visa'] +dataset_category: +class_name: ['all'] +k_shot: 0 +type: train +log_dir: vegan-arkansas +image_metrics: ['auroc', 'ap', 'f1-max'] +pixel_metrics: ['auroc', 'aupro', 'f1-max'] +visualize: True +prompt_learn_method: concat +cls_seg_los: seg +data_path: ['/data/alireza/datasets//visa/'] +experiment_root: ./workspaces/trained_on_visa_prompt_learning_segonly_518/vegan-arkansas +save_path: ./workspaces/trained_on_visa_prompt_learning_segonly_518/vegan-arkansas/checkpoints +Command arguments: /home/alireza/miniconda3/envs/cuda11_8torch2_5/bin/python /home/alireza/KyotoServer/CLIP-AD/tipsomaly/train.py --model_name prompt_learning_segonly_518 --dataset visa --device 3 --cls_seg_los seg diff --git a/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_1.pth b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_1.pth new file mode 100644 index 0000000000000000000000000000000000000000..836183d4c888d3cbc229c8435e5312d01ca9febe Binary files /dev/null and b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_1.pth differ diff --git a/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_2.pth b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_2.pth new file mode 100644 index 0000000000000000000000000000000000000000..4435db85fe559321317bb63c332994f04764ad45 Binary files /dev/null and b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints/learnable_params_2.pth differ diff --git a/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/log.txt b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/log.txt new file mode 100644 index 0000000000000000000000000000000000000000..414351dce71c6f8901a815bac9f892e9b0ab759e --- /dev/null +++ b/Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/log.txt @@ -0,0 +1,2 @@ +25-08-24 02:49:49.051 - INFO: Epoch 1 Mean Losses: LS CE: 1.6292, LS FC: 0.0135, LS DC P: 0.9782, LS DC N: 0.0234, Loss: 1.0151 +25-08-24 02:55:10.451 - INFO: Epoch 2 Mean Losses: LS CE: 1.7016, LS FC: 0.0139, LS DC P: 0.9251, LS DC N: 0.0069, Loss: 0.9459 diff --git a/app.py b/app.py new file mode 100644 index 0000000000000000000000000000000000000000..f7382ce3f9ccbe1c9c3d70c379e1af5b6cedf362 --- /dev/null +++ b/app.py @@ -0,0 +1,251 @@ +import os +import sys +from dataclasses import dataclass +from pathlib import Path +from typing import Dict, Optional, Tuple + +import gradio as gr +import numpy as np +import torch +import torch.nn.functional as F +from PIL import Image +from scipy.ndimage import gaussian_filter +from transformers import AutoProcessor, AutoTokenizer, SiglipVisionModel + +# Make Tipsomaly package importable from repository root. +ROOT_DIR = os.path.dirname(os.path.abspath(__file__)) +TIPSOMALY_DIR = os.path.join(ROOT_DIR, "Tipsomaly") +MODEL_DIR = os.path.join(TIPSOMALY_DIR, "model") +if TIPSOMALY_DIR not in sys.path: + sys.path.insert(0, TIPSOMALY_DIR) +if MODEL_DIR not in sys.path: + sys.path.insert(0, MODEL_DIR) + +from Tipsomaly.model.omaly.text_encoder import text_encoder as TipsomalyTextEncoder +from Tipsomaly.model.omaly.vision_encoder import vision_encoder as TipsomalyVisionEncoder +from Tipsomaly.model.siglip2.siglip2_prompt_learnable import SiglipTextModelWithPromptLearning + + +@dataclass +class DemoConfig: + model_id: str = os.getenv("SIGLIP2_MODEL_ID", "google/siglip2-base-patch16-256") + image_size: int = int(os.getenv("IMAGE_SIZE", "256")) + max_len: int = int(os.getenv("MAX_LEN", "64")) + use_local_to_global: bool = os.getenv("USE_LOCAL_TO_GLOBAL", "true").lower() == "true" + sigma: float = float(os.getenv("ANOMALY_SMOOTH_SIGMA", "4")) + object_name: str = os.getenv("OBJECT_NAME", "object") + prompt_learn_method: str = os.getenv("PROMPT_LEARN_METHOD", "none") + n_prompt: int = int(os.getenv("N_PROMPT", "8")) + n_deep_tokens: int = int(os.getenv("N_DEEP_TOKENS", "0")) + d_deep_tokens: int = int(os.getenv("D_DEEP_TOKENS", "0")) + checkpoint_epoch: int = int(os.getenv("LEARNABLE_PROMPT_EPOCH", "2")) + + +CHECKPOINTS: Dict[str, str] = { + "mvtec": "Tipsomaly/workspaces/trained_on_mvtec_default/vegan-arkansas/checkpoints", + "visa": "Tipsomaly/workspaces/trained_on_visa_default/vegan-arkansas/checkpoints", +} + + +def calc_sigm_score_hf( + vis_feat: torch.Tensor, + txt_feat: torch.Tensor, + temperature: torch.Tensor, + bias: torch.Tensor, +) -> torch.Tensor: + if vis_feat.dim() < 3: + vis_feat = vis_feat.unsqueeze(dim=1) + logits = vis_feat @ txt_feat.permute(0, 2, 1) * temperature + bias + probs = torch.sigmoid(logits) + return probs + + +def regrid_upsample_smooth(flat_scores: torch.Tensor, size: int, sigma: float) -> torch.Tensor: + h_w = int(flat_scores.shape[1] ** 0.5) + regrided = flat_scores.reshape(flat_scores.shape[0], h_w, h_w, -1).permute(0, 3, 1, 2) + upsampled = torch.nn.functional.interpolate( + regrided, (size, size), mode="bilinear", align_corners=False + ).permute(0, 2, 3, 1) + rough_maps = (1 - upsampled[..., 0] + upsampled[..., 1]) / 2 + anomaly_map = torch.stack( + [torch.from_numpy(gaussian_filter(one_map, sigma=sigma)) for one_map in rough_maps.detach().cpu()], + dim=0, + ) + return anomaly_map + + +def make_heatmap_rgb(anomaly_map: np.ndarray) -> Image.Image: + normalized = anomaly_map - anomaly_map.min() + denom = normalized.max() + 1e-8 + normalized = normalized / denom + + # Lightweight blue->red colormap without extra dependencies. + red = (normalized * 255).astype(np.uint8) + green = (np.clip(1.0 - np.abs(normalized - 0.5) * 2.0, 0, 1) * 255).astype(np.uint8) + blue = ((1.0 - normalized) * 255).astype(np.uint8) + rgb = np.stack([red, green, blue], axis=-1) + return Image.fromarray(rgb, mode="RGB") + + +class TipsomalyDemo: + def __init__(self, config: DemoConfig) -> None: + self.config = config + self.device = "cuda" if torch.cuda.is_available() else "cpu" + + self.tokenizer = AutoTokenizer.from_pretrained(config.model_id) + self.processor = AutoProcessor.from_pretrained(config.model_id) + self.vision_backbone = SiglipVisionModel.from_pretrained(config.model_id).to(self.device).eval() + self.text_backbone = SiglipTextModelWithPromptLearning.from_pretrained(config.model_id).to(self.device).eval() + + self.temperature, self.bias = self._load_logit_params() + text_embd_dim = self.text_backbone.text_model.head.out_features + self.vision_encoder = TipsomalyVisionEncoder(self.vision_backbone, "siglip2-hf").to(self.device).eval() + self.text_embd_dim = text_embd_dim + + def _load_logit_params(self) -> Tuple[torch.Tensor, torch.Tensor]: + from transformers import AutoModel + + model = AutoModel.from_pretrained(self.config.model_id).to(self.device).eval() + temperature = model.logit_scale.exp() + bias = model.logit_bias + return temperature, bias + + def _build_text_encoder(self, domain: str, prompt_learn_method: str) -> TipsomalyTextEncoder: + encoder = TipsomalyTextEncoder( + tokenizer=self.tokenizer, + bb_text_encoder=self.text_backbone, + bb_type="siglip2-hf", + text_embd_dim=self.text_embd_dim, + MAX_LEN=self.config.max_len, + prompt_learn_method=prompt_learn_method, + prompt_type=domain, + n_prompt=self.config.n_prompt, + n_deep=self.config.n_deep_tokens, + d_deep=self.config.d_deep_tokens, + ).to(self.device).eval() + return encoder + + def _resolve_checkpoint_path(self, token_source: str, custom_checkpoint: str) -> Optional[Path]: + if token_source == "none": + return None + if token_source == "custom": + if not custom_checkpoint.strip(): + raise gr.Error("Custom checkpoint selected, but path is empty.") + path = Path(custom_checkpoint.strip()) + else: + if token_source not in CHECKPOINTS: + raise gr.Error(f"Unknown token source: {token_source}") + base = Path(ROOT_DIR) / CHECKPOINTS[token_source] + path = base / f"learnable_params_{self.config.checkpoint_epoch}.pth" + if not path.exists(): + raise gr.Error(f"Checkpoint not found: {path}") + return path + + def _load_learnable_prompts(self, encoder: TipsomalyTextEncoder, checkpoint_path: Optional[Path]) -> bool: + if checkpoint_path is None: + return False + checkpoint = torch.load(str(checkpoint_path), map_location=self.device, weights_only=False) + prompts = checkpoint["learnable_prompts"] if isinstance(checkpoint, dict) else checkpoint + encoder.learnable_prompts = prompts + return True + + def _preprocess_image(self, image: Image.Image) -> torch.Tensor: + image = image.convert("RGB").resize((self.config.image_size, self.config.image_size)) + batch = self.processor(images=image, return_tensors="pt") + return batch["pixel_values"].to(self.device) + + @torch.inference_mode() + def infer( + self, + image: Image.Image, + domain: str, + token_source: str, + custom_checkpoint: str, + ) -> Tuple[Image.Image, float]: + if image is None: + raise gr.Error("Please upload an image.") + + checkpoint_path = self._resolve_checkpoint_path(token_source, custom_checkpoint) + prompt_learn_method = "concat" if checkpoint_path else self.config.prompt_learn_method + text_encoder = self._build_text_encoder(domain, prompt_learn_method=prompt_learn_method) + has_learned = self._load_learnable_prompts(text_encoder, checkpoint_path) + + fixed_text_features = text_encoder([self.config.object_name], self.device, learned=False) + fixed_text_features = fixed_text_features / fixed_text_features.norm(dim=-1, keepdim=True) + seg_text_features = fixed_text_features + if has_learned: + learned_text_features = text_encoder([self.config.object_name], self.device, learned=True) + learned_text_features = learned_text_features / learned_text_features.norm(dim=-1, keepdim=True) + seg_text_features = learned_text_features + + pixel_values = self._preprocess_image(image) + vision_features = self.vision_encoder(pixel_values) + vision_features = [feat / feat.norm(dim=-1, keepdim=True) for feat in vision_features] + + # Decoupled behavior: classification stays fixed; segmentation can use learned prompts. + img_scr0 = calc_sigm_score_hf(vision_features[0], fixed_text_features, self.temperature, self.bias).squeeze(dim=1).detach() + img_scr1 = calc_sigm_score_hf(vision_features[1], fixed_text_features, self.temperature, self.bias).squeeze(dim=1).detach() + + img_map = calc_sigm_score_hf(vision_features[2], seg_text_features, self.temperature, self.bias).detach() + if self.config.use_local_to_global: + max_local = torch.max(img_map, dim=1)[0] + img_scr0 = img_scr0 + max_local + img_scr1 = img_scr1 + max_local + + pxl_scr = regrid_upsample_smooth(img_map, self.config.image_size, self.config.sigma) + anomaly_map = pxl_scr[0].cpu().numpy() + anomaly_score = float(img_scr1[0][1].item()) + return make_heatmap_rgb(anomaly_map), anomaly_score + + +CONFIG = DemoConfig() +MODEL = TipsomalyDemo(CONFIG) + + +def predict( + image: Image.Image, + domain: str, + token_source: str, + custom_checkpoint: str, +) -> Tuple[Image.Image, float]: + return MODEL.infer(image, domain, token_source, custom_checkpoint) + + +with gr.Blocks(title="Tipsomaly Demo") as demo: + gr.Markdown( + "# Tipsomaly Anomaly Detection Demo\n" + "Upload one image and choose the domain prompt set. " + "The app returns an anomaly heatmap and image-level anomaly score." + ) + with gr.Row(): + image_input = gr.Image(type="pil", label="Input Image") + with gr.Column(): + domain_input = gr.Radio( + choices=["industrial", "medical"], + value="industrial", + label="Domain", + ) + token_source_input = gr.Radio( + choices=["none", "mvtec", "visa", "custom"], + value="none", + label="Learnable Tokens", + info="Use pretrained prompt tokens from workspace checkpoints.", + ) + custom_checkpoint_input = gr.Textbox( + label="Custom Checkpoint Path", + value="", + placeholder="Optional, used only when Learnable Tokens = custom", + ) + run_btn = gr.Button("Run Detection", variant="primary") + with gr.Row(): + anomaly_map_output = gr.Image(type="pil", label="Anomaly Map") + anomaly_score_output = gr.Number(label="Anomaly Score") + + run_btn.click( + fn=predict, + inputs=[image_input, domain_input, token_source_input, custom_checkpoint_input], + outputs=[anomaly_map_output, anomaly_score_output], + ) + +if __name__ == "__main__": + demo.launch() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..b6793e90800f41e71893ff087fc55c1cc1c0ee5b --- /dev/null +++ b/requirements.txt @@ -0,0 +1,8 @@ +gradio>=4.44.0 +numpy>=1.26 +pillow>=10.0 +scipy>=1.11 +torch>=2.1 +torchvision>=0.16 +transformers>=4.45 +sentencepiece>=0.1.99