Giter Site home page Giter Site logo

google-research / big_vision Goto Github PK

View Code? Open in Web Editor NEW
2.0K 35.0 133.0 4.15 MB

Official codebase used to develop Vision Transformer, SigLIP, MLP-Mixer, LiT and more.

License: Apache License 2.0

Python 31.63% Shell 0.04% Jupyter Notebook 67.08% JavaScript 0.02% TypeScript 1.00% SCSS 0.12% HTML 0.11%

big_vision's Introduction

Big Vision

This codebase is designed for training large-scale vision models using Cloud TPU VMs or GPU machines. It is based on Jax/Flax libraries, and uses tf.data and TensorFlow Datasets for scalable and reproducible input pipelines.

The open-sourcing of this codebase has two main purposes:

  1. Publishing the code of research projects developed in this codebase (see a list below).
  2. Providing a strong starting point for running large-scale vision experiments on GPU machines and Google Cloud TPUs, which should scale seamlessly and out-of-the box from a single TPU core to a distributed setup with up to 2048 TPU cores.

big_vision aims to support research projects at Google. We are unlikely to work on feature requests or accept external contributions, unless they were pre-approved (ask in an issue first). For a well-supported transfer-only codebase, see also vision_transformer.

Note that big_vision is quite dynamic codebase and, while we intend to keep the core code fully-functional at all times, we can not guarantee timely updates of the project-specific code that lives in the .../proj/... subfolders. However, we provide a table with last known commits where specific projects were known to work.

The following research projects were originally conducted in the big_vision codebase:

Architecture research

Multimodal research

Training

Misc

  • Are we done with ImageNet?, by Lucas Beyer*, Olivier J. Hénaff*, Alexander Kolesnikov*, Xiaohua Zhai*, and Aäron van den Oord*

Codebase high-level organization and principles in a nutshell

The main entry point is a trainer module, which typically does all the boilerplate related to creating a model and an optimizer, loading the data, checkpointing and training/evaluating the model inside a loop. We provide the canonical trainer train.py in the root folder. Normally, individual projects within big_vision fork and customize this trainer.

All models, evaluators and preprocessing operations live in the corresponding subdirectories and can often be reused between different projects. We encourage compatible APIs within these directories to facilitate reusability, but it is not strictly enforced, as individual projects may need to introduce their custom APIs.

We have a powerful configuration system, with the configs living in the configs/ directory. Custom trainers and modules can directly extend/modify the configuration options.

Project-specific code resides in the .../proj/... namespace. It is not always possible to keep project-specific in sync with the core big_vision libraries, Below we provide the last known commit for each project where the project code is expected to work.

Training jobs are robust to interruptions and will resume seamlessly from the last saved checkpoint (assuming a user provides the correct --workdir path).

Each configuration file contains a comment at the top with a COMMAND snippet to run it, and some hint of expected runtime and results. See below for more details, but generally speaking, running on a GPU machine involves calling python -m COMMAND while running on TPUs, including multi-host, involves

gcloud compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=all
  --command "bash big_vision/run_tpu.sh COMMAND"

See instructions below for more details on how to run big_vision code on a GPU machine or Google Cloud TPU.

By default we write checkpoints and logfiles. The logfiles are a list of JSON objects, and we provide a short and straightforward example colab to read and display the logs and checkpoints.

Current and future contents

The first release contains the core part of pre-training, transferring, and evaluating classification models at scale on Cloud TPU VMs.

We have since added the following key features and projects:

  • Contrastive Image-Text model training and evaluation as in LiT and CLIP.
  • Patient and consistent distillation.
  • Scaling ViT.
  • MLP-Mixer.
  • UViM.

Features and projects we plan to release in the near future, in no particular order:

  • ImageNet-21k in TFDS.
  • Loading misc public models used in our publications (NFNet, MoCov3, DINO).
  • Memory-efficient Polyak-averaging implementation.
  • Advanced JAX compute and memory profiling. We are using internal tools for this, but may eventually add support for the publicly available ones.

We will continue releasing code of our future publications developed within big_vision here.

Non-content

The following exist in the internal variant of this codebase, and there is no plan for their release:

  • Regular regression tests for both quality and speed. They rely heavily on internal infrastructure.
  • Advanced logging, monitoring, and plotting of experiments. This also relies heavily on internal infrastructure. However, we are open to ideas on this and may add some in the future, especially if implemented in a self-contained manner.
  • Not yet published, ongoing research projects.

GPU Setup

We first discuss how to setup and run big_vision on a (local) GPU machine, and then discuss the setup for Cloud TPUs. Note that data preparation step for (local) GPU setup can be largely reused for the Cloud TPU setup. While the instructions skip this for brevity, we highly recommend using a virtual environment when installing python dependencies.

Setting up python packages

The first step is to checkout big_vision and install relevant python dependencies:

git clone https://github.com/google-research/big_vision
cd big_vision/
pip3 install --upgrade pip
pip3 install -r big_vision/requirements.txt

The latest version of jax library can be fetched as

pip3 install --upgrade "jax[cuda]" -f https://storage.googleapis.com/jax-releases/jax_cuda_releases.html

You may need a different jax package, depending on CUDA and cuDNN libraries installed on your machine. Please consult official jax documentation for more information.

Preparing tfds data

For unified and reproducible access to standard datasets we opted to use the tensorflow_datasets (tfds) library. It requires each dataset to be downloaded, preprocessed and then to be stored on a hard drive (or, if you use "Google Cloud", preferably stored in a "GCP bucket".).

Many datasets can be downloaded and preprocessed automatically when used for the first time. Nevertheless, we intentionally disable this feature and recommend doing dataset preparation step separately, ahead of the first run. It will make debugging easier if problems arise and some datasets, like imagenet2012, require manually downloaded data.

Most of the datasets, e.g. cifar100, oxford_iiit_pet or imagenet_v2 can be fully automatically downloaded and prepared by running

cd big_vision/
python3 -m big_vision.tools.download_tfds_datasets cifar100 oxford_iiit_pet imagenet_v2

A full list of datasets is available at this link.

Some datasets, like imagenet2012 or imagenet2012_real, require the data to be downloaded manually and placed into $TFDS_DATA_DIR/downloads/manual/, which defaults to ~/tensorflow_datasets/downloads/manual/. For example, for imagenet2012 and imagenet2012_real one needs to place the official ILSVRC2012_img_train.tar and ILSVRC2012_img_val.tar files in that directory and then run python3 -m big_vision.tools.download_tfds_datasets imagenet2012 imagenet2012_real (which may take ~1 hour).

If you use Google Cloud and, TPUs in particular, you can then upload the preprocessed data (stored in $TFDS_DATA_DIR) to "Google Cloud Bucket" and use the bucket on any of your (TPU) virtual machines to access the data.

Running on a GPU machine

Finally, after installing all python dependencies and preparing tfds data, the user can run the job using config of their choice, e.g. to train ViT-S/16 model on ImageNet data, one should run the following command:

python3 -m big_vision.train --config big_vision/configs/vit_s16_i1k.py --workdir workdirs/`date '+%m-%d_%H%M'`

or to train MLP-Mixer-B/16, run (note the gpu8 config param that reduces the default batch size and epoch count):

python3 -m big_vision.train --config big_vision/configs/mlp_mixer_i1k.py:gpu8 --workdir workdirs/`date '+%m-%d_%H%M'`

Cloud TPU VM setup

Create TPU VMs

To create a single machine with 8 TPU cores, follow the following Cloud TPU JAX document: https://cloud.google.com/tpu/docs/run-calculation-jax

To support large-scale vision research, more cores with multiple hosts are recommended. Below we provide instructions on how to do it.

First, create some useful variables, which we be reused:

export NAME=<a name of the TPU deployment, e.g. my-tpu-machine>
export ZONE=<GCP geographical zone, e.g. europe-west4-a>
export GS_BUCKET_NAME=<Name of the storage bucket, e.g. my_bucket>

The following command line will create TPU VMs with 32 cores, 4 hosts.

gcloud compute tpus tpu-vm create $NAME --zone $ZONE --accelerator-type v3-32 --version tpu-ubuntu2204-base

Install big_vision on TPU VMs

Fetch the big_vision repository, copy it to all TPU VM hosts, and install dependencies.

git clone https://github.com/google-research/big_vision
gcloud compute tpus tpu-vm scp --recurse big_vision/big_vision $NAME: --zone=$ZONE --worker=all
gcloud compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=all --command "bash big_vision/run_tpu.sh"

Download and prepare TFDS datasets

We recommend preparing tfds data locally as described above and then uploading the data to Google Cloud bucket. However, if you prefer, the datasets which do not require manual downloads can be prepared automatically using a TPU machine as described below. Note that TPU machines have only 100 GB of disk space, and multihost TPU slices do not allow for external disks to be attached in a write mode, so the instructions below may not work for preparing large datasets. As yet another alternative, we provide instructions on how to prepare tfds data on CPU-only GCP machine.

Specifically, the seven TFDS datasets used during evaluations will be generated under ~/tensorflow_datasets on TPU machine with this command:

gcloud compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=0 --command "TFDS_DATA_DIR=~/tensorflow_datasets bash big_vision/run_tpu.sh big_vision.tools.download_tfds_datasets cifar10 cifar100 oxford_iiit_pet oxford_flowers102 cars196 dtd uc_merced"

You can then copy the datasets to GS bucket, to make them accessible to all TPU workers.

gcloud compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=0 --command "rm -r ~/tensorflow_datasets/downloads && gsutil cp -r ~/tensorflow_datasets gs://$GS_BUCKET_NAME"

If you want to integrate other public or custom datasets, i.e. imagenet2012, please follow the official guideline.

Pre-trained models

For the full list of pre-trained models check out the load function defined in the same module as the model code. And for example config on how to use these models, see configs/transfer.py.

Run the transfer script on TPU VMs

The following command line fine-tunes a pre-trained vit-i21k-augreg-b/32 model on cifar10 dataset.

gcloud 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.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"

Run the train script on TPU VMs

To train your own big_vision models on a large dataset, e.g. imagenet2012 (prepare the TFDS dataset), run the following command line.

gcloud 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.train --config big_vision/configs/bit_i1k.py  --workdir gs://$GS_BUCKET_NAME/big_vision/workdir/`date '+%m-%d_%H%M'`"

FSDP training.

big_vision supports flexible parameter and model sharding strategies. Currently, we support a popular FSDP sharding via a simple config change, see this config example. For example, to run FSDP finetuning of a pretrained ViT-L model, run the following command (possible adjusting batch size depending on your hardware):

gcloud 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.train --config big_vision/configs/transfer.py:model=vit-i21k-augreg-l/16,dataset=oxford_iiit_pet,crop=resmall_crop,fsdp=True,batch_size=256 --workdir gs://$GS_BUCKET_NAME/big_vision/workdir/`date '+%m-%d_%H%M'` --config.lr=0.03"

Image-text training with SigLIP.

A minimal example that uses public coco captions data:

gcloud 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.siglip --config big_vision/configs/proj/image_text/siglip_lit_coco.py --workdir gs://$GS_BUCKET_NAME/big_vision/`date '+%Y-%m-%d_%H%M'`"

Sometimes useful gcloud commands

  • Destroy the TPU machines: gcloud compute tpus tpu-vm delete $NAME --zone $ZONE
  • Remove all big_vision-related folders on all hosts: gcloud compute tpus tpu-vm ssh $NAME --zone $ZONE --worker=all --command 'rm -rf ~/big_vision ~/bv_venv'

Preparing tfds data on a standalone GCP CPU machine.

First create a new machine and a disk (feel free to adjust exact machine type and disk settings/capacity):

export NAME_CPU_HOST=<A name of a CPU-only machine>
export NAME_DISK=<A name of a disk>
gcloud compute instances create $NAME_CPU_HOST --machine-type c3-standard-22 --zone $ZONE --image-family ubuntu-2204-lts --image-project ubuntu-os-cloud
gcloud compute disks create $NAME_DISK --size 1000GB --zone $ZONE --type pd-balanced

Now attach the disk to the newly create machine:

gcloud compute instances attach-disk $NAME_CPU_HOST --disk $NAME_DISK --zone $ZONE

Next, ssh to the machine gcloud compute ssh $NAME_CPU_HOST --zone=$ZONE and follow instructions to format and mount the disk. Let's assume it was mounted to /mnt/disks/tfds.

Almost there, now clone and set up big_vision:

gcloud compute ssh $NAME_CPU_HOST --zone=$ZONE --command "git clone https://github.com/google-research/big_vision.git && cd big_vision && sh big_vision/run_tpu.sh"

Finally, prepare the dataset (e.g. coco_captions) using the utility script and copy the result to you google cloud bucket:

gcloud compute ssh $NAME_CPU_HOST --zone=$ZONE --command "cd big_vision && TFDS_DATA_DIR=/mnt/disks/tfds/tensorflow_datasets bash big_vision/run_tpu.sh big_vision.tools.download_tfds_datasets coco_captions"
gcloud compute ssh $NAME_CPU_HOST --zone=$ZONE --command "rm -rf /mnt/disks/tfds/tensorflow_datasets/downloads && gsutil cp -r /mnt/disks/tfds/tensorflow_datasets gs://$GS_BUCKET_NAME"

ViT baseline

We provide a well-tuned ViT-S/16 baseline in the config file named vit_s16_i1k.py. It achieves 76.5% accuracy on ImageNet validation split in 90 epochs of training, being a strong and simple starting point for research on the ViT models.

Please see our arXiv note for more details and if this baseline happens to by useful for your research, consider citing

@article{vit_baseline,
  url = {https://arxiv.org/abs/2205.01580},
  author = {Beyer, Lucas and Zhai, Xiaohua and Kolesnikov, Alexander},
  title = {Better plain ViT baselines for ImageNet-1k},
  journal={arXiv preprint arXiv:2205.01580},
  year = {2022},
}

Project specific commits

The last known commit where the specific project code is expected to work. The core code and configs are expected to work at head.

Project Commit
UViM https://github.com/google-research/big_vision/commit/21bd6ebe253f070f584d8b777ad76f4abce51bef
image_text https://github.com/google-research/big_vision/commit/8921d5141504390a8a4f7b2dacb3b3c042237290
distill https://github.com/google-research/big_vision/commit/2f3f493af048dbfd97555ff6060f31a0e686f17f
GSAM WIP
CLIPPO https://github.com/google-research/big_vision/commit/fd2d3bd2efc9d89ea959f16cd2f58ae8a495cd44
CapPa https://github.com/google-research/big_vision/commit/7ace659452dee4b68547575352c022a2eef587a5
GIVT https://github.com/google-research/big_vision/commit/0cb70881dd33b3343b769347dc19793c4994b8cb

Citing the codebase

If you found this codebase useful for your research, please consider using the following BibTEX to cite it:

@misc{big_vision,
  author = {Beyer, Lucas and Zhai, Xiaohua and Kolesnikov, Alexander},
  title = {Big Vision},
  year = {2022},
  publisher = {GitHub},
  journal = {GitHub repository},
  howpublished = {\url{https://github.com/google-research/big_vision}}
}

Disclaimer

This is not an official Google Product.

License

Unless explicitly noted otherwise, everything in the big_vision codebase (including models and colabs) is released under the Apache2 license. See the LICENSE file for the full license text.

big_vision's People

Contributors

ahmadmustafaanis avatar akolesnikoff avatar andresusanopinto avatar andsteing avatar eltociear avatar juntang-zhuang avatar kuz-man avatar lkhphuc avatar lucasb-eyer avatar mitscha avatar mohammedelfatihsalah avatar prazek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

big_vision's Issues

Announcement: big_vision is transitioning from jax.pmap to jax.jit.

In the coming 1-2 weeks big_vision is expected to transition from pmap-based parallelism to jit-parallelism. This will enable more flexible parallelisation strategies, including, but not limited to, ZERO* and fully-sharded data-parallel (aka fsdp) training.

This transition may temporarily break project-specific code (or we will just remove such code). If you want to read/run the old code, please see the table at the end of the README for the project-specific commits to sync to.

Implementation of contrast() seems wrong

I have created #108 for demonstration purpose. In short: the mean here

# 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

is supposed to be the mean pixel value, but as it is it's just summing over the histogram (therefore equal to height * width), divided by 256. For the standard decode_jpeg_and_inception_crop(224), I have verified that mean is always 224 * 224 / 256 = 196. I have also created the following calibration grid to double-check the transform's behavior, with RGB values (192, 64, 64) for the reddish squares and (64, 192, 192) for the bluish squares:

download (8)

As it is, contrast(tf_color_tile, 1.9) returns the following:
download (11)
with RGB values (188, 0, 0) and (0, 188, 188). After the fix, contrast(tf_color_tile, 1.9) returns the following:
download (12)
with RGB values (249, 6, 6) and (6, 249, 249), which is more in line with other implementations. E.g. the approximate torchvision equivalent

from torchvision.transforms.v2 import functional as F
F.adjust_contrast(torch_color_tile, contrast_factor=1.9)

returns RGB values (250, 6, 6) and (6, 250, 250).

Confusion on FlexiViT

Hi, thanks for bringing us such great work! I have two questions regarding the paper.

  1. The PI-resize method does not introduce any learnable parameter, it should be compatible with any ViT model. Therefore, we can use the PI-resize in a zero-shot manner? Then, what's the point of training the FlexiViT? I know since the patch size can be (almost) any number with PI-resize, we can transfer the knowledge of ViT-8 through distillation. But is there any difference between training a FlexiViT and using PI-resize directly in the ViT-8 model (without training)? In Figure 3, the authors mentioned that "Standard ViTs (ViT-16/ViT-30) are not flexible", but the authors "simply resize the patch embedding weights ω and the position embeddings π with bilinear interpolation", not PI.

  2. Will the weight of FlexiCLIP be released someday?

Thanks, I am really looking forward to the answers!

Best,

Zilun

Clarification: SigLIP Image Transform

Thanks for open-sourcing the SigLIP models!

Clarification question: in the demo IPython notebook, the image transform function has the form pp_img = pp_builder.get_preprocess_fn(f'resize({RES})|value_range(-1, 1)').

Looking at the code here, this seems to be resizing an image to RES x RES (warping aspect ratio).

Is this the expected behavior? Were the SigLIP models trained with this transform (aspect ratio warping)?

Mixup Per Example?

Hi! I was wondering why the implementation of mixup uses a single sampled $a$ per batch as opposed to using a different sample $a$ per batch element. Intuitively, it seems that doing this should lead to higher variance in the optimization process.

def get_mixup(rng, p):

Loss Scale for Training Siglip

Hi,

Thanks for your great work. I was trying to apply siglip loss for training contrastive models. However, I find the loss scale is quiet small, usually around 0.003 at the begging. I wonder if any thing goes wrong in my implementation.

    n = logits.size(0)
    labels = 2 * torch.eye(n) - torch.ones(n, n)  # -1 with diagonal 1
    labels = labels.to(logits.device)
    loss = -torch.mean(F.logsigmoid(labels * logits)) / n    
    return loss```

TPU utilization could be improved further?

Training details are in #2

I think the TPU utilization is a bit lower than expected:

Screenshot 2022-05-11 at 7 42 31 PM

Is this expected?

I understand there might be other network access factors that can contribute to this but wanted to know.

Load ViT with CLIPPO Weights

Hi,
I am trying to load the ViT model in big vision with the clippo weights.

!git clone --branch=main https://github.com/google-research/big_vision
!cd big_vision && git checkout fd2d3bd2efc9d89ea959f16cd2f58ae8a495cd44  # this is the clippo commit

now my script:

checkpoint_path = '/home/ahmad/Desktop/projects/big_vision/big_vision/weights/clippo_b16_yfcc100m_i21k_init_75c4.npz'
init_params = utils.load_checkpoint(None, checkpoint_path)['params']
model = vit.Model(
  num_classes=768, variant=None, name="img"
)
classifier = nn.Dense(10) # 10 classes
opt = optax.adam(1e-3)
# Training 
for epoch in range(10):

  for batch in ds['train'].batch(2):
    
    # Get images and labels
    images = batch['image'].numpy()
    # convert the image from (28, 28, 1) gray scale to (28, 28, 3) rgb
    images = np.repeat(images, 3, axis=-1)
    print(images.shape)
    labels = batch['label'].numpy()

    # Forward pass
    zimg, _ = model.apply({'params': init_params}, images)

This is the error I get

---------------------------------------------------------------------------
ScopeParamNotFoundError                   Traceback (most recent call last)
Cell In[12], [line 48](vscode-notebook-cell:?execution_count=12&line=48)
     [45](vscode-notebook-cell:?execution_count=12&line=45) labels = batch['label'].numpy()
     [47](vscode-notebook-cell:?execution_count=12&line=47) # Forward pass
---> [48](vscode-notebook-cell:?execution_count=12&line=48) zimg, _ = model.apply({'params': init_params}, images)  
     [50](vscode-notebook-cell:?execution_count=12&line=50) # Get logits
     [51](vscode-notebook-cell:?execution_count=12&line=51) logits = classifier.apply({'params': classifier.params}, zimg)

    [... skipping hidden 6 frame]

File [~/Desktop/projects/big_vision/big_vision/models/vit.py:186](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:186), in _Model.__call__(self, image, train)
    [183](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:183) out = {}
    [185](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:185) # Patch extraction
--> [186](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:186) x = out["stem"] = nn.Conv(
    [187](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:187)     self.width,
    [188](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:188)     self.patch_size,
    [189](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:189)     strides=self.patch_size,
    [190](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:190)     padding="VALID",
    [191](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:191)     name="embedding",
    [192](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:192) )(image)
    [194](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:194) n, h, w, c = x.shape
    [195](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/Desktop/projects/big_vision/big_vision/models/vit.py:195) x = jnp.reshape(x, [n, h * w, c])

    [... skipping hidden 2 frame]

File [~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:480](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:480), in _Conv.__call__(self, inputs)
    [474](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:474) if self.mask is not None and self.mask.shape != kernel_shape:
    [475](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:475)   raise ValueError(
    [476](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:476)       'Mask needs to have the same shape as weights. '
    [477](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:477)       f'Shapes are: {self.mask.shape}, {kernel_shape}'
    [478](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:478)   )
--> [480](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:480) kernel = self.param(
    [481](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:481)     'kernel', self.kernel_init, kernel_shape, self.param_dtype
    [482](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:482) )
    [484](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:484) if self.mask is not None:
    [485](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/linen/linear.py:485)   kernel *= self.mask

    [... skipping hidden 1 frame]

File [~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:896](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:896), in Scope.param(self, name, init_fn, unbox, *init_args)
    [894](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:894)   if self.is_collection_empty('params'):
    [895](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:895)     raise errors.ScopeCollectionNotFound('params', name, self.path_text)
--> [896](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:896)   raise errors.ScopeParamNotFoundError(name, self.path_text)
    [897](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:897) value = init_fn(self.make_rng('params'), *init_args)
    [898](https://file+.vscode-resource.vscode-cdn.net/home/ahmad/Desktop/projects/big_vision/big_vision/trainers/~/anaconda3/envs/robustness/lib/python3.8/site-packages/flax/core/scope.py:898) self.put_variable('params', name, value)

ScopeParamNotFoundError: Could not find parameter named "kernel" in scope "/embedding". (https://flax.readthedocs.io/en/latest/api_reference/flax.errors.html#flax.errors.ScopeParamNotFoundError)

The npz file has the following keys:

dict_keys(['chrono', 'opt', 'params'])

and in the params parameter keys are:

dict_keys(['t', 'img'])

and in the img we have

init_params['params']['img'].keys()
>>>dict_keys(['pos_embedding', 'MAPHead_0', 'Transformer', 'embedding', 'head'])

My Jax version is:

'0.4.13'

and flax version is:

'0.7.2'

Question about SigLIP

Hello, Google Research team!

Thanks a lot for your work! I came across your paper SigLIP and was curious to reproduce the results myself on another dataset. I checked the README and it says that the SigLIT code is in TODO status. However in the codebase both sigmoid_loss and chunked_sigmoid_loss are both implemented and integrated into the training script as well as config is defined in this .ipynb. So my question is the following: is there is still something missing in SigLIP, or I can already try to run it using the command like this:

big_vision.trainers.proj.image_text.contrastive \
    --config ... \
    --workdir ...

I also have another question about the paper itself, or it's rather an ask for a recommendation. You pre-trained some models with an image encoder frozen, and in 2 days you achieved very competitive scores. However, 20k batch size and 107k total steps which means that the model saw a total of 2.14B image-text examples with text model initialized from scratch and huge ViT-g/14. What do you think about an inverse experiment, how long it is gonna take to train a ViT from scratch having a nice pre-trained text representation? The reason why I'm asking is that in my research I'm dealing with pretty non-conventional images, but regular texts

Looking forward to more research, thank you!

Negative rho values in GSAM training

Hi! I've been trying to reproduce the GSAM results. I noticed that in the code, the learning rate (LR) warmup starts from 0, which is lower than the minimum LR for the post-warmup decay. Because of this, the rho parameter, which is scheduled proportionally with the LR, has negative values early in training.

This does not seem intentional, as rho is never supposed to be negative according to the paper. I'm curious if this makes any difference to the results of the paper if fixed. My guess is that its a very small amount of training (1/3 of the first epoch) and wouldn't change anything.

@lucasb-eyer @juntang-zhuang

requirements issue

hi iam doing the pip install -r requirements.txt, but it keeps downloading new dev versions for tfds_nightly
image

Is there Pytorch version CLIPPO?

Thank you for releasing code for these inspiring works!
Especially, I'm interested in CLIPPO.

Are there any plans to release a Pytorch version?

Contrastive Input Pipeline

Hi, this is an amazing codebase for big-vision tasks!

I wanted to re-implement MOCO, but I was unsure how to modify the input pipeline + data augmentation to allow for applying independent random augmentations to the same image. Is there a simple way to do this?

My current implementation just applies the augmentations after the batch of images leaves the input pipeline (without any augmentation), but this requires me to write new data augmentation functions in Jax, which isn't ideal. Do you have any ideas on how to integrate this into the input pipeline?

Any help would be appreciated and let me know if you need more information.

bfloat16 Training

Thank you for releasing code for these inspiring works!

I tried to use bfloat16 for model parameters, and manually converted images and labels from float32 to bfloat16 before feeding them for training, but noticed that training slowed down by about 3 times. Also, the performance becomes noticeably worse. I'm wondering if it is wrong to use bfloat16 in this way?

Thank you very much for your help.

Memory Efficient Attention integration

Hello, big_vision team!

Thanks for your work on the repository. Looking through the code I noticed that ViT is using classical attention (see line 91 of ViT implementation). It seems like it should be relatively easy to replace current attention implementation with a memory-efficient alternative from flaxformer (line 595 in flaxformer) just passing dot_product_attention_multihead as attention_fn in nn.MultiHeadDotProductAttnetion (line 221 in flax). I think such improvement is worth considering since Flesh Attention authors reported up to x2.4 speedup on long sequences (1k-4k tokens)

What do you think about it? Are there any limitations that make efficient attention integration harder than it seems? I'm not experienced in Jax, so your feedback would be very appreciated

Accuracy of vit-b-16 training

Hi, May I ask the top-1 accuracy of vit-b-16 training on imagenet-1k based on the config file "vit_1ik.py". I find the related paper report the accuracy is about 74.6.

Thank you very much!

Best
Lucas

Errors in notebooks

Hi.

  1. When running notebook in colab uvim_depth_task.ipynb on line
    oracle_params, oracle_state = vit.load(None, "depth_stageI_params.npz") the error is raised
AttributeError: module 'big_vision.utils' has no attribute 'load_checkpoint'

image
2. The same error in clippo_colab.ipynb on line

params = utils.load_checkpoint(None, checkpoint_path)['params']

image

  1. When running lit.ipynb on line
params0 = model.init(jax.random.PRNGKey(42), *init_params)['params'].unfreeze()

the error is raised

AttributeError: 'dict' object has no attribute 'unfreeze'

image

Any extra dataset prep needed?

I have followed the instructions from README. I have set up a TPU v3-8 machine which can be confirmed below:

image

I have hosted the ImageNet-1k (imagenet2012) in a separate bucket and it's structured like the below (following instructions from here):

Screenshot 2022-05-10 at 4 03 31 PM

While launching training, I am using the following command:

gcloud alpha compute tpus tpu-vm ssh $NAME --zone=$ZONE --worker=all --command "TFDS_DATA_DIR=gs://imagenet-1k/tensorflow_datasets bash big_vision/run_tpu.sh big_vision.train --config big_vision/configs/vit_s16_i1k.py  --workdir gs://$GS_BUCKET_NAME/big_vision/workdir/`date '+%m-%d_%H%M'`"

It results into the following:

SSH key found in project metadata; not updating instance.
SSH: Attempting to connect to worker 0...
2022-05-10 10:30:25.858388: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcudart.so.11.0'; dlerror: libcudart.so.11.0: cannot open shared object file: No such file or directory
2022-05-10 10:30:27.319919: W tensorflow/stream_executor/platform/default/dso_loader.cc:64] Could not load dynamic library 'libcuda.so.1'; dlerror: libcuda.so.1: cannot open shared object file: No such file or directory
2022-05-10 10:30:27.319952: W tensorflow/stream_executor/cuda/cuda_driver.cc:269] failed call to cuInit: UNKNOWN ERROR (303)
I0510 10:30:27.335715 140289404775488 xla_bridge.py:263] Unable to initialize backend 'tpu_driver': NOT_FOUND: Unable to find driver in registry given worker:
I0510 10:30:27.336199 140289404775488 xla_bridge.py:263] Unable to initialize backend 'gpu': NOT_FOUND: Could not find registered platform with name: "cuda". Available platform names are: Interpreter TPU Host
I0510 10:30:30.058175 140289404775488 train.py:65] Hello from process 0 holding 8/8 devices and writing to workdir gs://big_vision_exp/big_vision/workdir/05-10_1030.
I0510 10:30:30.568850 140289404775488 train.py:95] NOTE: Global batch size 1024 on 1 hosts results in 1024 local batch size. With 8 dev per host (8 dev total), that's a 128 per-device batch size.
I0510 10:30:30.570343 140289404775488 train.py:95] NOTE: Initializing train dataset...
I0510 10:30:31.039579 140289404775488 dataset_info.py:522] Load pre-computed DatasetInfo (eg: splits, num examples,...) from GCS: imagenet2012/5.1.0
I0510 10:30:31.303886 140289404775488 dataset_info.py:439] Load dataset info from /tmp/tmpggpl8znitfds
I0510 10:30:31.308489 140289404775488 dataset_info.py:492] Field info.description from disk and from code do not match. Keeping the one from code.
I0510 10:30:31.308714 140289404775488 dataset_info.py:492] Field info.release_notes from disk and from code do not match. Keeping the one from code.
I0510 10:30:31.308900 140289404775488 dataset_info.py:492] Field info.supervised_keys from disk and from code do not match. Keeping the one from code.
I0510 10:30:31.308959 140289404775488 dataset_info.py:492] Field info.module_name from disk and from code do not match. Keeping the one from code.
I0510 10:30:31.309248 140289404775488 logging_logger.py:44] Constructing tf.data.Dataset imagenet2012 for split _EvenSplit(split='train[:99%]', index=0, count=1, drop_remainder=False), from gs://imagenet-1k/tensorflow_datasets/imagenet2012/5.1.0
Traceback (most recent call last):
  File "/usr/lib/python3.8/runpy.py", line 194, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "/usr/lib/python3.8/runpy.py", line 87, in _run_code
    exec(code, run_globals)
  File "/home/spsayakpaul/big_vision/train.py", line 372, in <module>
    app.run(main)
  File "/home/spsayakpaul/bv_venv/lib/python3.8/site-packages/absl/app.py", line 312, in run
    _run_main(main, args)
  File "/home/spsayakpaul/bv_venv/lib/python3.8/site-packages/absl/app.py", line 258, in _run_main
    sys.exit(main(argv))
  File "/home/spsayakpaul/big_vision/train.py", line 122, in main
    train_ds = input_pipeline.make_for_train(
  File "/home/spsayakpaul/big_vision/input_pipeline.py", line 69, in make_for_train
    data, _ = get_dataset_tfds(dataset=dataset, split=split,
  File "/home/spsayakpaul/big_vision/input_pipeline.py", line 53, in get_dataset_tfds
    return builder.as_dataset(
  File "/home/spsayakpaul/bv_venv/lib/python3.8/site-packages/tensorflow_datasets/core/logging/__init__.py", line 81, in decorator
    return function(*args, **kwargs)
  File "/home/spsayakpaul/bv_venv/lib/python3.8/site-packages/tensorflow_datasets/core/dataset_builder.py", line 565, in as_dataset
    raise AssertionError(
AssertionError: Dataset imagenet2012: could not find data in gs://imagenet-1k/tensorflow_datasets. Please make sure to call dataset_builder.download_and_prepare(), or pass download=True to tfds.load() before trying to access the tf.data.Dataset object.

Is there anything I'm missing out here?

[BUG] in big_vision.models.proj.flexi.vit

Hello, big_vision team!
Thanks for your work on the repository. I found two small typo in the flexivit code:

line 194
restored_params = utils.load_params(None, init_file)
==>
restored_params = utils.load_params(init_file)

line 205
restored_params["embedding"]["kernel"] = resample_patchemb(old=old_patchemb, new_hw=model_cfg.patch_size)
==>
restored_params["embedding"]["kernel"] = resample_patchemb(old=old_patchemb, new_hw=model_cfg.get("patch_size"))

Behavior of `solarize()` depends on integer overflow

I am not 100% sure about the intention but I do want to raise the alarm. The solarize() transform here

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)

inverts the pixel when its value is greater or equal to the threshold, so one would think that higher augmentation magnitude needs lower threshold. However, the threshold increases linearly with magnitude:

'Solarize': lambda level: (int((level/_MAX_LEVEL) * 256),),

Counterintuitively, it still works as expected with magnitude=_MAX_LEVEL because of integer overflow. Given

t = tf.constant([[[0,0,0]]], dtype=tf.uint8)

t < i evaluates to tf.Tensor([[[False False False]]], shape=(1, 1, 3), dtype=bool) iff not (i % 256). In other words, magnitude=_MAX_LEVEL means int((level/_MAX_LEVEL) * 256) = 256, which is equivalent to 0 in tf.uint8. Given the following tf_gradient that goes from (0, 0, 0) to (255, 255, 255) in alternating directions

download (13)

Both solarize(tf_gradient, 256) and solarize(tf_gradient, 0) indeed fully invert the image:

download (14)

But if magnitude is 9, int((9/10) * 256) = 230, and solarize(tf_gradient, 230) "abruptly" only inverts a small portion of the image:

download (15)

question about FlexiViT

FlexiViT is a very imaginative work.
I was also bothered by the flexible patch size.
I want to know how to implement PI-resize in Section 3.4 in the code.
And how to optimize the PI-resize in training.

  • Does PI-resize need to set learnable parameters?
  • Does the loss function need to be used for constraint and optimization?

Question About Listed ViT Models in the configs/proj/flexivit/README.md

Hello,

First of all, thank you very much for releasing many helpful materials and code samples of the interesting work FlexiVit.

When I went through the paper, the models referred to as ViT-B-16 and ViT-B-30 seems to be the baseline ViT models trained with the fixed patch sizes (16 and 30 respectively). Moreover, accordingly, their positional embedding sizes should be 15 and 8 if I am not wrong (img_size divided by the patch_size).
However, when I downloaded and loaded the .npz files of these models from the README file, I encountered that the patch size and the positional embedding size were 32 and 7 which matches the setup of the flexiVit-B model mentioned in the paper but not that of the baseline ViT models (given my understanding).

Thus, I was curious whether the links map to the wrong models or I misunderstood the setup mentioned in the paper regarding these models.

Could you please help me with this matter.
Thanks!

questions about t-SNE visualization in FlexiViT

Hi, FlexiViT is a very inspirational idea.

However, I'm kind of stuck at the t-SNE visualization in Fig. 6 of the paper.

Does t-SNE employ the arccosine-transformed CKA as the precomputed metrics ?

If so, how do we calculate the CKA similarity? Is it between a FlexiViT at different patch sizes and a standard ViT at a fixed patch size?

Error with putting arrays on CPU in cloud TPUs

Hi I've been setting up big vision on a v4-32 TPU pod and I run into this error whenever I call u.put_cpu

jaxlib.xla_extension.XlaRuntimeError: INVALID_ARGUMENT: Cannot copy array to non-addressable device TFRT_CPU_0

I'm guessing the CPUs on the TPU pod aren't configured properly? Is there a way around this or a way to fix this issue?

Totally new to TPUs and let me know if you need more information.

PlaiGemma finetuned model

Thanks for the great work! I've checked out some finetuned model released on HF, like docVQA, just wanna know if you have any plans to provide finetune example for different downstream tasks?

About RL fine-tuning code release

Hi!

First of all, thanks for sharing those amazing and helpful codebases. I wonder if there is a plan to release the full code of the ICML'23 paper "Tuning computer vision models with task rewards", including the instructions to reproduce the results in the paper.

Thank you :)

How to save fine tuned PaliGemma model?

Hi 👋🏻

Thanks a lot for PaliGemma fine-tune example notebook. It worked without any issue. However, it lacks the last step - saving a fine-tuned model. (Or I'm just missing something.) I'd appreciate any help. Thank you. 🙏🏻

FlexiVit is also flexible with image resolution?

Hello, big_vision team!

Thanks for your work on the repository.
I trained FlexiVit-B on a fine-grained dataset CUB-200-2011 using pretrained weights from in21k, on a fixed resolution, say 480r, but I found it also performs good when testing on a smaller resolution, 240r. And it almost aligns the testing performance if it was trained on 240r. So my question is, is FlexiVit flexible to image resolution as well?
Thank you and looking forward to your reply.

Question regarding value range (-1,1)

Hi, thank you so much for releasing code for these inspiring works. I notice that the config file uses value_range(-1, 1) instead of vgg_value_range. Is (-1,1) necessary for reproducing results on a normal imagenet dataset?

Thank you very much for your time and help.

augmentation and regularization used in MLP-Mixer

Hello! Thank you for your work!
In the paper of MLP-Mixer, when training mixer-B/16 on imagenet1k from scratch, it is said extra regularization is applied to gain the the accuracy of 76% , I want to know what detailed augmentation and regularization strategy is used for the experiment? Is there any config file can be found?
Thank you for your help! : )

AttributeError pp_img in lit notebook

Hi.
An AttributeError is raised when running big_vision/blob/main/big_vision/configs/proj/image_text/lit.ipynb notebook in colab:
image

P.S: raised here config.pp_img.
P.S.S: here also will be AttributeError: config.pp_txt

Running out of RAM on cloud TPU when reading data from Cloud Storage

Hi! I am trying to run the vit_s16_i1k.py script on a TPU-v3-8 machine. I put the data in a google-storage bucket and I am running the following command

TFDS_DATA_DIR=gs://bucket-name/ python3 -m big_vision.train --config big_vision/configs/vit_s16_i1k.py --workdir workdirs/i1k_training_`date '+%m-%d_%H%M'`

The training runs for a few iterations, and then fails with the killed message. When I look at htop outputs, the memory used by the process grows all the way up to 335G available before the process crashes.

I have been able to work around this issue by creating a data disk, mounting it on the TPU VM and putting the data there. In that case the same process only uses 205G of RAM and runs normally.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.