From 97a975f64f8b0d14204b79ebaaec51342c6d1ab9 Mon Sep 17 00:00:00 2001 From: root <“junnan.li@salesforce.com”> Date: Thu, 27 Jan 2022 12:37:45 +0000 Subject: [PATCH] init --- README.md | 1 + configs/bert_config.json | 21 + configs/caption_coco.yaml | 33 + configs/med_config.json | 21 + configs/nlvr.yaml | 21 + configs/nocaps.yaml | 15 + configs/pretrain.yaml | 27 + configs/retrieval_coco.yaml | 34 + configs/retrieval_flickr.yaml | 34 + configs/vqa.yaml | 25 + demo.ipynb | 173 ++++ eval_nocaps.py | 118 +++ models/__init__.py | 0 models/__pycache__/__init__.cpython-36.pyc | Bin 0 -> 141 bytes models/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 149 bytes models/__pycache__/blip.cpython-38.pyc | Bin 0 -> 6882 bytes models/__pycache__/blip_nlvr.cpython-38.pyc | Bin 0 -> 3546 bytes .../__pycache__/blip_retrieval.cpython-38.pyc | Bin 0 -> 8668 bytes models/__pycache__/blip_vqa.cpython-38.pyc | Bin 0 -> 4869 bytes models/__pycache__/booster.cpython-38.pyc | Bin 0 -> 6660 bytes .../__pycache__/booster_nlvr.cpython-38.pyc | Bin 0 -> 3300 bytes .../booster_retrieval.cpython-38.pyc | Bin 0 -> 8698 bytes .../booster_retrieval_new.cpython-38.pyc | Bin 0 -> 8341 bytes models/__pycache__/booster_vqa.cpython-38.pyc | Bin 0 -> 4914 bytes models/__pycache__/med.cpython-36.pyc | Bin 0 -> 27989 bytes models/__pycache__/med.cpython-38.pyc | Bin 0 -> 28146 bytes .../__pycache__/nlvr_encoder.cpython-38.pyc | Bin 0 -> 23237 bytes models/__pycache__/univlm.cpython-36.pyc | Bin 0 -> 5848 bytes models/__pycache__/univlm.cpython-38.pyc | Bin 0 -> 6091 bytes .../univlm_pretrain.cpython-38.pyc | Bin 0 -> 9588 bytes .../univlm_retrieval.cpython-38.pyc | Bin 0 -> 6972 bytes models/__pycache__/univlm_vqa.cpython-38.pyc | Bin 0 -> 4913 bytes models/__pycache__/vit.cpython-36.pyc | Bin 0 -> 8286 bytes models/__pycache__/vit.cpython-38.pyc | Bin 0 -> 12335 bytes models/__pycache__/vl_model.cpython-38.pyc | Bin 0 -> 2483 bytes models/__pycache__/xbert.cpython-38.pyc | Bin 0 -> 27884 bytes models/blip.py | 236 +++++ models/blip_nlvr.py | 103 ++ models/blip_pretrain.py | 339 +++++++ models/blip_retrieval.py | 322 ++++++ models/blip_vqa.py | 186 ++++ models/med.py | 955 ++++++++++++++++++ models/nlvr_encoder.py | 843 ++++++++++++++++ models/vit.py | 305 ++++++ pretrain.py | 173 ++++ requirements.txt | 4 + train_caption.py | 206 ++++ train_nlvr.py | 213 ++++ train_retrieval.py | 345 +++++++ train_vqa.py | 202 ++++ .../__pycache__/randaugment.cpython-36.pyc | Bin 0 -> 10887 bytes .../__pycache__/randaugment.cpython-38.pyc | Bin 0 -> 10398 bytes transform/randaugment.py | 340 +++++++ utils.py | 278 +++++ 54 files changed, 5573 insertions(+) create mode 100644 README.md create mode 100644 configs/bert_config.json create mode 100644 configs/caption_coco.yaml create mode 100644 configs/med_config.json create mode 100644 configs/nlvr.yaml create mode 100644 configs/nocaps.yaml create mode 100644 configs/pretrain.yaml create mode 100644 configs/retrieval_coco.yaml create mode 100644 configs/retrieval_flickr.yaml create mode 100644 configs/vqa.yaml create mode 100644 demo.ipynb create mode 100644 eval_nocaps.py create mode 100644 models/__init__.py create mode 100644 models/__pycache__/__init__.cpython-36.pyc create mode 100644 models/__pycache__/__init__.cpython-38.pyc create mode 100644 models/__pycache__/blip.cpython-38.pyc create mode 100644 models/__pycache__/blip_nlvr.cpython-38.pyc create mode 100644 models/__pycache__/blip_retrieval.cpython-38.pyc create mode 100644 models/__pycache__/blip_vqa.cpython-38.pyc create mode 100644 models/__pycache__/booster.cpython-38.pyc create mode 100644 models/__pycache__/booster_nlvr.cpython-38.pyc create mode 100644 models/__pycache__/booster_retrieval.cpython-38.pyc create mode 100644 models/__pycache__/booster_retrieval_new.cpython-38.pyc create mode 100644 models/__pycache__/booster_vqa.cpython-38.pyc create mode 100644 models/__pycache__/med.cpython-36.pyc create mode 100644 models/__pycache__/med.cpython-38.pyc create mode 100644 models/__pycache__/nlvr_encoder.cpython-38.pyc create mode 100644 models/__pycache__/univlm.cpython-36.pyc create mode 100644 models/__pycache__/univlm.cpython-38.pyc create mode 100644 models/__pycache__/univlm_pretrain.cpython-38.pyc create mode 100644 models/__pycache__/univlm_retrieval.cpython-38.pyc create mode 100644 models/__pycache__/univlm_vqa.cpython-38.pyc create mode 100644 models/__pycache__/vit.cpython-36.pyc create mode 100644 models/__pycache__/vit.cpython-38.pyc create mode 100644 models/__pycache__/vl_model.cpython-38.pyc create mode 100644 models/__pycache__/xbert.cpython-38.pyc create mode 100644 models/blip.py create mode 100644 models/blip_nlvr.py create mode 100644 models/blip_pretrain.py create mode 100644 models/blip_retrieval.py create mode 100644 models/blip_vqa.py create mode 100644 models/med.py create mode 100644 models/nlvr_encoder.py create mode 100644 models/vit.py create mode 100644 pretrain.py create mode 100644 requirements.txt create mode 100644 train_caption.py create mode 100644 train_nlvr.py create mode 100644 train_retrieval.py create mode 100644 train_vqa.py create mode 100644 transform/__pycache__/randaugment.cpython-36.pyc create mode 100644 transform/__pycache__/randaugment.cpython-38.pyc create mode 100644 transform/randaugment.py create mode 100644 utils.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..4ac7aec --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# BLIP diff --git a/configs/bert_config.json b/configs/bert_config.json new file mode 100644 index 0000000..9b0a67d --- /dev/null +++ b/configs/bert_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertForMaskedLM" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30522, + "encoder_width": 768, + "add_cross_attention": true +} \ No newline at end of file diff --git a/configs/caption_coco.yaml b/configs/caption_coco.yaml new file mode 100644 index 0000000..b398665 --- /dev/null +++ b/configs/caption_coco.yaml @@ -0,0 +1,33 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +coco_gt_root: 'annotation/coco_gt' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 +batch_size: 32 +init_lr: 1e-5 + +# vit: 'large' +# vit_grad_ckpt: True +# vit_ckpt_layer: 5 +# batch_size: 16 +# init_lr: 2e-6 + +image_size: 384 + +# generation configs +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 5 + diff --git a/configs/med_config.json b/configs/med_config.json new file mode 100644 index 0000000..0ffad0a --- /dev/null +++ b/configs/med_config.json @@ -0,0 +1,21 @@ +{ + "architectures": [ + "BertModel" + ], + "attention_probs_dropout_prob": 0.1, + "hidden_act": "gelu", + "hidden_dropout_prob": 0.1, + "hidden_size": 768, + "initializer_range": 0.02, + "intermediate_size": 3072, + "layer_norm_eps": 1e-12, + "max_position_embeddings": 512, + "model_type": "bert", + "num_attention_heads": 12, + "num_hidden_layers": 12, + "pad_token_id": 0, + "type_vocab_size": 2, + "vocab_size": 30524, + "encoder_width": 768, + "add_cross_attention": true +} diff --git a/configs/nlvr.yaml b/configs/nlvr.yaml new file mode 100644 index 0000000..2d1122a --- /dev/null +++ b/configs/nlvr.yaml @@ -0,0 +1,21 @@ +image_root: '/export/share/datasets/vision/NLVR2/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_nlvr.pth' + +#size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 64 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +max_epoch: 15 + +image_size: 384 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-5 +min_lr: 0 + diff --git a/configs/nocaps.yaml b/configs/nocaps.yaml new file mode 100644 index 0000000..27bb115 --- /dev/null +++ b/configs/nocaps.yaml @@ -0,0 +1,15 @@ +image_root: '/export/share/datasets/vision/nocaps/' +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth' + +vit: 'base' +batch_size: 32 + +image_size: 384 + +max_length: 20 +min_length: 5 +num_beams: 3 +prompt: 'a picture of ' \ No newline at end of file diff --git a/configs/pretrain.yaml b/configs/pretrain.yaml new file mode 100644 index 0000000..02355ee --- /dev/null +++ b/configs/pretrain.yaml @@ -0,0 +1,27 @@ +train_file: ['/export/share/junnan-li/VL_pretrain/annotation/coco_karpathy_train.json', + '/export/share/junnan-li/VL_pretrain/annotation/vg_caption.json', + ] +laion_path: '' + +# size of vit model; base or large +vit: 'base' +vit_grad_ckpt: False +vit_ckpt_layer: 0 + +image_size: 224 +batch_size: 75 + +queue_size: 57600 +alpha: 0.4 + +# optimizer +weight_decay: 0.05 +init_lr: 3e-4 +min_lr: 1e-6 +warmup_lr: 1e-6 +lr_decay_rate: 0.9 +max_epoch: 20 +warmup_steps: 3000 + + + diff --git a/configs/retrieval_coco.yaml b/configs/retrieval_coco.yaml new file mode 100644 index 0000000..a8569e9 --- /dev/null +++ b/configs/retrieval_coco.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/coco/images/' +ann_root: 'annotation' +dataset: 'coco' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_coco.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 12 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 256 +negative_all_rank: True + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/configs/retrieval_flickr.yaml b/configs/retrieval_flickr.yaml new file mode 100644 index 0000000..d75ea4e --- /dev/null +++ b/configs/retrieval_flickr.yaml @@ -0,0 +1,34 @@ +image_root: '/export/share/datasets/vision/flickr30k/' +ann_root: 'annotation' +dataset: 'flickr' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_retrieval_flickr.pth' + +# size of vit model; base or large + +vit: 'base' +batch_size_train: 32 +batch_size_test: 64 +vit_grad_ckpt: True +vit_ckpt_layer: 4 +init_lr: 1e-5 + +# vit: 'large' +# batch_size_train: 16 +# batch_size_test: 32 +# vit_grad_ckpt: True +# vit_ckpt_layer: 10 +# init_lr: 5e-6 + +image_size: 384 +queue_size: 57600 +alpha: 0.4 +k_test: 128 +negative_all_rank: False + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 6 + diff --git a/configs/vqa.yaml b/configs/vqa.yaml new file mode 100644 index 0000000..118f396 --- /dev/null +++ b/configs/vqa.yaml @@ -0,0 +1,25 @@ +vqa_root: '/export/share/datasets/vision/VQA/Images/mscoco/' #followed by train2014/ +vg_root: '/export/share/datasets/vision/visual-genome/' #followed by image/ +train_files: ['vqa_train','vqa_val','vg_qa'] +ann_root: 'annotation' + +# set pretrained as a file path or an url +pretrained: 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth' + +# size of vit model; base or large +vit: 'base' +batch_size_train: 16 +batch_size_test: 32 +vit_grad_ckpt: False +vit_ckpt_layer: 0 +init_lr: 2e-5 + +image_size: 480 + +k_test: 128 +inference: 'rank' + +# optimizer +weight_decay: 0.05 +min_lr: 0 +max_epoch: 10 \ No newline at end of file diff --git a/demo.ipynb b/demo.ipynb new file mode 100644 index 0000000..8dd5efd --- /dev/null +++ b/demo.ipynb @@ -0,0 +1,173 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "id": "cbcb066b", + "metadata": {}, + "outputs": [], + "source": [ + "import sys\n", + "if 'google.colab' in sys.modules:\n", + " print('Running in Colab.')\n", + " !pip3 install transformers==4.15.0 timm==0.4.12 fairscale==0.4.4\n", + " !git clone https://github.com/\n", + " sys.path.append('./BLIP')" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a811a65f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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sU7lt5fln3/7HCz386n2ziKbG+s4Jb6rIlAS3NURwRDWzOc5tXDIPM1FciLiCcMLF28L6OfN/3qv//0/e//NyrwPVhvqP77c91+uj8deW6NSalcDRCIILzmDCXgqa9IwNyVO0caBxpzmQgjCMk1SUm616ei993ob14YvX673Pr77cP7+qeQyk9ApnQPor396/flK+L/9P/yXCWMq5d7VzC9T/nBe1rlsDr9srbdR5hWIh6kkkZRM7askm6X3fhAcc04B0LcN3HOelstpXpZlykL4eOyf3976Vol49JZLef7wnhlVbbtvL59f8umUp6LmKSdiqvedhUtK63nN6xkBs1CEz0TnxOphph8KMkIMVY8aQMvZIFob133svTngelqKJA0HQACotbEQEl2vt9FHBDiQlFKY3r9/nudiQK11BChZRMTMWx9gLiXttSFizvn4bKgZmBJGLtNQc4s2GgEmlvU09W5ER2eTeutlmlLiMLu/bZJpqL78/PPby5dwBSQb2vbHX/yjf3C5XLa3FxF5PLba6nK+jN63t+vzd99Pl6f79Z4Q1tPyuL6+fXkNob/9D3/Z635+focEJDKtT/PlYqp//a//n+d379599507LKen999/t8wzAK6XdyycCT0iALJIOATB4/EoKYmk4dZqXZd5qIWbjz7UT+fTNE17rUfWfNsrEyah3jqxpCStNRHiVPatQQwmIkkl595q3TdtfVkXydP7D++OnhAyjaGPxwMgeh+SSwRsj0cRWdcllwThbkbMrba5ZJJU21DVdZ5LSULk4VvvECBEZt7NWY4lCR7qY48Yveecp5wjwtyJKNzXZQEAj0D3gDjOaAjhgXutJNJqA4RlKnbcCiDAw9wlCSKVLAhgHm4GhxWevqrq4Ot/yHWYuZkFgU1Mz0sh190AWKzVIyK/18oR82n1PpZMp7kE8qPblNPe+tv1GmAdEkpy8zBrrZ3P62Vdr7crhENEd+CUAtDGADNDevvyMp+XWbhtG4nU3r2Pp2X+7t0JiLvHLy+3x17nks19AIYbu5GbEhOifo0ogmtPLGWZKacj5xEQTLTdrt5rD1T1MToxPWf+Z9+sv39te28zGnK61zanfC58njhMHyMqpYfhbk7EKQm4qWrbW29tPa2HhVqYI0LHsMPQ4+E61tNKiHXbzRQiGOHjzBNFHzpLoI/f//J6735KdFqX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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from PIL import Image\n", + "import requests\n", + "import torch\n", + "from torchvision import transforms\n", + "from torchvision.transforms.functional import InterpolationMode\n", + "\n", + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n", + "\n", + "img_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/demo.jpg' \n", + "raw_image = Image.open(requests.get(img_url, stream=True).raw).convert('RGB') \n", + "\n", + "w,h = raw_image.size\n", + "display(raw_image.resize((w//5,h//5)))" + ] + }, + { + "cell_type": "markdown", + "id": "f72f4406", + "metadata": {}, + "source": [ + "# Image Captioning" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "6835daef", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth\n", + "caption: a woman sitting on the beach with a dog\n" + ] + } + ], + "source": [ + "from models.blip import blip_decoder\n", + "\n", + "image_size = 384\n", + "transform = transforms.Compose([\n", + " transforms.Resize((image_size,image_size),interpolation=InterpolationMode.BICUBIC),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))\n", + " ]) \n", + "image = transform(raw_image).unsqueeze(0).to(device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_base_caption.pth'\n", + " \n", + "model = blip_decoder(pretrained=model_url, image_size=384, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "with torch.no_grad():\n", + " caption = model.generate(image, sample=False, num_beams=3, max_length=20, min_length=5)\n", + " print('caption: '+caption[0])" + ] + }, + { + "cell_type": "markdown", + "id": "fac320a2", + "metadata": {}, + "source": [ + "# VQA" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "5e6f3fb1", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "load checkpoint from https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth\n", + "answer: on beach\n" + ] + } + ], + "source": [ + "from models.blip_vqa import blip_vqa\n", + "\n", + "image_size = 480\n", + "transform = transforms.Compose([\n", + " transforms.Resize((image_size,image_size),interpolation=InterpolationMode.BICUBIC),\n", + " transforms.ToTensor(),\n", + " transforms.Normalize((0.48145466, 0.4578275, 0.40821073), (0.26862954, 0.26130258, 0.27577711))\n", + " ]) \n", + "image = transform(raw_image).unsqueeze(0).to(device) \n", + "\n", + "model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model*_vqa.pth'\n", + " \n", + "model = blip_vqa(pretrained=model_url, image_size=480, vit='base')\n", + "model.eval()\n", + "model = model.to(device)\n", + "\n", + "question = 'where is the woman sitting?'\n", + "\n", + "with torch.no_grad():\n", + " answer = model(image, question, train=False, inference='generate') \n", + " print('answer: '+answer[0])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "be95d7b4", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/eval_nocaps.py b/eval_nocaps.py new file mode 100644 index 0000000..3cbb09a --- /dev/null +++ b/eval_nocaps.py @@ -0,0 +1,118 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length'], repetition_penalty=1.1) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + val_dataset, test_dataset = create_dataset('nocaps', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([val_dataset,test_dataset], [False,False], num_tasks, global_rank) + else: + samplers = [None,None] + + val_loader, test_loader = create_loader([val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*2,num_workers=[4,4], + is_trains=[False, False], collate_fns=[None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val', remove_duplicate='image_id') + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test', remove_duplicate='image_id') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nocaps.yaml') + parser.add_argument('--output_dir', default='output/NoCaps') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/models/__pycache__/__init__.cpython-36.pyc b/models/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..790416ff09d815969e6ec166b73dc08a65935b5b GIT binary patch literal 141 zcmXr!<>h)9vm%iJ2p)q77+?f49Dul(1xTbY1T$zd`mJOr0tq9CUsn356$SZ4CHln~ ziAAaUS*3Y-iFvv?nfhg!#hLke@i~ck>7|M3srtG3DXBTd`tk9Zd6^~g@p=W7w>WHo Pf~7gBb|5p0ftUdRE0rTv literal 0 HcmV?d00001 diff --git a/models/__pycache__/__init__.cpython-38.pyc b/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b32b2a4686331b7b7f3f36fa012bfc09b23839fb GIT binary patch literal 149 zcmWIL<>g`kf`>6H5<&E15P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!HOeriQQeo={j zaYkZMs(w~!US49JZce6tS!Qu&eqMY|VqSV_VtT56ZhlH?PO*M`d}dx|NqoFsLFFwD R8=zomPO2Tq%+El~002=#BliFR literal 0 HcmV?d00001 diff --git a/models/__pycache__/blip.cpython-38.pyc b/models/__pycache__/blip.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dec0a170c0c32d9ad041e8f1aff66abf59e3315 GIT binary patch literal 6882 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/blip_nlvr.cpython-38.pyc b/models/__pycache__/blip_nlvr.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b98c7ae634ae283d5fa5dbca59bf032727c8ff51 GIT binary patch literal 3546 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/blip_retrieval.cpython-38.pyc b/models/__pycache__/blip_retrieval.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba4960a1f358fb4f5a8dbb4c8f1baf9c0b7c07ee GIT binary patch literal 8668 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 aB*L{kMzRmm-(| literal 0 HcmV?d00001 diff --git a/models/__pycache__/booster_retrieval_new.cpython-38.pyc b/models/__pycache__/booster_retrieval_new.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5508c38835ff3b0e2603b1fffb68e0047ab66ab GIT binary patch literal 8341 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 A5dZ)H literal 0 HcmV?d00001 diff --git a/models/__pycache__/med.cpython-36.pyc b/models/__pycache__/med.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..485c206910e6ff7bfda12ff11d20b6aff037f016 GIT binary patch literal 27989 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/med.cpython-38.pyc b/models/__pycache__/med.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd9baed96174dddace7679ee81b9b2cc5c87a27b GIT binary patch literal 28146 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/nlvr_encoder.cpython-38.pyc b/models/__pycache__/nlvr_encoder.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..206b21b2c5a21cecc7fa71388ed7156cbb408563 GIT binary patch literal 23237 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 VQe~=Au3V@%IL}n7mHhPl{{lFUGjKy! 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/univlm_pretrain.cpython-38.pyc b/models/__pycache__/univlm_pretrain.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a1fb0dd0541f8e55dcfe4630365bbb4a3894b69 GIT binary patch literal 9588 zcmaJ{>u(&_b)VPH?#@2>5J`~|HKt@qD@#jBu^m@XEI;K0u~a)zoOEh;JlvUG&T?P6 zb5|1C*|socL=DQu{SpL?Q!Jal)MuIxMbQsI+kc=yzZe)4eFi9m6len!{h+v3f9KBZ zE-9&AV(#4MnS0MY_nhB3mtQFq3<$|V*2=WARyL_K+F1qd?VObtHK|si zU9^hrl2vM#t+Fh=Cb_zszAd?FFWa2jR;_6_<7RJ5){K`K&hARC;pT2j+p={4wDYaG z_PjMOOVn393*LfT_)v3;Zt1pW9dy+%OO^6HtU;=%k@1qp_{-f+-ETx$%3tZaUMnh5 z_Vp{D_Z(MLROF~YRoA*3UdP|^*j3ahoeoi`G0)+ieZ%Kb(eL=&=A%wgsnvB{ySDDt zHhNvZ!)x@_y+1uE1aBeZNtWzNmf}u*U8>7&+Rfa~TB@6M4Xjo3QbQeU&AEB8)^t)X zxJ6OUB;}G@7Uk^MB~7vntaUmneWl~S_4*b24d!v?_?`QU)zz9L4AeuDqeJt6XcQn!2mFs;jxF9d(ciGp>G68e~JMsW#JLmX^Jv42;fM zjLBlmFVXgrzzlK_4+9%)v~vT9N|>WDVcsnql(rRoGruk2YXx2$mO^<~9u&fKSg^}{ zsyXcz!vZLmc2xA3!MK^>EI)vEVH-U`3pLHT=KP?@7lsEhPTQrKFN#tzDJ{h%w=CKY z?OWsFL3-cFWj>XBk?0)RN5>TNmYRnfXlpLwSr$2(*AbChLe6X`4Gm+T2&Lf)@f7c) z?iAOC$3iVk?JB!iv#6VXOx+AP{iHjK^tgLqNA%?j%@fU&pj-hL=XPjbm2usvaowpf zg_fs69qDPz`;0pe`qhTCE6?IV?dq7rC2*<)4xbeqF1ZVW>N$Ml+wh`7?-HB?cwwYK{ZbXoK-HEJ%_KG^)55{x@WfiTfEOa zvs*X4P7Mc_opC+!HrsTJPXuN>y3ZW$cROa?Z+ZJZ;J2NIX9qaZ=1H7Se8BJY`rH)t zCe`ojjx#=*i1>i$L7%_j2SgvIs}o>$ZI6w|u>y|A*5!51d%=a(Rku~GuQ`6Nzt-|= zG@qba>$X>2&*vhs>Ezj1W^Mi4v-bJ(PoG(PrheY5KkZg~d_7WYz5X?59ktI|QNHc; z>{hoX76(N{dd6ONg7qjRrgs@iN(bNk8jn&fhc!Hg6D^^3E>=wafOp$=FHsAgi$e;K@#bGHzyISu zzf_UgA`z0dj8%touR}X~weob73i>^dMHw5@!>nyax+yARYx+*hP6Q-M@h+=@kL&%l zXhCox;H17?Fv+enu!A@oEAB~)HN*XOyDGRERM)*0z7((pe85`X+Y-xxy+;n%L3Cmn zno4$T7#Oj_DvHmM(zQtttU@wJ`=;;mbw+A6Dv3Um9g6aTWl2wKo_QpsvB|N$_L}Fq zV0aLzEw2;vDYx#suGbMl&dA=}b%aqnzU&W)*1L9h#gj)w;b+YB5in1LXDyK!3_YjuA4%0e}SYn6)QKsE(LlpY$DEkI|!vpb1>vlX) z<6gVRG#U{zFVPXv68F84T-Zsaebe(B>pZYD67EVl(gLqlw~UDX5H97Lt-X=Sa>5`5L z-bNx74W+DPWL;KdMN&S|)r>qXXXHHosNO)!v^=dCN>MH8Q)x^YqqY_v!XeesQLWMLjDF{k$>Mn#Pp}Ns$pxtR=BzN&$`DY(6$b6A z(lgSV(wC&Kkr~v4$=A5lOof_2N|LL-uN=haX4=(ulwl^6n_0ku6gQfH4#Rw?0WV7L zD{d-&*VThCaDXN{00_)qvk)o-M+kwrxt-LQLK-j+b=F`G}&m_iuR;<8Cs2m85Poa^+s^-GnaDEqSZvwLHO2frnsi6Sn((Lo7 zU3z4Og@pE@u<)rfEDGwyg!CQqU@jReg-h=%b;xQw8+Qtv zI2;}Z$3QFTOTG=s>f`z@hs&eB6Am?`!4ZBuT-ugjk={8uF!AjZ;o;Dvuk0$rlUTzO z;gMkl^Lv8iUY0&kufGJ&oC@(>Aw@?(V+GIAF`efUI?E5ySpl8J5uNZTO0@C`-ERoG zD+%2dQ97EGj-oV&(JPSN_`AnM4N*8cu6t6{9SOB@&2dqa3RB~n6N2lOPjO>-Iy@CV zwW|zH(^(X9b4JL`nefa$xj8eehH=a4hT=|RL>2wcqAro3u!{A}?C68laJ6}Ea5g*` zo(|8}$(NnI+W7eHPyYVO+8dV|u@3#`OZOh&$4nFj;$thwMMm4XWdj#9`1-Yb%T%NE zctwlSJzzDU2Fu`#fE?Leb`9-T5y-I5IyO*N(1j6)DZRykK8s`EWw)JRBg!TH2t-91 znHqM4zP*H`qOqT&Zygxfwh@>T{lw)09+^SXto?*uERY-~J3$PQmDMd+#!$vOU5dcE9IhVeA>A@hlQRc85nY z7r-S-)qrh0`Yd;9>+G}C>p4mY(J+KvfLH+rTR;;|gP@NcrGsvrW9uR{0KCbyx($0& zqqRM!6U_p=#83zz3V=rBtvnc;;6mRO0IT5vk^$QLoo?F)sM35OqeyppJ+FfvfF-pJ z_B2hQPy-ALK#DVl@4JM>=wB>NjXqr|YvNLXCmuWE2TM$&vvCmT>CBRaYt~ost zt0?8Pdh3prk1>=0PJxvmDo8*uLRWRfRM^)*!HOYex6k31B`B2|t>Ru1c6&sU+E7Yb zMajWD3j&zNRL~p~P5Q=dUha* zYia~&$4jvnZ^Fs~OUL8V7eM{^WIjY$;N*m|to&5Z=(< zUyWSj(T_kg;BgF;>9oOQNHF#n2QrsK*_97ULj?d4a5>IxD`-9RwlYu#YN$3o@ZiY1f;E7mbD+`4v5N`}I>?7R zDzxuiO&ovtO|S(m;Q~e}G1P*!fq!N{yoKxnzVUZ>0`iq5Y~2bRi>;-H8L`KWe1sX~ z-3qUPWFle4<%USMU{D|`v^}!vyIoeWj?&hhs{Zif&rr+#WluD{RRpgd%P_VRJ;|uHSDd}eYiRl zVN_8^?hJtFoJ4Wh;8~t}Pew1QO~*h4@jg9F%>!ZzAd9d)+9+6$nupp%KcY7$fiCrf zOLh@GAvsDcIhAZQ1wR#*&xYEt8;IJGmk-~x=S3oco#w{@HsL^Rv{C1f0=z4NWAVkVuB^P)2|E zi(ky#)MX=$N7rT1vxZLNpMo5T)EDstv;;2IabkX59zbzm8)e*Yh*1>L+kj?m1ndC1 z?U{$ryQTjAc*0MJo^a-&9oe@~+IL9llccfe9lU@$zrvXRBi9sZ*!SuR5#)1Z*OI!4wg~s*3;>^Vj;^bE|uZBD`vLx+XzPANVLHv2T&Y zTUz7{o?s411Dt)Qhy@{zaR;8(Zz$KbFUPE`C^2fyQl!dm`5PV>u2j!PvW)!QhcTXpv1HVO$!V`+s^pR+a41SAh#;C0aq+~QFt6_4iZa25-?KNo&6=c)W3YEVDT?pMpZ{@q1u!3o>HZ}32xXx1u9;ICE!<3Lx zxTd!Yzbf5S7z9E@5Gh9isU00b!_06xY0b}z(9QK9 zA#gSmX13+eN&{6`;__AY``E^&^TUVT0STFA4=-zKU&KzAu79x>c8Yd)*>aoa}p;gaK6Z(`Hs0@GBqaS_Xb1 z-I^HWFUtCTJu|Cpy|#CTquD2`pNQ+;PLCIDU^>hkpozVK7s6-V2Cjq=2kHPjkqux! zpyaP8`E4X{ZN5kO-=XApDG_@mc1?ISe?xVGEw3R5YZzlu2@3)PCJyBli|K z<)`K68TG6zCZ1tX#l@Pq4Heg&;+75pi^(;5z{s9Q@cSDL3Q^8L4(%HrLqy8wo3=+D zLkyy6{?SZ~-Ecle=h11gOK4#(5~~1#K)|pXqgHW2EUso58N7J$2IUC2#;BZd32@kE z4<8piiMd0Ih_%sv?=H$*jAib5VzdC}vtR{c4Y)hYBNl*&z%NVnObR<($jko+idJx? literal 0 HcmV?d00001 diff --git a/models/__pycache__/univlm_retrieval.cpython-38.pyc b/models/__pycache__/univlm_retrieval.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fdf41c77cd230fc05633d5a250dd025956787dcc GIT binary patch literal 6972 zcmaJ`O>7%UcJ4nm$>xtJiI%0YEz`0soBokJo}VO+GoJlZ0%UB=&d%-xon16q>?YL| z*`%wBvMdy_o0Xm2i+3l-0NFrFfB-W`ruU@_P)zh~t6-UGG{$Kr_|FfH#_6w>U|5>Pfh$s0|yaFxIds<(2^}gX6 zI@OJy*|%IvmCatx&7sZe<@&a3XKneuZD_jIFSte3lIuDBl3VJR-EzO;R&?$6wZIPY z&$J*P+TH5D>COa|pcpvMbhj4f4rULvpcIs!Y5Tf67ntADn$?%!MQa+;`D)1c*9LJr z>c}FM9}a@B*VJW&DxVIv!#LUt*(0a)R|suzExS(@uWw7RT)JC45d(L?W(kh3V<@q52sT3c@^8I$#X2o69d7MwDCM|)kQe%)KGuaJbg#gRLJ#!7ctPVFp>>^ZsaqbGc;%pq z5%wXq&!}1{tJTt4V5+{^bhUj$tu%*~j?bJwLBD>2&Kay`;+gAUjqVJdc~v4V>Z(*j z=}gDySm0qnXa{FWI(!!ci!U9#C2WyDG!9|Csx9}1HXCi{g1pGLji7LBY>_W@&j)$D zF9gmDMT1wm%iW7uu|Zp}^wF5MU7EIC5_xd5A_{n4hCEk-3f8#V(GK-G9<;6oRc!yv z-oH7G?DrMcN$yQ+)OfV}jq3?#&3lcZ&$k-w0c-HBu+fip`G|#$LAw#gEt~{)I|$Xc z(Wi59Eor2-Gh#lE263Yu^}-ViM18*#dI`=<<2p_N7Kq~Eh&NPwgW69F$0?dHq83mi zX!)HeA^M#8aRTo8A)C(Iz_GPcHr!c12)yB7(2L@Z*Ylr-?4<$2l%hNBPms}5b?gZ75ag!o#e~w%gV7i zs8)}!hk9vBg(_-KyrCblM3!2E;Zu~E-%sQW3p-K5L*{Lc+HL3~#|NynC3D!XAeL5p z)a%I{9OodBR&Nk@q|+Y7EwUNECyOz5)bEjFor64Vo`iDL1}Q0Jp+D${F(371@nc#r z#7fc{#388hus>{;rIm!ew(A@jv~-SaPnzVOAu9Jej9l{e5Z;=qR7wp+7Q_A~oFO)t zy+d5QtGF04cugPr4cv03Dz!bC6{#}pqUd?8o}VP1_l5TB-&qfLhXcmflP#Zx>)lZt z`|<5ww4OTRb*08+eH2GKy}m~aD7U;ee9F#a(Gs5I9$wn4W7PG6?&!8&H5{Xgch#)v z_);D1`1?OQS1{{(U0>7}F}iNp7`?ajy3b#m7-)-3X7YO<1qwo6!;`q^;mUXav`)UP zYaE#5lgYDvroE?qQ~N~wBl2;U^4vMDb#o#OOIlz)H=(uuo~&CDMvxB*6!wDBi`K?ZVmnApz^{R z7c;uEqS&4L63ULS4(72+_0Skw;I5APnM0CofxP&^;-S_tf~w-81lgAmtxAW+!CQyg zxGeD3J%`>xF!RDVr;RJAc0SjC`mXZz=d(C)LFuEK@w6Umst91vVl1oVajkLJn9HX=c1k zcGtZjmM8nwHtuUeKd^%wY?oqiRV)Q9OkFSCHZLuP*(b}-(HAs40tRAdGitBXhbnSEV@jpS6cf=~r zBb~riaIg#xn)pIqlx~3PO+2@zl;6)NUww`8O;B!5D2rRD-I&xqw;t1u-4Qplb#AKK zt*mwnwK_C>6K62x)0R7GuWpMqu`cdpJN332)p*l+ z5pTb#bxQ3n?B#877dG$?(ZG`v7x7lU4d2T_Oyy=;Fmpcb{#XI6%?CpEv zym+r|0Ex{#>ip)5U;XRD&5s{+Qvdt!4_*RpykcijRRDu6p(LHYzw7nFxWl(TeR-K$ zj3IAYGCu^m0kA>nVF;cIaj|bue-$xg#A1(!aWX(`Mo`@4xSUjdjwXt-Uf)l)WhomM zL@h2Y3PbD+<~Mbj-wwkeg}zy;TkInudu9SiCWwRMrjy8pN%KkS>>*8khuxy7`FNm0 zE&DC1*@%t(p~o(u!YJk-TvN2aLq&rRq#cAiQ7e@C2D?jRR_L`%uZuLKs6e9^1&Lye zDW%LJ-g56aYtq_@!Y9&%YNXCtou=kmfEyt#zV&{wqaKgqK|ca$vLYZK zY5T)r7zco%5O14ZA@(XQz^nv>IRiv%zR!M}T5}2x$$ZNnl0nFv-y3fE(gXmO`VO-BJ?uJ?^tk$P>4E zdYzx0({ePNl@t+X9Y>hbfsBX&Y7b zsPjn0s*|cG7p9hMPo^U#GAhbDxY`g}CU=y$MO0G_z;cZsV=U{}Re42UG0H|kubPft#{+7*L4UuoZ3lFg^|Da|r8$gVFfF|d@H~gnplw(f zOZ6(XfbJXs^&CdpDFvcYxQB786E3mR-r^~+oe9{YJLTj@c`w4=3TyuX3W!-6BKG~U z&UMOuENKS@uqhCCTG}_zd;Yt|*ch9_?3%#cM%SeH9iZ;z9$$HE2O1)a^(c)VknJ!%3 z>wCyX9}u-l}V)5oiPD zMU8MM^kTkIFImX_p9jsH(hvIq)?|M|uOHHjZhP!Gy-pe{+eF(x<4K6yRM5Rkr^I|c zjI?OCCqGirRMx*ig=3e2v2Y_12EI9Ab=CgkzC<}F6K!-SvV^^W{OGokyw7t#)G^9N zYd%Goz>0ZcpT(U@q1K%0E4;DKRDH!a_9+^))`EsLX;s|$?&GcoyE0O{YC4RxhZwWv z4?{!@1eOIfsB5yUekDne8bwI?24e#z^-qs3@hSv23(C0I4o%sm*B(*JQZnQN;XtSU z5FuyN=dG>u;%_1o^ZJHtVfMe^NhrQ+IJE`Sf&I;y=XLt48hckyZ_gVdZnMZJk;&+~ z%_xnTCOlb%>Q#EpP-C-1$(tV;nM_Y@dN6!x8c(}iOeY{BF%o%IVK~CCu^hEQXaoRYogN0 z@atuG5JzW!j6?6rm&%XBO+G7djq8-AID}wd$3xDeYvADMKZcV;D#FBN zO9y<{vkQiy1kCU4tMuW=A){ns(m=q=&{<=EbY7awq zpg;WJS45tC69U?+y{o6d>Dv9?pyl_H4^WuQ*}(mv;3rRkPY1)JdkSo18-!9;O2-73 zRX{#3g(d{^rE{bS_Q#mgG}&L`<09Fp?kf{uY^NY1<@{$vo7h**=a(oXbVxOX@G?LF z1#i{U)Hj;*S>#IAkpEN3Rh2z;%g6G?2_v!7(@Bb!^XZPo@!C-;lD$o1KEuncC^Jsh zQj=9_U6sa74}$$rxdC-bl*Ohep8Vu7qgSCsZ9(CF>Nh?hM4&uZ4Jf1lo1Zg)Fusrf J)f8;~{{i{Ja^3&{ literal 0 HcmV?d00001 diff --git a/models/__pycache__/univlm_vqa.cpython-38.pyc b/models/__pycache__/univlm_vqa.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a0b68c6b03f8ac2e6a3e9e9c23b0732d8a1524c1 GIT binary patch literal 4913 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 SYKaie8ZzIh{n~md@c#<}vl`XFav0N{rT1 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 R=>Y5M@`dHA%d5)|{tquGw)6l1 literal 0 HcmV?d00001 diff --git a/models/__pycache__/vit.cpython-38.pyc b/models/__pycache__/vit.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c8a211ddde8538d15fec7af1bfd954d24d340bd GIT binary patch literal 12335 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 literal 0 HcmV?d00001 diff --git a/models/__pycache__/vl_model.cpython-38.pyc b/models/__pycache__/vl_model.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a26f8a8be2cef667c99734fab1227698a060f07d GIT binary patch literal 2483 zcmZ`*OOGQ(63$FlmCNO_r@Mz&vkMdt0hVUEg#?!oLJPyNmt`&t2qi+PYG>LmPd!X# zdYG}?0x=*?{Rj5cbL6o9V9y-(wCB9;ss936G(@EBu^T~I6_pto8Ictc-&g*$*GmXo z@BHhVob?F#A5M0!049Hf+ei?SNLrGLa!PT`N>;Uas|tAF=dCiVA|Cm1P_}s+Y{If# z#XMf?I#tXFB~`+^l>D8@NVdNxvaRB2`mn`&GWdiH;tPmG2Gs3;R+yrx@94ZXqefRs z5130nP}<&X>QOOv9UOn$NL7M$4<~n;yQ(f0O5X+ztLq`U+P&~~@G`H1P=r%SIFs>@ zWJF~k!|yx1wK^0Jek1|mAsndVu6*_nq3R(-(&8jH>X*Z#;|k-NW2^34G4$s-nwqA* z_?8ml0<;=#f?>cA?l#;lxXr&oSn`mZ(?^gE%g$SpKB7w^f+d*-=b>b>Re*+)^&E~i zrd!Q4lF^}G6YXV7w(myfa~sR}A^ncRymOc7g-lKZ+gUQ%T?R6RU+*l02uYoRMIS8w zDX;8U?8)xN!gkNoB}6}^%V0@G&-Ry8?#qKS2K%qbtB{hX7XMAM_4U3Un;TDNsNBg( z_NK9Va3h=Lc9KC6Gdod!J+s@gzG1Re2Io4rQ1NV3lH&kSk$w* z&AdKC{U!5zh1~@9W%3qTGs^Dyj@pg=WZz={1RCf%7Vi4I9>PQN&Pij0#hzM(UwnA$ z=9m01m$JY!=A|$*H7wxi4GdV^x_$FY4do{TrV&*xEOR|pi&ybHvs~wuvPx&w+}P|y zWf&@hlz1b*p}_XI*n_PXDY^-HTvTKK-aLU64kxQ|M^z_E3RzSxlxk)tyi?CBF;TfR zx(hoC=v{L09E?5!%tWi~@_jhGm^DpV)MHWR4-h7ME;RF*(k>RFs0%BE##ZH5M!KoQ zOe?5UQ7h>>{xuO>`~dx2etY-ApC(2W((n}Evq}_9UH^S!AlZ<{TUs|-Gl=N=P@FlW za}6|&Na1~d3IC84 z{6t+xh+&x&n?pb=#RevcEjJBwQhtSp}U9nBnVN^*%wV(^PhauaUz z9}s|N3JkDkfss7}kSPV+w`6d}P8qC*fcCeSPakM7T|tloYVHQjnO0lUg_T+v|Q+mqk zpm-Jp?|526#H>8;Lr+?<>(+&~(Zh)gnpzoXbpWIb%BCJWW*Zktb*~sI{S-JG#2N`; z`?C5~43Vy&;2FfMQPRvUu;~URa9rb}l}`T!U3l?V7`ZlZ_}96X*I;5$5NPZ^i&;Vg zkSVtJez}Y%OO1afbOeH5_0>75>!T5TL{=XPo@~dCe&nK${oDA!J15;H YIevHbzVBg^8T=9;iKIXoDLr7n0oq-yi2wiq literal 0 HcmV?d00001 diff --git a/models/__pycache__/xbert.cpython-38.pyc b/models/__pycache__/xbert.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..417a30e0e9ee34874648c84a9048c0ec8d3de833 GIT binary patch literal 27884 zcmeHwd2l4xdEa!;eP93#2FIRkE|(-`g~gH-LrN=hrR9<FY&F}; zRdcO;HQy>!3*qx2d7i}cC@LSQ zS>z1cBgh#EbIu@V)Gi^X6y`jDoH4tMoN}1+Aacg-3FJ(KIS(Oc(w;)jRG9N-Y~#6D3khSYx zeX&`0-HI7xo>})Aop!w$B(H3&H=Rl%NL_K-ZbtaNq5^?`=B@@(Dp z&bMu;=Dg=QZT7+qX7(%E=`!w)m?h1>S>+YIkM@51|mp7VT z0|ULV(!t>RdKDa*bFO>Op(jpz(Ro%mc4N`&C|z}M^v`ZMF8g@C?Ot;f`qTgF)N{@Y z8+`2or?s)vaH99duXNU&_U>%V@-SMsqUw#dV@plJq5ArI^G0oPWuv`@>T6!7w$xGA z>dFqRrAFJXt*ec;Q(tI0wH2psyR|y0;mo1Nul)P>93 z6OQsOblOXeFZd>aex53HmD>1tW ztkwPSI)!f>U-uRgFLpDwZR`NEc;;%{H&zq)Ch<+##_hNttED}2oB0{$@0iHRqGT>C zmAB2?%*&xv0eM3^2JVV}Y74W5+}*W4>W0?bJ=yUG`ae+>T+; zX#w!{x{SEiu&=*j1aW8G4GPhqkcpl3s)1g=`Z_+rWcNvTLTa+5GW7+w)7JB#&&n%mfPg8WLuww<=VDWEfe7JwOA4u(0&wQfUh zFxjeKukG!D8;tFjnY~2Vnd_)lP!uq#wQ*hJ0Mk!mvv)SUU@UCi*7pF+&O(q5?}B0F zEH_-wQMHAQr6mk0iCwg~5~S*?-d=WslyiL@Fc74d9k1?rN}a$&1*t`W%~#AI;X2JF z^(Zniv)NirFjcMgO6=>8p24df<(+X?>dHB@y3uae+ozk2GgliffC*sVUf!rLJ7-!% zKkk|93xLkq^&9G5RCXF)cLYhy$Qe^cDN#0x@xw;Z+?*T);@M~g<`kE3G0AtrPZqzt zzksj%StJ0G*jiSVd}BMd6WdC*NBlUiY>?kinun0%CvE;(mN9jq-6jzGkYAB0u4q$j|s`Kl5me&$g3(8acd5 zg;!|+X<|;@jovAX$vsFaY0N7iN8QC7?sJ)THNz`Tjd~DCC9B?wpCH?419KCOrF<|X z_pXL2H<(xl@~*8pH)>bwO`yeE(`hezD?whrhSdSN$hpo7(SuwM9CeG2u|-K9XM5=o z(ecJTjwBY(7&&vq%$OPDFupl6Vdk)IxWeD&#I6+!U&pZL^rB(7*huYq%c<3ZT&)Ix z*Z|(F1%+Deg^hYMe3Gu!?9L+g8UaK-g(MgX0dCQ0Hu0d>(X%YV4yq$aID>8xNybQ+ zg;8Aa^SV6SgI|fr_;KfuMEF&3E3hkYE7sReyc7eD1;W8^(oWkM{HE-zox^Y1&f5k2 zX27bDeKD$8!NEDKbSW6+WITsG(6BpkyoRHo*o9odwVRi&fUIfs zXK_DS)A!mHtKnMhj%U@aR>)jfoh2cF?F~>s#XDd+&Er@?yyZQ;1@FPa1O??Z>t5rk z!)+Cm`tJa+j=J$GDWdG>i4ID9rxJo?)FbROvA%i}ll%Bu$p+1+gM;dkuAu1FF(KXhUWp5K64DTq8g6ZoJZSH^ zk}cZBDl$JGMBVNz5@P+7ao8~NmwVlOJ(0{9C1C%Ok-%>mza_J16pabsn8U{A;XRwG zOBVL?xr4=Rm#YK){OVe!I4;>UIO`557Q;VF5+^kc(i!v1qt`CdbOtju)LrOWtG*sh)G~XzyLZvB^^Rik%{%vQ?+b5ynwHoDd!KHMhGXva zVymwg_Z&D!z48N76vxY4B3UbL5zSLXoCH=5Xm%C6!X$YGqGj`kV_QJT+X?dMHqhTr z0u&(?(tfVHdWMOGgUY2&^TVwu^KL#)xYCqiH zyMZ!gx#Jl5#yH+6PsaRlzU2>jW7}oCIlf~Y;XPU3EL!;;lp^>Jc@wLX7)$)N;Sb$3 zwkG`SXH11vT%FRt+lTy#9m9zE6WfQ8dql2d+eewZH7SfT-?-1mbvd>*DKk9j=P~{% ze`@=fO|dm$5A7tjkAp2|9_KY?r^u9}#=Djb^y82}wL0VneBu> zwPb7^_7D3hDTga;tfJ-#WKRKK#qCB*GuU?nU|; z#S+a^Oh{-06M`6f0uijyb~jo<0+p>mFnB?Ef(|snPe0dL@*p&-=lSLe zUl{7jbSs_Cnp)%o3Jj{rr#Z-OUSoNqv*D`uA)|6cixESSlnOZyItr4)F79*Jf~+ta zE$C;0@rbDyd}AWY-t}g2Pyh)=A(c{K=@xUVqpiku$JV@))`zGXx>n6nY!Sh>nisAm zx)JUsycy|M6e2f8d{ixV1!hP_wdSsS$nFsjQ!Pg&>&+fNQ=RIvbJDUx+^SKb#hmR@L=nXT^-^|LCY16bCO7YAWI|sAgw3V-G%u@MEWGA!u<%6 zST2{$m9ThN36dy$27t_uRusGDhKOCLaD}z(YlUuPK&e69b=kc;}Kczmte3f?E>UB&n5W z6x_6maex^h%h#3Kp1@5{Y76yT)PI2%Kw?Qk&z0gGzG-N~n3qB+$~ak(k8;9~nse$H zYQn7b#0Sz|^?oMPNTBM4>etZ<0dmd4(yIq}CENWiypk!C86>YLbN+j{a9J1GY_QXY zR;u&{*W;d}nPOpHbCehGb?-w0nx&~dsXJI3V0%sH4bly{gDzWhjB)j8yrKI=4Mrp$ zd=%F~L8~HwZ=lKUTW;ww;ouMP?e;hah}24jz8;6b_DX7BAiFaxz05wa1&$7|lv2!D z#CPB$>=JTZ&Ts@ZHiY|7?V5#Z08|~Kf5Og*QlMY`nh%D!Mb1Z9_2M46pYzMFOEphF z1VKuHgbz50dw?2|-;-YICg#7ECalN|S2BHoLa5iGf{ZPt5kwGo9S@pMYNl;1aw?*R z{a}Rlhwh9Q$Rdgwr`!D=h={y{()_UdNqjHh>;5ey9suN~0ab;mM%YykU;=_pr7%;f zAd)u|A2zp)Ez?U$URn=!)r68*=VZD$nJ_2Y&B;nm4s4fZ1zB&dmY161x^+<+n6+Mu zXq{>gv`|2r+#Vw6a%-}O)W!)jZlU|CgQQXn;?S9?NoG*v*5k<4kMeeki7)~^9^glq zCm>H68zeW{(0N82ds4ItK@#===lU*q5AxbLAVt1{ceRQQm^eXk7(z!XmUumrgb0kk ziFld&SFmqyZTf1M8(`S;s4t7JtPhcb-oie#nb2>7t%g=I4UHwYb4FNfsMpEFFYz9X zAsI5TVmY7Mta#r{MG_##4V)#@#J#OvWb!d4A7}CjCO4V9grr( z6)_op*StvtCFUwM#kAC6`wMSEOI;TCIYE&cN^YUkmHnaQr#SR!CNi&LtoTlN6SxHQ z=#KCvW1MC^u-=MCy9o{bKk+5%huGCW$7G)sKhxhCVLM8Foe5FO!K?k3Q0;WzYNr%) z_N;aqnf!?6GvX@-3=UZ_H5lM4w5g$I)XfE1_DIZtdl(7=87In}N77>``V0wRDQr@{ z1kDJ-Sitgyd)l$g)r@+Y`T9CE$--0+mdtxF&B{ANLN`GTEVKi!1Bz~yVjmKD6dA#! z*vbWG)Rr7DAmRK4`Ox-Mqlix28DzT>QIHMInvj3TV8z|7MGlIhR_>OU{X~AUhoxlF zmC&U=m9+YKG_)V%^C%arjE7nWoq@1KT{QL2*z>afGTU8PgieekHTsO;g7+8jb>UPK z3-wxM{%SL08-x@@70O+5b|AfJKa1&2lC0WsXp{2z1t%#cN$70sB=UwpfMF$3|H&84 znmg|qcA7RG4|=tmhDW307g1-z2P-7x9IOqPBRrdk0oNz-tp6U6bUU?G^owDO1zd$~ zg1{HrvjcU(aCztQpfAu*;mdJBlXf$)HS7NlI0H*yy}5ZVQY;MU8(_Y1J!`?SmSKo#TYx4@VBvHZ7%YJuIJ-zQ;7WcD zZ3M;hE`%ofGidutLX7)~CM+827uX4bj349G1k0EDfmJ(vs2x82StON_H_GZjeFH;G zbxIf>bg^9u&C1i@!Pp~cWLDkW9Xm0VeXUwfm?y{J^%@04((4=^vpP5jC>p$y+8)4)IAKA|%Q=Lws7B;J_n7g0*@%m%zZ9IL1Z4#kCvK;Q? z6OGuP<{nclv54=$M>rqkh?IQ*MIa>rMd)fMazUPhc}m;+2IiV95v8&?t0XhhqXoCf z1e#ua8x=u}6$`hQycJtb-87zrsaS}98ir+;Yz!no#J;tiJU6xVd^TjR0i=k@c zrun1h3ovuYT3{b;qXgX8%-IJ5qXA9pqqx`WM9x0QJ;q(Q*lDew`!r{b0FsKCvCZke zp>+}O>|;&%*fia9xQJf62&agXtn^+cQaw4?Aa_}O3NE2skkpf);l04aG2`jR{u6@7h#0$e2RT#Gn!;r#%6iH{?1*zqFzCv#R7|lpK<(B zDwDNYKt(W^fb|j*QMeUTMgjx617)m^Qk(w+w&=pJcYmJ`Dco zqXC7!0h7Wo?je{g8IQ)chB4wNX{fNX%mEo0df`c!9t<_xH=5jM4EI^D0EJ8bC6d!& zk{Z9blqB@t1e@~1}K&^FH_u;x{ zU4xIJ6+weF01dO&bB;%_u=;Ab2yk@fXJ=-TfdNV(i`yTT~9j&)VSYeUAsK)qOJ6KS0Immzn$(B-s9vAsrdtx5Ce} zKiu>&V6}xF{mZW8kY2S3tG}$jP(#2R2TUExt4V&{NHjwRZCjq{otf}$9$lM^W9B)Il!R;@4 zvRc(q@nZ>nB*pqv9b?uYL*!P=%-lWfY6;Wt)oe%mxyWu-R6SNh7D#W0>ab>rx>hXp zD=aglb0c0$b_#)2Blo9p_2;>)S8)+57NCSH!|VZL2>2oxKIASz9tm7e!h|9Sl7etW zI{jKE*LSD$UQ6X!s))BnOvo1aX5!sa-!Q+G$#?Gz^BbEY{_X;!N=`VFxr-r1Iamb! zJo^1fj$jHGU_i1142U+9{4d2)1GoqGLXr6UohYnC3GpmEz#X-R1L3|FDt8JOJq~0) z1Ic!3hkVHa%575Go)tv&3_;>by}c6TpjK*J1@7(~tXldn(KgkpB|X2o%U#Z5pzl$? zFK{lVkq}i-6KVq!gLaW$jftlxxFm3{aTq>?Z>_+DgT`oG7G21{;&sTnaywT`OAUpM zubqLl@go>o=j_=bAHNIRh+gt5902Da7AGJ^LTNaJ5OW_45xFq3d77&qC%=w7d~V+i*F9rYK`8TA*Le2xiu?_fmi_3U>o)Ks44Gf`x{g;%1|>(W)> zHowM671j=GQljM)BGjfhUmY+f`@6! zA?b&uP(cN|nT|dn^pNM07=#S`P$JJ)xMho|LxT{%nOY%KYF(@tL1Lj%cPssX!~uq0 z9fT5b#puIH_w#W&q)9>cSvb?T9H^v1S_{waj;sC+3iZhpy*0WQ4{}^1kpsEM6BA}R zzIlxMYxiWl0U#po-xrAbwBWzZ&ioFOgP>-TJ&I&!U>z+x?^>FO(K#6Z{1)2CvMM3~ z;GZGIobL&+40C>-X*X|NzI2($XAhj9Sq&$gk^SJL%k=?IpF|lgMzVu6%Snt85Sx48 zsvVC8M&hCk1bzM4f2jz#{9eFUoYcEZW%_2M{V-I3$C3ST7#dRvU<%1ZK+O>ODReIK zf!Sx@pBU(!1XA?(?%VG3-$fUWMRj255{fx{mX5Rh4txi+7d8TvoCKWZO*qQOmkqed zYg;+Anvi|cf$%xl=b#wfD67p+#Y*%QAW;AE;dt zEvMe5NzVSyI`6A*p95AC0@$OB_u*NP((eZeMjr-gMis$1N(77mHo$PM;a4uY6isf3 z&w?azTP;W~HNdM!Mq!cr1_2VG8?2@l_VYo4Rgt|iOYtcXPdXk=7@K#8EJZ(5%nrOc z2aV4~yw~Sf^e)~WMiLZsoRW@83Pu+Y*~sV;r->*iXQ^>LxQn_}m(hETWen1(F)#_h zQEN3283>)vAF-}p*Lz6%_wnF<_L@isxgfJHT?Z%Fvn?ZASe?Ic2f#yb9g@kC`=xkVR7?`ZbRy=RrJ$vR%U0rWno$aXQGr%Kf z9(?qHM`j;*^P`VEbZ)*@vlebx=iQZtyJEe+?p~|68#C5ir{1!jTd8k4j>27Itzm(L zx9YfgcYUq1V7<@XTm!T;ycucrQWJ>RdUvPoxHH!IhK)&`o3-BAX*ruS)}@WLy1Qw; zr`~|sK`_p{n$1StdbZQ-xEpILjkZQIb+x?Ff_7huob4>Eo}J&v;5CmqL%3$g(j`P~ z^){}ez7N-OP~;Z97G@GleSIDCh^dj4m%fYMh!t|GLlR&?`w*hOD~oXrx{~rb`npfz zMiWu`&k%Ki8HX-8WV!}yis~UcAlwwaCQ8x61TMbNB}QtNO0JLKiXP(#s#k17#M2-h z4o$?BASGgwqNT5gL_+cyVku4@O970u1Lb~9`_tUPnrSa!_tJ7vGPRXYHc*dbgTL*5 zc+Kwxe1(w)ZiiwvB-ZdFkZzyC(|tn`$_z!&E^pm{CHH;o3BJ_D9Vgb)?eAA~4-MjL z(ie_|%di@&K}d}>SpNgL5h||Yx(}a;4yz21+S8v8?%$vNiVpOL^pIca52ru}MnEZj z6mDKL>0qW-!P*>xvKk?hLg!w9${VQ5-27>QNfP~M>80j|@mFDhmLRZMokyH4eokjC zaz2dO`a!MCfnNWtMT-FQ#EpvJEoF%`JnVIiX||xkbu1EvZmXKT*u{o)pUy#9mjo(m zx$3`j9>2)s=a_sJNhsR~MQO9!KV9m_S*k}zWZnyU0NNApL!56;Tr4>fI@^U1*yqiK zkPz{wdV;l)7pLP*r27g|TC+9~154XN8edtCj{s=HI2C&2%`kM(HlfLoKmCjNRt7XA zKV!5JNG;K^FfEJLJ%P0Bn^?xwR+?-M4A-Ds2*1dPcUV}<5=__#?uIWJLLB1J6V}cr zYX<$vu<#80mTNFaFmU5ue%E5^529;7tMd>bkwbfIGsAlQ+!8b_c!~fGh;ms3e9$~C zo@xh;XmG3?HOGl?ge0sJ1M?6)!;_MiwX@qfS|e8x3L+tuN$s^LK^aK|VI-$wc7gTy zVq2(x01MfLn9KDw0P%tXO93M3@jQUO;6k#Wi*hJ}MaO#Aa*2lPp0%_g#E#GT00>)@ zL?EFcftpjXXLH?F&WEJKJ=@LV7V2t?5p(6PS<{+CNPPF~74fy5uMEDyw=T16$O@qE za%KUTY7%2ct-IVzQZqX>V~H#)jCJp^?x7}Ytdbv%?<7Jg>WLg-@gZ>M&+pfdE94It zoXl-Z;xs_1G%6Svu08`EH9wq1`^qnwd|0@f78@QdgK*zA$%?zk4 z>en0Y=J^jOgqMWV2;v^D0sQ7l@GMc+ye27d&kXxvG zi!0jakk~9p({Bhv3G#Xm>0(9A*~54s0qwhS?hz8y1Dddjf*(QEvC)EI8N~!t^N4tu zjWg1rY$OmNVSdX@nBPcc9x)G_aCv|~Zi3;ddBjMBA)h_6eLyR77qu0bjzZDrE?f-Z z8^_oED7pdrr~y&0;OE;ZSmQx5;S>brCh{!55ayY1tQnHLhy9^2FAhhcqIy4^hFJEJ zp9}L7Te&tO58+}&Z|tn{aMFSNdI;w9}}E!-ijf3-SkIbeJ<`K5Zne8pVapo@KA$`ls6>w zYX$kJnSJ_jd~0HBa%-v$3wr;(KjBXz1n>~v%XHt{772~9{h)HRDsLTza}*dB>C-=! zJ{=1Cl0jdJKEnwQw{v~h2^%@MANCWnnj`RS!Fdu=hp8}vRochnaMCK<`P+%DBM53s zZ5=^=$v@(c!#cw3esield$h9x_{b<{t~5p5kqxiJ|tI8U@P<{#T3r)_K<_lG`X zx(og>|2XFBC=OVXb1o*q8hT~s`bQCFe{^*M-${I@?BZ>zV}|R`1EX|t-@a0XHHc_0 zLTvUzszQXS(ThfF)4i=b1NkM?UFmZuX2Jk{+1Qo&8Ed{1o^iArX+IafC~Tcr3r~x& z6jV_kV56;g5jLk$wJVYe#tnxt3-Jx8Sy(}5kIz^`i?5n!S@NFH8u|ukZ34tlEkH$* za1TL86SW(i_S1>)cB1Y$H$t>Yn6GvkIIsumE+AUc>~2piNT{Tu;aNDEm}3&;M51WU z!v1SAF$+?LK@0DIUl?~eXf>DZ@EX^A{qzSGtN2fP!N?=9XglY1AuX$7AaTllo* zDf(8hQkm!6X;2P{xQ3Eho^XT1dE9wT^c}dFSW*jAmshzo4_n&i=dy+aw~KfwL75HB z!mgcTVM1o~nM~TN7RxTOL}mBRxqvuAXf|C4K`@iI5PXTHgyKt74YC5^CbYs06>?lX z;8}pH)tWnNT@dAwh6R9|yG}jJ#=71K7*uaIxdQK63a4b()XvMg5uj!Sgm4AtO;jCi zeW;)`w

tLYeGly^SJMzvd{x0L%}-Z-$M<1=w3|`{lMB#m+zC}; z;`geFW)4~mLc+j&a93$rl9*0}6zUB3Ec?K`%d!xzr(Hbg_g2|Vd{4*vmG5P0A{sj~9oU(9LNvU7OPw0!FB3}e~k&X-MA2Ing zBskngbamaDf?>j{h8^wUBQ25n*N0h%pz=-LiumwaQ_L;x-r}pH4-J zaP``i=}A5n#mkp@MTS|AW>=@~(#}dUB{BmtFCSy^cOa=2cdhxy`Q*-XL(xP}&=b6( z_D`bBwcvH0d9Nd>mZGYA98YyJ%G;aQ%0OY(J?8*J#@bnyKZ3Y)G_hgGh5^tV8{B{7t!GB*>d~*@sfzN1oC*ChTgAnYZ zaVW6j%O*X$mmp%H24WD` zws4tDCHscEVRH(sKxi>`XG4lQy8t(b0c9=*+;h}<05|>mes~;<$GC)v^@S!ww+|Uh z>-G^N3I_`HRlEu>`jhz4CUs7V`U563A>*)&rmMcf+k?Do-VD(=^jRqY-Vk6Q;dgz5 zdjqQa99qz-`qjv?{z_coEZmrw*R}tH?37)a+Hqo|(3`Ql_1w3yPn;sC>;+Nx_h#QW z_Dn9{zJRYb{EOy()u{D(pN^n&q3%8?@&Jfl^Sf0WEyXPb#mV~=%@bg(I>5@ zDB&vl9Kyv2zS86OqGzG{0cwagL$#@*E`@ zQO6b(5dMgr#@RZPIGJW*8|r5nLmKBaWO%oO_yx2wV(asMQvNtj1}ei1KCy#7?I14F zPNBCrO$ScskN9OoJC$$k)kr_t@`?-rpC!|GyHwoAm`a+eFfhma;EFbNyZDlkTV{ zxun~nCS1$GlvhN&be6o+Ugxx;HxYzQeKf81m|z*0>d`%Vn(Jecd(^Ws3)dU1L~&O- z8%;Vm>{gRNqjCDd2cXozKLm%&go>ou-WkDAc4)b2e!eTNAPeW6!tV(ks90CD@kImm-y-=U?*n%2sWik7xzEKul(Y~57{a85WAp%q8k36~{O$sE}A zZlk;8z3zg3Uxfns`THuAo=^n+-uG1~)SPv<4)^PKs6yFel6>lVoeqy@2lb}s&YjZ- zy2HIuJL5wuX~8uThjUx>G!fLoc< zJ&F+ajtp+vRDtGc0Dl@D%NVD?)UK%>)t+H4W)S0 zD4m`@bxJp}x9J`wg5%i#t`0}81DN#M=DW+Y)!`Q=RXk8Kq=}fftlMI4S=(2;OY$aCmVTk`>AQZ!)Bp8apzj*E^w&@H6J(FCah8+nO{v0Ko=*ZiT zqa9JZU4g>!?Yw!B$(NXPpULFIvG1G64Kfsf=}i#CAztIHx3Vl>4H9HMq=KLzTc);# z;KpTFJ;5S_6CNDH3ax`#(oSz!7@UjCFis`Tk=on^+hK|3G;aLbg zos5H71T=O(R3flmYE%b8TDP%0w=nf#NUONmiH4j1D+XLAarzj8EMa*I)za0RaByN4 zdBq4a3r@WS7mM(WGEpD?Lp1U$oFRe^&U=V$KDc`}p#R1HY5`-^B15bD6xLCwEHpy@ z6mP?uO&0JN2BUboQneVq$xVdytmd>wpRBE{;oU6CegwtAR{lP_MCgq{gP1Xnhfb9J zi!}H`Wpq%R*r)SA;NBdLH@b+*dV0m+#6hm&@KX%kYSn+`EB7)PWk<)Dh+gq7Ufsjw z-F(`E$FsbZi7n#kEJyJWvN8+{H47Q&7?XFQkE_~8&(f)vg?!6QW|)GATY#uJG8T!}cH!GxB^Bs_SwFFH7*^V@Af)xlVF zJG->e7Gi+^q$t;SZCtEQ^)n!|y;x0Gg6^u51CJ3v;MAm5l}A6S#|ECt7RLxeeU~dy z%Gx3z1cDCtimEC_O{=03ON}upBdH$Qzb@SxPh^mjU-W^0LcL+L s=e`u;Gx@0$e{=ED>C%g_M5$PMur!SHnUam)(NejT8<`w=G4{g$1%B-Bs{jB1 literal 0 HcmV?d00001 diff --git a/models/blip.py b/models/blip.py new file mode 100644 index 0000000..5c2887e --- /dev/null +++ b/models/blip.py @@ -0,0 +1,236 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' + +from models.vit import VisionTransformer, interpolate_pos_embed +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +import os +from urllib.parse import urlparse +from timm.models.hub import download_cached_file + +class BLIP_Base(nn.Module): + def __init__(self, + med_config = './configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + + def forward(self, image, caption, mode): + + assert mode in ['image', 'text', 'multimodal'], "mode parameter must be image, text, or multimodal" + text = self.tokenizer(caption, return_tensors="pt").to(image.device) + + if mode=='image': + # return image features + image_embeds = self.visual_encoder(image) + return image_embeds + + elif mode=='text': + # return text features + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + return text_output.last_hidden_state + + elif mode=='multimodal': + # return multimodel features + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text.input_ids[:,0] = self.tokenizer.enc_token_id + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + return output.last_hidden_state + + + +class BLIP_Decoder(nn.Module): + def __init__(self, + med_config = './configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + prompt = 'a picture of ', + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel(config=med_config) + + self.prompt = prompt + self.prompt_length = len(self.tokenizer(self.prompt).input_ids)-1 + + + def forward(self, image, caption): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + text = self.tokenizer(caption, padding='longest', truncation=True, max_length=40, return_tensors="pt").to(image.device) + + text.input_ids[:,0] = self.tokenizer.bos_token_id + + decoder_targets = text.input_ids.masked_fill(text.input_ids == self.tokenizer.pad_token_id, -100) + decoder_targets[:,:self.prompt_length] = -100 + + decoder_output = self.text_decoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + loss_lm = decoder_output.loss + + return loss_lm + + def generate(self, image, sample=False, num_beams=3, max_length=30, min_length=10, top_p=0.9, repetition_penalty=1.0): + image_embeds = self.visual_encoder(image) + + if not sample: + image_embeds = image_embeds.repeat_interleave(num_beams,dim=0) + + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + model_kwargs = {"encoder_hidden_states": image_embeds, "encoder_attention_mask":image_atts} + + prompt = [self.prompt] * image.size(0) + input_ids = self.tokenizer(prompt, return_tensors="pt").input_ids.to(image.device) + input_ids[:,0] = self.tokenizer.bos_token_id + input_ids = input_ids[:, :-1] + + if sample: + #nucleus sampling + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + do_sample=True, + top_p=top_p, + num_return_sequences=1, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=1.1, + **model_kwargs) + else: + #beam search + outputs = self.text_decoder.generate(input_ids=input_ids, + max_length=max_length, + min_length=min_length, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + repetition_penalty=repetition_penalty, + **model_kwargs) + + captions = [] + for output in outputs: + caption = self.tokenizer.decode(output, skip_special_tokens=True) + captions.append(caption[len(self.prompt):]) + return captions + + +def blip_decoder(pretrained='',**kwargs): + model = BLIP_Decoder(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def blip_feature_extractor(pretrained='',**kwargs): + model = BLIP_Base(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + assert(len(msg.missing_keys)==0) + return model + +def init_tokenizer(): + tokenizer = BertTokenizer.from_pretrained('bert-base-uncased') + tokenizer.add_special_tokens({'bos_token':'[DEC]'}) + tokenizer.add_special_tokens({'additional_special_tokens':['[ENC]']}) + tokenizer.enc_token_id = tokenizer.additional_special_tokens_ids[0] + return tokenizer + + +def create_vit(vit, image_size, use_grad_checkpointing=False, ckpt_layer=0, drop_path_rate=0): + + assert vit in ['base', 'large'], "vit parameter must be base or large" + if vit=='base': + vision_width = 768 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=12, + num_heads=12, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0 or drop_path_rate + ) + elif vit=='large': + vision_width = 1024 + visual_encoder = VisionTransformer(img_size=image_size, patch_size=16, embed_dim=vision_width, depth=24, + num_heads=16, use_grad_checkpointing=use_grad_checkpointing, ckpt_layer=ckpt_layer, + drop_path_rate=0.1 or drop_path_rate + ) + return visual_encoder, vision_width + +def is_url(url_or_filename): + parsed = urlparse(url_or_filename) + return parsed.scheme in ("http", "https") + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + if 'visual_encoder_m.pos_embed' in model.state_dict().keys(): + state_dict['visual_encoder_m.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder_m.pos_embed'], + model.visual_encoder_m) + for key in model.state_dict().keys(): + if key in state_dict.keys(): + if state_dict[key].shape!=model.state_dict()[key].shape: + del state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + \ No newline at end of file diff --git a/models/blip_nlvr.py b/models/blip_nlvr.py new file mode 100644 index 0000000..8824cba --- /dev/null +++ b/models/blip_nlvr.py @@ -0,0 +1,103 @@ +from models.med import BertConfig +from models.nlvr_encoder import BertModel +from models.vit import interpolate_pos_embed +from models.blip import create_vit, init_tokenizer, is_url + +from timm.models.hub import download_cached_file + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_NLVR(nn.Module): + def __init__(self, + med_config = './configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + self.cls_head = nn.Sequential( + nn.Linear(self.text_encoder.config.hidden_size, self.text_encoder.config.hidden_size), + nn.ReLU(), + nn.Linear(self.text_encoder.config.hidden_size, 2) + ) + + def forward(self, image, text, targets, train=True): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image0_embeds, image1_embeds = torch.split(image_embeds,targets.size(0)) + + text = self.tokenizer(text, padding='longest', return_tensors="pt").to(image.device) + text.input_ids[:,0] = self.tokenizer.enc_token_id + + output = self.text_encoder(text.input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = [image0_embeds,image1_embeds], + encoder_attention_mask = [image_atts[:image0_embeds.size(0)], + image_atts[image0_embeds.size(0):]], + return_dict = True, + ) + hidden_state = output.last_hidden_state[:,0,:] + prediction = self.cls_head(hidden_state) + + if train: + loss = F.cross_entropy(prediction, targets) + return loss + else: + return prediction + +def blip_nlvr(pretrained='',**kwargs): + model = BLIP_NLVR(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +def load_checkpoint(model,url_or_filename): + if is_url(url_or_filename): + cached_file = download_cached_file(url_or_filename, check_hash=False, progress=True) + checkpoint = torch.load(cached_file, map_location='cpu') + elif os.path.isfile(url_or_filename): + checkpoint = torch.load(url_or_filename, map_location='cpu') + else: + raise RuntimeError('checkpoint url or path is invalid') + state_dict = checkpoint['model'] + + state_dict['visual_encoder.pos_embed'] = interpolate_pos_embed(state_dict['visual_encoder.pos_embed'],model.visual_encoder) + + for key in list(state_dict.keys()): + if 'crossattention.self.' in key: + new_key0 = key.replace('self','self0') + new_key1 = key.replace('self','self1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + elif 'crossattention.output.dense.' in key: + new_key0 = key.replace('dense','dense0') + new_key1 = key.replace('dense','dense1') + state_dict[new_key0] = state_dict[key] + state_dict[new_key1] = state_dict[key] + + msg = model.load_state_dict(state_dict,strict=False) + print('load checkpoint from %s'%url_or_filename) + return model,msg + \ No newline at end of file diff --git a/models/blip_pretrain.py b/models/blip_pretrain.py new file mode 100644 index 0000000..9d0db2e --- /dev/null +++ b/models/blip_pretrain.py @@ -0,0 +1,339 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +from models.med import BertConfig, BertModel, BertLMHeadModel +from transformers import BertTokenizer +import transformers +transformers.logging.set_verbosity_error() + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Pretrain(nn.Module): + def __init__(self, + med_config = './configs/bert_config.json', + image_size = 224, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer, 0) + + if vit=='base': + checkpoint = torch.hub.load_state_dict_from_url( + url="https://dl.fbaipublicfiles.com/deit/deit_base_patch16_224-b5f2ef4d.pth", + map_location="cpu", check_hash=True) + state_dict = checkpoint["model"] + msg = self.visual_encoder.load_state_dict(state_dict,strict=False) + elif vit=='large': + from timm.models.helpers import load_custom_pretrained + from timm.models.vision_transformer import default_cfgs + load_custom_pretrained(self.visual_encoder,default_cfgs['vit_large_patch16_224_in21k']) + + self.tokenizer = init_tokenizer() + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel.from_pretrained('bert-base-uncased',config=encoder_config, add_pooling_layer=False) + self.text_encoder.resize_token_embeddings(len(self.tokenizer)) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=encoder_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("queue_ptr", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + # create the decoder + decoder_config = BertConfig.from_json_file(med_config) + decoder_config.encoder_width = vision_width + self.text_decoder = BertLMHeadModel.from_pretrained('bert-base-uncased',config=decoder_config) + self.text_decoder.resize_token_embeddings(len(self.tokenizer)) + tie_encoder_decoder_weights(self.text_decoder.bert,self.text_encoder,'','/attention') + + + def forward(self, image, caption, alpha): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=30, + return_tensors="pt").to(image.device) + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_all / self.temp + + sim_targets = torch.zeros(sim_i2t_m.size()).to(image.device) + sim_targets.fill_diagonal_(1) + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_all / self.temp + sim_t2i = text_feat @ image_feat_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + self._dequeue_and_enqueue(image_feat_m, text_feat_m) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + with torch.no_grad(): + weights_t2i = F.softmax(sim_t2i[:,:bs],dim=1)+1e-4 + weights_t2i.fill_diagonal_(0) + weights_i2t = F.softmax(sim_i2t[:,:bs],dim=1)+1e-4 + weights_i2t.fill_diagonal_(0) + + # select a negative image for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + ##================= LM ========================## + decoder_input_ids = text.input_ids.clone() + decoder_input_ids[:,0] = self.tokenizer.bos_token_id + decoder_targets = decoder_input_ids.masked_fill(decoder_input_ids == self.tokenizer.pad_token_id, -100) + + decoder_output = self.text_decoder(decoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + labels = decoder_targets, + return_dict = True, + ) + + loss_lm = decoder_output.loss + return loss_ita, loss_itm, loss_lm + + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + batch_size = image_feats.shape[0] + + ptr = int(self.queue_ptr) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.queue_ptr[0] = ptr + + +def blip_pretrain(**kwargs): + model = BLIP_Pretrain(**kwargs) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +from typing import List +def tie_encoder_decoder_weights(encoder: nn.Module, decoder: nn.Module, base_model_prefix: str, skip_key:str): + uninitialized_encoder_weights: List[str] = [] + if decoder.__class__ != encoder.__class__: + logger.info( + f"{decoder.__class__} and {encoder.__class__} are not equal. In this case make sure that all encoder weights are correctly initialized." + ) + + def tie_encoder_to_decoder_recursively( + decoder_pointer: nn.Module, + encoder_pointer: nn.Module, + module_name: str, + uninitialized_encoder_weights: List[str], + skip_key: str, + depth=0, + ): + assert isinstance(decoder_pointer, nn.Module) and isinstance( + encoder_pointer, nn.Module + ), f"{decoder_pointer} and {encoder_pointer} have to be of type torch.nn.Module" + if hasattr(decoder_pointer, "weight") and skip_key not in module_name: + assert hasattr(encoder_pointer, "weight") + encoder_pointer.weight = decoder_pointer.weight + if hasattr(decoder_pointer, "bias"): + assert hasattr(encoder_pointer, "bias") + encoder_pointer.bias = decoder_pointer.bias + print(module_name+' is tied') + return + + encoder_modules = encoder_pointer._modules + decoder_modules = decoder_pointer._modules + if len(decoder_modules) > 0: + assert ( + len(encoder_modules) > 0 + ), f"Encoder module {encoder_pointer} does not match decoder module {decoder_pointer}" + + all_encoder_weights = set([module_name + "/" + sub_name for sub_name in encoder_modules.keys()]) + encoder_layer_pos = 0 + for name, module in decoder_modules.items(): + if name.isdigit(): + encoder_name = str(int(name) + encoder_layer_pos) + decoder_name = name + if not isinstance(decoder_modules[decoder_name], type(encoder_modules[encoder_name])) and len( + encoder_modules + ) != len(decoder_modules): + # this can happen if the name corresponds to the position in a list module list of layers + # in this case the decoder has added a cross-attention that the encoder does not have + # thus skip this step and subtract one layer pos from encoder + encoder_layer_pos -= 1 + continue + elif name not in encoder_modules: + continue + elif depth > 500: + raise ValueError( + "Max depth of recursive function `tie_encoder_to_decoder` reached. It seems that there is a circular dependency between two or more `nn.Modules` of your model." + ) + else: + decoder_name = encoder_name = name + tie_encoder_to_decoder_recursively( + decoder_modules[decoder_name], + encoder_modules[encoder_name], + module_name + "/" + name, + uninitialized_encoder_weights, + skip_key, + depth=depth + 1, + ) + all_encoder_weights.remove(module_name + "/" + encoder_name) + + uninitialized_encoder_weights += list(all_encoder_weights) + + # tie weights recursively + tie_encoder_to_decoder_recursively(decoder, encoder, base_model_prefix, uninitialized_encoder_weights, skip_key) diff --git a/models/blip_retrieval.py b/models/blip_retrieval.py new file mode 100644 index 0000000..2294db6 --- /dev/null +++ b/models/blip_retrieval.py @@ -0,0 +1,322 @@ +from models.med import BertConfig, BertModel +from transformers import BertTokenizer + +import torch +from torch import nn +import torch.nn.functional as F + +from models.blip import create_vit, init_tokenizer, load_checkpoint + +class BLIP_Retrieval(nn.Module): + def __init__(self, + med_config = './configs/med_config.json', + image_size = 384, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + embed_dim = 256, + queue_size = 57600, + momentum = 0.995, + negative_all_rank = False, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit,image_size, vit_grad_ckpt, vit_ckpt_layer) + self.tokenizer = init_tokenizer() + med_config = BertConfig.from_json_file(med_config) + med_config.encoder_width = vision_width + self.text_encoder = BertModel(config=med_config, add_pooling_layer=False) + + text_width = self.text_encoder.config.hidden_size + + self.vision_proj = nn.Linear(vision_width, embed_dim) + self.text_proj = nn.Linear(text_width, embed_dim) + + self.itm_head = nn.Linear(text_width, 2) + + # create momentum encoders + self.visual_encoder_m, vision_width = create_vit(vit,image_size) + self.vision_proj_m = nn.Linear(vision_width, embed_dim) + self.text_encoder_m = BertModel(config=med_config, add_pooling_layer=False) + self.text_proj_m = nn.Linear(text_width, embed_dim) + + self.model_pairs = [[self.visual_encoder,self.visual_encoder_m], + [self.vision_proj,self.vision_proj_m], + [self.text_encoder,self.text_encoder_m], + [self.text_proj,self.text_proj_m], + ] + self.copy_params() + + # create the queue + self.register_buffer("image_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("text_queue", torch.randn(embed_dim, queue_size)) + self.register_buffer("idx_queue", torch.full((1,queue_size),-100)) + self.register_buffer("ptr_queue", torch.zeros(1, dtype=torch.long)) + + self.image_queue = nn.functional.normalize(self.image_queue, dim=0) + self.text_queue = nn.functional.normalize(self.text_queue, dim=0) + + self.queue_size = queue_size + self.momentum = momentum + self.temp = nn.Parameter(0.07*torch.ones([])) + + self.negative_all_rank = negative_all_rank + + + def forward(self, image, caption, alpha, idx): + with torch.no_grad(): + self.temp.clamp_(0.001,0.5) + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + image_feat = F.normalize(self.vision_proj(image_embeds[:,0,:]),dim=-1) + + text = self.tokenizer(caption, padding='max_length', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + + text_output = self.text_encoder(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat = F.normalize(self.text_proj(text_output.last_hidden_state[:,0,:]),dim=-1) + + ###============== Image-text Contrastive Learning ===================### + idx = idx.view(-1,1) + idx_all = torch.cat([idx.t(), self.idx_queue.clone().detach()],dim=1) + pos_idx = torch.eq(idx, idx_all).float() + sim_targets = pos_idx / pos_idx.sum(1,keepdim=True) + + # get momentum features + with torch.no_grad(): + self._momentum_update() + image_embeds_m = self.visual_encoder_m(image) + image_feat_m = F.normalize(self.vision_proj_m(image_embeds_m[:,0,:]),dim=-1) + image_feat_m_all = torch.cat([image_feat_m.t(),self.image_queue.clone().detach()],dim=1) + + text_output_m = self.text_encoder_m(text.input_ids, attention_mask = text.attention_mask, + return_dict = True, mode = 'text') + text_feat_m = F.normalize(self.text_proj_m(text_output_m.last_hidden_state[:,0,:]),dim=-1) + text_feat_m_all = torch.cat([text_feat_m.t(),self.text_queue.clone().detach()],dim=1) + + sim_i2t_m = image_feat_m @ text_feat_m_all / self.temp + sim_t2i_m = text_feat_m @ image_feat_m_all / self.temp + + sim_targets = torch.zeros(sim_i2t_m.size()).to(image.device) + sim_targets.fill_diagonal_(1) + + sim_i2t_targets = alpha * F.softmax(sim_i2t_m, dim=1) + (1 - alpha) * sim_targets + sim_t2i_targets = alpha * F.softmax(sim_t2i_m, dim=1) + (1 - alpha) * sim_targets + + sim_i2t = image_feat @ text_feat_m_all / self.temp + sim_t2i = text_feat @ image_feat_m_all / self.temp + + loss_i2t = -torch.sum(F.log_softmax(sim_i2t, dim=1)*sim_i2t_targets,dim=1).mean() + loss_t2i = -torch.sum(F.log_softmax(sim_t2i, dim=1)*sim_t2i_targets,dim=1).mean() + + loss_ita = (loss_i2t+loss_t2i)/2 + + idxs = concat_all_gather(idx) + self._dequeue_and_enqueue(image_feat_m, text_feat_m, idxs) + + ###============== Image-text Matching ===================### + encoder_input_ids = text.input_ids.clone() + encoder_input_ids[:,0] = self.tokenizer.enc_token_id + + # forward the positve image-text pair + bs = image.size(0) + output_pos = self.text_encoder(encoder_input_ids, + attention_mask = text.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True, + ) + + + if self.negative_all_rank: + # compute sample similarity + with torch.no_grad(): + mask = torch.eq(idx, idxs.t()) + + image_feat_world = concat_all_gather(image_feat) + text_feat_world = concat_all_gather(text_feat) + + sim_i2t = image_feat @ text_feat_world.t() / self.temp + sim_t2i = text_feat @ image_feat_world.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + image_embeds_world = all_gather_with_grad(image_embeds) + + # select a negative image (from all ranks) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds_world[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from all ranks) for each image + input_ids_world = concat_all_gather(encoder_input_ids) + att_mask_world = concat_all_gather(text.attention_mask) + + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(input_ids_world[neg_idx]) + text_atts_neg.append(att_mask_world[neg_idx]) + + else: + with torch.no_grad(): + mask = torch.eq(idx, idx.t()) + + sim_i2t = image_feat @ text_feat.t() / self.temp + sim_t2i = text_feat @ image_feat.t() / self.temp + + weights_i2t = F.softmax(sim_i2t,dim=1) + weights_i2t.masked_fill_(mask, 0) + + weights_t2i = F.softmax(sim_t2i,dim=1) + weights_t2i.masked_fill_(mask, 0) + + # select a negative image (from same rank) for each text + image_embeds_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_t2i[b], 1).item() + image_embeds_neg.append(image_embeds[neg_idx]) + image_embeds_neg = torch.stack(image_embeds_neg,dim=0) + + # select a negative text (from same rank) for each image + text_ids_neg = [] + text_atts_neg = [] + for b in range(bs): + neg_idx = torch.multinomial(weights_i2t[b], 1).item() + text_ids_neg.append(encoder_input_ids[neg_idx]) + text_atts_neg.append(text.attention_mask[neg_idx]) + + text_ids_neg = torch.stack(text_ids_neg,dim=0) + text_atts_neg = torch.stack(text_atts_neg,dim=0) + + text_ids_all = torch.cat([encoder_input_ids, text_ids_neg],dim=0) + text_atts_all = torch.cat([text.attention_mask, text_atts_neg],dim=0) + + image_embeds_all = torch.cat([image_embeds_neg,image_embeds],dim=0) + image_atts_all = torch.cat([image_atts,image_atts],dim=0) + + output_neg = self.text_encoder(text_ids_all, + attention_mask = text_atts_all, + encoder_hidden_states = image_embeds_all, + encoder_attention_mask = image_atts_all, + return_dict = True, + ) + + + vl_embeddings = torch.cat([output_pos.last_hidden_state[:,0,:], output_neg.last_hidden_state[:,0,:]],dim=0) + vl_output = self.itm_head(vl_embeddings) + + itm_labels = torch.cat([torch.ones(bs,dtype=torch.long),torch.zeros(2*bs,dtype=torch.long)], + dim=0).to(image.device) + loss_itm = F.cross_entropy(vl_output, itm_labels) + + return loss_ita, loss_itm + + + @torch.no_grad() + def copy_params(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data.copy_(param.data) # initialize + param_m.requires_grad = False # not update by gradient + + + @torch.no_grad() + def _momentum_update(self): + for model_pair in self.model_pairs: + for param, param_m in zip(model_pair[0].parameters(), model_pair[1].parameters()): + param_m.data = param_m.data * self.momentum + param.data * (1. - self.momentum) + + + @torch.no_grad() + def _dequeue_and_enqueue(self, image_feat, text_feat, idxs): + # gather keys before updating queue + image_feats = concat_all_gather(image_feat) + text_feats = concat_all_gather(text_feat) + + + batch_size = image_feats.shape[0] + + ptr = int(self.ptr_queue) + assert self.queue_size % batch_size == 0 # for simplicity + + # replace the keys at ptr (dequeue and enqueue) + self.image_queue[:, ptr:ptr + batch_size] = image_feats.T + self.text_queue[:, ptr:ptr + batch_size] = text_feats.T + self.idx_queue[:, ptr:ptr + batch_size] = idxs.T + ptr = (ptr + batch_size) % self.queue_size # move pointer + + self.ptr_queue[0] = ptr + + +def blip_retrieval(pretrained='',**kwargs): + model = BLIP_Retrieval(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) + print("missing keys:") + print(msg.missing_keys) + return model + + +@torch.no_grad() +def concat_all_gather(tensor): + """ + Performs all_gather operation on the provided tensors. + *** Warning ***: torch.distributed.all_gather has no gradient. + """ + tensors_gather = [torch.ones_like(tensor) + for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(tensors_gather, tensor, async_op=False) + + output = torch.cat(tensors_gather, dim=0) + return output + + +class GatherLayer(torch.autograd.Function): + """ + Gather tensors from all workers with support for backward propagation: + This implementation does not cut the gradients as torch.distributed.all_gather does. + """ + + @staticmethod + def forward(ctx, x): + output = [torch.zeros_like(x) for _ in range(torch.distributed.get_world_size())] + torch.distributed.all_gather(output, x) + return tuple(output) + + @staticmethod + def backward(ctx, *grads): + all_gradients = torch.stack(grads) + torch.distributed.all_reduce(all_gradients) + return all_gradients[torch.distributed.get_rank()] + + +def all_gather_with_grad(tensors): + """ + Performs all_gather operation on the provided tensors. + Graph remains connected for backward grad computation. + """ + # Queue the gathered tensors + world_size = torch.distributed.get_world_size() + # There is no need for reduction in the single-proc case + if world_size == 1: + return tensors + + tensor_all = GatherLayer.apply(tensors) + + return torch.cat(tensor_all, dim=0) diff --git a/models/blip_vqa.py b/models/blip_vqa.py new file mode 100644 index 0000000..9f284b4 --- /dev/null +++ b/models/blip_vqa.py @@ -0,0 +1,186 @@ +from models.med import BertConfig, BertModel, BertLMHeadModel +from models.blip import create_vit, init_tokenizer, load_checkpoint + +import torch +from torch import nn +import torch.nn.functional as F +from transformers import BertTokenizer +import numpy as np + +class BLIP_VQA(nn.Module): + def __init__(self, + med_config = './configs/med_config.json', + image_size = 480, + vit = 'base', + vit_grad_ckpt = False, + vit_ckpt_layer = 0, + ): + """ + Args: + med_config (str): path for the mixture of encoder-decoder model's configuration file + image_size (int): input image size + vit (str): model size of vision transformer + """ + super().__init__() + + self.visual_encoder, vision_width = create_vit(vit, image_size, vit_grad_ckpt, vit_ckpt_layer, drop_path_rate=0.1) + self.tokenizer = init_tokenizer() + + encoder_config = BertConfig.from_json_file(med_config) + encoder_config.encoder_width = vision_width + self.text_encoder = BertModel(config=encoder_config, add_pooling_layer=False) + + decoder_config = BertConfig.from_json_file(med_config) + self.text_decoder = BertLMHeadModel(config=decoder_config) + + + def forward(self, image, question, answer=None, n=None, weights=None, train=True, inference='rank', k_test=128): + + image_embeds = self.visual_encoder(image) + image_atts = torch.ones(image_embeds.size()[:-1],dtype=torch.long).to(image.device) + + question = self.tokenizer(question, padding='longest', truncation=True, max_length=35, + return_tensors="pt").to(image.device) + question.input_ids[:,0] = self.tokenizer.enc_token_id + + if train: + ''' + n: number of answers for each question + weights: weight for each answer + ''' + answer = self.tokenizer(answer, padding='longest', return_tensors="pt").to(image.device) + answer.input_ids[:,0] = self.tokenizer.bos_token_id + answer_targets = answer.input_ids.masked_fill(answer.input_ids == self.tokenizer.pad_token_id, -100) + + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + question_states = [] + question_atts = [] + for b, n in enumerate(n): + question_states += [question_output.last_hidden_state[b]]*n + question_atts += [question.attention_mask[b]]*n + question_states = torch.stack(question_states,0) + question_atts = torch.stack(question_atts,0) + + answer_output = self.text_decoder(answer.input_ids, + attention_mask = answer.attention_mask, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = answer_targets, + return_dict = True, + reduction = 'none', + ) + + loss = weights * answer_output.loss + loss = loss.sum()/image.size(0) + + return loss + + + else: + question_output = self.text_encoder(question.input_ids, + attention_mask = question.attention_mask, + encoder_hidden_states = image_embeds, + encoder_attention_mask = image_atts, + return_dict = True) + + if inference=='generate': + num_beams = 3 + question_states = question_output.last_hidden_state.repeat_interleave(num_beams,dim=0) + question_atts = torch.ones(question_states.size()[:-1],dtype=torch.long).to(question_states.device) + model_kwargs = {"encoder_hidden_states": question_states, "encoder_attention_mask":question_atts} + + bos_ids = torch.full((image.size(0),1),fill_value=self.tokenizer.bos_token_id,device=image.device) + + outputs = self.text_decoder.generate(input_ids=bos_ids, + max_length=10, + min_length=1, + num_beams=num_beams, + eos_token_id=self.tokenizer.sep_token_id, + pad_token_id=self.tokenizer.pad_token_id, + **model_kwargs) + + answers = [] + for output in outputs: + answer = self.tokenizer.decode(output, skip_special_tokens=True) + answers.append(answer) + return answers + + elif inference=='rank': + max_ids = self.rank_answer(question_output.last_hidden_state, question.attention_mask, + answer.input_ids, answer.attention_mask, k_test) + return max_ids + + + + def rank_answer(self, question_states, question_atts, answer_ids, answer_atts, k): + + num_ques = question_states.size(0) + start_ids = answer_ids[0,0].repeat(num_ques,1) # bos token + + start_output = self.text_decoder(start_ids, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + return_dict = True, + reduction = 'none') + logits = start_output.logits[:,0,:] # first token's logit + + # topk_probs: top-k probability + # topk_ids: [num_question, k] + answer_first_token = answer_ids[:,1] + prob_first_token = F.softmax(logits,dim=1).index_select(dim=1, index=answer_first_token) + topk_probs, topk_ids = prob_first_token.topk(k,dim=1) + + # answer input: [num_question*k, answer_len] + input_ids = [] + input_atts = [] + for b, topk_id in enumerate(topk_ids): + input_ids.append(answer_ids.index_select(dim=0, index=topk_id)) + input_atts.append(answer_atts.index_select(dim=0, index=topk_id)) + input_ids = torch.cat(input_ids,dim=0) + input_atts = torch.cat(input_atts,dim=0) + + targets_ids = input_ids.masked_fill(input_ids == self.tokenizer.pad_token_id, -100) + + # repeat encoder's output for top-k answers + question_states = tile(question_states, 0, k) + question_atts = tile(question_atts, 0, k) + + output = self.text_decoder(input_ids, + attention_mask = input_atts, + encoder_hidden_states = question_states, + encoder_attention_mask = question_atts, + labels = targets_ids, + return_dict = True, + reduction = 'none') + + log_probs_sum = -output.loss + log_probs_sum = log_probs_sum.view(num_ques,k) + + max_topk_ids = log_probs_sum.argmax(dim=1) + max_ids = topk_ids[max_topk_ids>=0,max_topk_ids] + + return max_ids + + +def blip_vqa(pretrained='',**kwargs): + model = BLIP_VQA(**kwargs) + if pretrained: + model,msg = load_checkpoint(model,pretrained) +# assert(len(msg.missing_keys)==0) + return model + + +def tile(x, dim, n_tile): + init_dim = x.size(dim) + repeat_idx = [1] * x.dim() + repeat_idx[dim] = n_tile + x = x.repeat(*(repeat_idx)) + order_index = torch.LongTensor(np.concatenate([init_dim * np.arange(n_tile) + i for i in range(init_dim)])) + return torch.index_select(x, dim, order_index.to(x.device)) + + \ No newline at end of file diff --git a/models/med.py b/models/med.py new file mode 100644 index 0000000..7b00a35 --- /dev/null +++ b/models/med.py @@ -0,0 +1,955 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on huggingface code base + * https://github.com/huggingface/transformers/blob/v4.15.0/src/transformers/models/bert +''' + +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False): + super().__init__() + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + + + +class BertLMHeadModel(BertPreTrainedModel): + + _keys_to_ignore_on_load_unexpected = [r"pooler"] + _keys_to_ignore_on_load_missing = [r"position_ids", r"predictions.decoder.bias"] + + def __init__(self, config): + super().__init__(config) + + self.bert = BertModel(config, add_pooling_layer=False) + self.cls = BertOnlyMLMHead(config) + + self.init_weights() + + def get_output_embeddings(self): + return self.cls.predictions.decoder + + def set_output_embeddings(self, new_embeddings): + self.cls.predictions.decoder = new_embeddings + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + labels=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + return_logits=False, + is_decoder=True, + reduction='mean', + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + labels (:obj:`torch.LongTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Labels for computing the left-to-right language modeling loss (next word prediction). Indices should be in + ``[-100, 0, ..., config.vocab_size]`` (see ``input_ids`` docstring) Tokens with indices set to ``-100`` are + ignored (masked), the loss is only computed for the tokens with labels n ``[0, ..., config.vocab_size]`` + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + Returns: + Example:: + >>> from transformers import BertTokenizer, BertLMHeadModel, BertConfig + >>> import torch + >>> tokenizer = BertTokenizer.from_pretrained('bert-base-cased') + >>> config = BertConfig.from_pretrained("bert-base-cased") + >>> model = BertLMHeadModel.from_pretrained('bert-base-cased', config=config) + >>> inputs = tokenizer("Hello, my dog is cute", return_tensors="pt") + >>> outputs = model(**inputs) + >>> prediction_logits = outputs.logits + """ + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + if labels is not None: + use_cache = False + + outputs = self.bert( + input_ids, + attention_mask=attention_mask, + position_ids=position_ids, + head_mask=head_mask, + inputs_embeds=inputs_embeds, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + is_decoder=is_decoder, + mode=mode, + ) + + sequence_output = outputs[0] + prediction_scores = self.cls(sequence_output) + + if return_logits: + return prediction_scores[:, :-1, :].contiguous() + + lm_loss = None + if labels is not None: + # we are doing next-token prediction; shift prediction scores and input ids by one + shifted_prediction_scores = prediction_scores[:, :-1, :].contiguous() + labels = labels[:, 1:].contiguous() + loss_fct = CrossEntropyLoss(reduction=reduction, label_smoothing=0.1) + lm_loss = loss_fct(shifted_prediction_scores.view(-1, self.config.vocab_size), labels.view(-1)) + if reduction=='none': + lm_loss = lm_loss.view(prediction_scores.size(0),-1).sum(1) + + if not return_dict: + output = (prediction_scores,) + outputs[2:] + return ((lm_loss,) + output) if lm_loss is not None else output + + return CausalLMOutputWithCrossAttentions( + loss=lm_loss, + logits=prediction_scores, + past_key_values=outputs.past_key_values, + hidden_states=outputs.hidden_states, + attentions=outputs.attentions, + cross_attentions=outputs.cross_attentions, + ) + + def prepare_inputs_for_generation(self, input_ids, past=None, attention_mask=None, **model_kwargs): + input_shape = input_ids.shape + # if model is used as a decoder in encoder-decoder model, the decoder attention mask is created on the fly + if attention_mask is None: + attention_mask = input_ids.new_ones(input_shape) + + # cut decoder_input_ids if past is used + if past is not None: + input_ids = input_ids[:, -1:] + + return { + "input_ids": input_ids, + "attention_mask": attention_mask, + "past_key_values": past, + "encoder_hidden_states": model_kwargs.get("encoder_hidden_states", None), + "encoder_attention_mask": model_kwargs.get("encoder_attention_mask", None), + "is_decoder": True, + } + + def _reorder_cache(self, past, beam_idx): + reordered_past = () + for layer_past in past: + reordered_past += (tuple(past_state.index_select(0, beam_idx) for past_state in layer_past),) + return reordered_past diff --git a/models/nlvr_encoder.py b/models/nlvr_encoder.py new file mode 100644 index 0000000..1946bb4 --- /dev/null +++ b/models/nlvr_encoder.py @@ -0,0 +1,843 @@ +import math +import os +import warnings +from dataclasses import dataclass +from typing import Optional, Tuple + +import torch +from torch import Tensor, device, dtype, nn +import torch.utils.checkpoint +from torch import nn +from torch.nn import CrossEntropyLoss +import torch.nn.functional as F + +from transformers.activations import ACT2FN +from transformers.file_utils import ( + ModelOutput, +) +from transformers.modeling_outputs import ( + BaseModelOutputWithPastAndCrossAttentions, + BaseModelOutputWithPoolingAndCrossAttentions, + CausalLMOutputWithCrossAttentions, + MaskedLMOutput, + MultipleChoiceModelOutput, + NextSentencePredictorOutput, + QuestionAnsweringModelOutput, + SequenceClassifierOutput, + TokenClassifierOutput, +) +from transformers.modeling_utils import ( + PreTrainedModel, + apply_chunking_to_forward, + find_pruneable_heads_and_indices, + prune_linear_layer, +) +from transformers.utils import logging +from transformers.models.bert.configuration_bert import BertConfig + + +logger = logging.get_logger(__name__) + + +class BertEmbeddings(nn.Module): + """Construct the embeddings from word and position embeddings.""" + + def __init__(self, config): + super().__init__() + self.word_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id) + self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size) + + # self.LayerNorm is not snake-cased to stick with TensorFlow model variable name and be able to load + # any TensorFlow checkpoint file + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + # position_ids (1, len position emb) is contiguous in memory and exported when serialized + self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1))) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + + self.config = config + + def forward( + self, input_ids=None, position_ids=None, inputs_embeds=None, past_key_values_length=0 + ): + if input_ids is not None: + input_shape = input_ids.size() + else: + input_shape = inputs_embeds.size()[:-1] + + seq_length = input_shape[1] + + if position_ids is None: + position_ids = self.position_ids[:, past_key_values_length : seq_length + past_key_values_length] + + if inputs_embeds is None: + inputs_embeds = self.word_embeddings(input_ids) + + embeddings = inputs_embeds + + if self.position_embedding_type == "absolute": + position_embeddings = self.position_embeddings(position_ids) + embeddings += position_embeddings + embeddings = self.LayerNorm(embeddings) + embeddings = self.dropout(embeddings) + return embeddings + + +class BertSelfAttention(nn.Module): + def __init__(self, config, is_cross_attention): + super().__init__() + self.config = config + if config.hidden_size % config.num_attention_heads != 0 and not hasattr(config, "embedding_size"): + raise ValueError( + "The hidden size (%d) is not a multiple of the number of attention " + "heads (%d)" % (config.hidden_size, config.num_attention_heads) + ) + + self.num_attention_heads = config.num_attention_heads + self.attention_head_size = int(config.hidden_size / config.num_attention_heads) + self.all_head_size = self.num_attention_heads * self.attention_head_size + + self.query = nn.Linear(config.hidden_size, self.all_head_size) + if is_cross_attention: + self.key = nn.Linear(config.encoder_width, self.all_head_size) + self.value = nn.Linear(config.encoder_width, self.all_head_size) + else: + self.key = nn.Linear(config.hidden_size, self.all_head_size) + self.value = nn.Linear(config.hidden_size, self.all_head_size) + + self.dropout = nn.Dropout(config.attention_probs_dropout_prob) + self.position_embedding_type = getattr(config, "position_embedding_type", "absolute") + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + self.max_position_embeddings = config.max_position_embeddings + self.distance_embedding = nn.Embedding(2 * config.max_position_embeddings - 1, self.attention_head_size) + self.save_attention = False + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def transpose_for_scores(self, x): + new_x_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size) + x = x.view(*new_x_shape) + return x.permute(0, 2, 1, 3) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + mixed_query_layer = self.query(hidden_states) + + # If this is instantiated as a cross-attention module, the keys + # and values come from an encoder; the attention mask needs to be + # such that the encoder's padding tokens are not attended to. + is_cross_attention = encoder_hidden_states is not None + + if is_cross_attention: + key_layer = self.transpose_for_scores(self.key(encoder_hidden_states)) + value_layer = self.transpose_for_scores(self.value(encoder_hidden_states)) + attention_mask = encoder_attention_mask + elif past_key_value is not None: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + key_layer = torch.cat([past_key_value[0], key_layer], dim=2) + value_layer = torch.cat([past_key_value[1], value_layer], dim=2) + else: + key_layer = self.transpose_for_scores(self.key(hidden_states)) + value_layer = self.transpose_for_scores(self.value(hidden_states)) + + query_layer = self.transpose_for_scores(mixed_query_layer) + + past_key_value = (key_layer, value_layer) + + # Take the dot product between "query" and "key" to get the raw attention scores. + attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2)) + + if self.position_embedding_type == "relative_key" or self.position_embedding_type == "relative_key_query": + seq_length = hidden_states.size()[1] + position_ids_l = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(-1, 1) + position_ids_r = torch.arange(seq_length, dtype=torch.long, device=hidden_states.device).view(1, -1) + distance = position_ids_l - position_ids_r + positional_embedding = self.distance_embedding(distance + self.max_position_embeddings - 1) + positional_embedding = positional_embedding.to(dtype=query_layer.dtype) # fp16 compatibility + + if self.position_embedding_type == "relative_key": + relative_position_scores = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores + elif self.position_embedding_type == "relative_key_query": + relative_position_scores_query = torch.einsum("bhld,lrd->bhlr", query_layer, positional_embedding) + relative_position_scores_key = torch.einsum("bhrd,lrd->bhlr", key_layer, positional_embedding) + attention_scores = attention_scores + relative_position_scores_query + relative_position_scores_key + + attention_scores = attention_scores / math.sqrt(self.attention_head_size) + if attention_mask is not None: + # Apply the attention mask is (precomputed for all layers in BertModel forward() function) + attention_scores = attention_scores + attention_mask + + # Normalize the attention scores to probabilities. + attention_probs = nn.Softmax(dim=-1)(attention_scores) + + if is_cross_attention and self.save_attention: + self.save_attention_map(attention_probs) + attention_probs.register_hook(self.save_attn_gradients) + + # This is actually dropping out entire tokens to attend to, which might + # seem a bit unusual, but is taken from the original Transformer paper. + attention_probs_dropped = self.dropout(attention_probs) + + # Mask heads if we want to + if head_mask is not None: + attention_probs_dropped = attention_probs_dropped * head_mask + + context_layer = torch.matmul(attention_probs_dropped, value_layer) + + context_layer = context_layer.permute(0, 2, 1, 3).contiguous() + new_context_layer_shape = context_layer.size()[:-2] + (self.all_head_size,) + context_layer = context_layer.view(*new_context_layer_shape) + + outputs = (context_layer, attention_probs) if output_attentions else (context_layer,) + + outputs = outputs + (past_key_value,) + return outputs + + +class BertSelfOutput(nn.Module): + def __init__(self, config, twin=False, merge=False): + super().__init__() + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + if twin: + self.dense0 = nn.Linear(config.hidden_size, config.hidden_size) + self.dense1 = nn.Linear(config.hidden_size, config.hidden_size) + else: + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if merge: + self.act = ACT2FN[config.hidden_act] + self.merge_layer = nn.Linear(config.hidden_size * 2, config.hidden_size) + self.merge = True + else: + self.merge = False + + def forward(self, hidden_states, input_tensor): + if type(hidden_states) == list: + hidden_states0 = self.dense0(hidden_states[0]) + hidden_states1 = self.dense1(hidden_states[1]) + if self.merge: + #hidden_states = self.merge_layer(self.act(torch.cat([hidden_states0,hidden_states1],dim=-1))) + hidden_states = self.merge_layer(torch.cat([hidden_states0,hidden_states1],dim=-1)) + else: + hidden_states = (hidden_states0+hidden_states1)/2 + else: + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertAttention(nn.Module): + def __init__(self, config, is_cross_attention=False, layer_num=-1): + super().__init__() + if is_cross_attention: + self.self0 = BertSelfAttention(config, is_cross_attention) + self.self1 = BertSelfAttention(config, is_cross_attention) + else: + self.self = BertSelfAttention(config, is_cross_attention) + self.output = BertSelfOutput(config, twin=is_cross_attention, merge=(is_cross_attention and layer_num>=6)) + self.pruned_heads = set() + + def prune_heads(self, heads): + if len(heads) == 0: + return + heads, index = find_pruneable_heads_and_indices( + heads, self.self.num_attention_heads, self.self.attention_head_size, self.pruned_heads + ) + + # Prune linear layers + self.self.query = prune_linear_layer(self.self.query, index) + self.self.key = prune_linear_layer(self.self.key, index) + self.self.value = prune_linear_layer(self.self.value, index) + self.output.dense = prune_linear_layer(self.output.dense, index, dim=1) + + # Update hyper params and store pruned heads + self.self.num_attention_heads = self.self.num_attention_heads - len(heads) + self.self.all_head_size = self.self.attention_head_size * self.self.num_attention_heads + self.pruned_heads = self.pruned_heads.union(heads) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + ): + if type(encoder_hidden_states)==list: + self_outputs0 = self.self0( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[0], + encoder_attention_mask[0], + past_key_value, + output_attentions, + ) + self_outputs1 = self.self1( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states[1], + encoder_attention_mask[1], + past_key_value, + output_attentions, + ) + attention_output = self.output([self_outputs0[0],self_outputs1[0]], hidden_states) + + outputs = (attention_output,) + self_outputs0[1:] # add attentions if we output them + else: + self_outputs = self.self( + hidden_states, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + ) + attention_output = self.output(self_outputs[0], hidden_states) + outputs = (attention_output,) + self_outputs[1:] # add attentions if we output them + return outputs + + +class BertIntermediate(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.intermediate_size) + if isinstance(config.hidden_act, str): + self.intermediate_act_fn = ACT2FN[config.hidden_act] + else: + self.intermediate_act_fn = config.hidden_act + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.intermediate_act_fn(hidden_states) + return hidden_states + + +class BertOutput(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.intermediate_size, config.hidden_size) + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + self.dropout = nn.Dropout(config.hidden_dropout_prob) + + def forward(self, hidden_states, input_tensor): + hidden_states = self.dense(hidden_states) + hidden_states = self.dropout(hidden_states) + hidden_states = self.LayerNorm(hidden_states + input_tensor) + return hidden_states + + +class BertLayer(nn.Module): + def __init__(self, config, layer_num): + super().__init__() + self.config = config + self.chunk_size_feed_forward = config.chunk_size_feed_forward + self.seq_len_dim = 1 + self.attention = BertAttention(config) + self.layer_num = layer_num + if self.config.add_cross_attention: + self.crossattention = BertAttention(config, is_cross_attention=self.config.add_cross_attention, layer_num=layer_num) + self.intermediate = BertIntermediate(config) + self.output = BertOutput(config) + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_value=None, + output_attentions=False, + mode=None, + ): + # decoder uni-directional self-attention cached key/values tuple is at positions 1,2 + self_attn_past_key_value = past_key_value[:2] if past_key_value is not None else None + self_attention_outputs = self.attention( + hidden_states, + attention_mask, + head_mask, + output_attentions=output_attentions, + past_key_value=self_attn_past_key_value, + ) + attention_output = self_attention_outputs[0] + + outputs = self_attention_outputs[1:-1] + present_key_value = self_attention_outputs[-1] + + if mode=='multimodal': + assert encoder_hidden_states is not None, "encoder_hidden_states must be given for cross-attention layers" + cross_attention_outputs = self.crossattention( + attention_output, + attention_mask, + head_mask, + encoder_hidden_states, + encoder_attention_mask, + output_attentions=output_attentions, + ) + attention_output = cross_attention_outputs[0] + outputs = outputs + cross_attention_outputs[1:-1] # add cross attentions if we output attention weights + layer_output = apply_chunking_to_forward( + self.feed_forward_chunk, self.chunk_size_feed_forward, self.seq_len_dim, attention_output + ) + outputs = (layer_output,) + outputs + + outputs = outputs + (present_key_value,) + + return outputs + + def feed_forward_chunk(self, attention_output): + intermediate_output = self.intermediate(attention_output) + layer_output = self.output(intermediate_output, attention_output) + return layer_output + + +class BertEncoder(nn.Module): + def __init__(self, config): + super().__init__() + self.config = config + self.layer = nn.ModuleList([BertLayer(config,i) for i in range(config.num_hidden_layers)]) + self.gradient_checkpointing = False + + def forward( + self, + hidden_states, + attention_mask=None, + head_mask=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=False, + output_hidden_states=False, + return_dict=True, + mode='multimodal', + ): + all_hidden_states = () if output_hidden_states else None + all_self_attentions = () if output_attentions else None + all_cross_attentions = () if output_attentions and self.config.add_cross_attention else None + + next_decoder_cache = () if use_cache else None + + for i in range(self.config.num_hidden_layers): + layer_module = self.layer[i] + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + layer_head_mask = head_mask[i] if head_mask is not None else None + past_key_value = past_key_values[i] if past_key_values is not None else None + + if self.gradient_checkpointing and self.training: + + if use_cache: + logger.warn( + "`use_cache=True` is incompatible with gradient checkpointing. Setting `use_cache=False`..." + ) + use_cache = False + + def create_custom_forward(module): + def custom_forward(*inputs): + return module(*inputs, past_key_value, output_attentions) + + return custom_forward + + layer_outputs = torch.utils.checkpoint.checkpoint( + create_custom_forward(layer_module), + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + mode=mode, + ) + else: + layer_outputs = layer_module( + hidden_states, + attention_mask, + layer_head_mask, + encoder_hidden_states, + encoder_attention_mask, + past_key_value, + output_attentions, + mode=mode, + ) + + hidden_states = layer_outputs[0] + if use_cache: + next_decoder_cache += (layer_outputs[-1],) + if output_attentions: + all_self_attentions = all_self_attentions + (layer_outputs[1],) + + if output_hidden_states: + all_hidden_states = all_hidden_states + (hidden_states,) + + if not return_dict: + return tuple( + v + for v in [ + hidden_states, + next_decoder_cache, + all_hidden_states, + all_self_attentions, + all_cross_attentions, + ] + if v is not None + ) + return BaseModelOutputWithPastAndCrossAttentions( + last_hidden_state=hidden_states, + past_key_values=next_decoder_cache, + hidden_states=all_hidden_states, + attentions=all_self_attentions, + cross_attentions=all_cross_attentions, + ) + + +class BertPooler(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + self.activation = nn.Tanh() + + def forward(self, hidden_states): + # We "pool" the model by simply taking the hidden state corresponding + # to the first token. + first_token_tensor = hidden_states[:, 0] + pooled_output = self.dense(first_token_tensor) + pooled_output = self.activation(pooled_output) + return pooled_output + + +class BertPredictionHeadTransform(nn.Module): + def __init__(self, config): + super().__init__() + self.dense = nn.Linear(config.hidden_size, config.hidden_size) + if isinstance(config.hidden_act, str): + self.transform_act_fn = ACT2FN[config.hidden_act] + else: + self.transform_act_fn = config.hidden_act + self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps) + + def forward(self, hidden_states): + hidden_states = self.dense(hidden_states) + hidden_states = self.transform_act_fn(hidden_states) + hidden_states = self.LayerNorm(hidden_states) + return hidden_states + + +class BertLMPredictionHead(nn.Module): + def __init__(self, config): + super().__init__() + self.transform = BertPredictionHeadTransform(config) + + # The output weights are the same as the input embeddings, but there is + # an output-only bias for each token. + self.decoder = nn.Linear(config.hidden_size, config.vocab_size, bias=False) + + self.bias = nn.Parameter(torch.zeros(config.vocab_size)) + + # Need a link between the two variables so that the bias is correctly resized with `resize_token_embeddings` + self.decoder.bias = self.bias + + def forward(self, hidden_states): + hidden_states = self.transform(hidden_states) + hidden_states = self.decoder(hidden_states) + return hidden_states + + +class BertOnlyMLMHead(nn.Module): + def __init__(self, config): + super().__init__() + self.predictions = BertLMPredictionHead(config) + + def forward(self, sequence_output): + prediction_scores = self.predictions(sequence_output) + return prediction_scores + + +class BertPreTrainedModel(PreTrainedModel): + """ + An abstract class to handle weights initialization and a simple interface for downloading and loading pretrained + models. + """ + + config_class = BertConfig + base_model_prefix = "bert" + _keys_to_ignore_on_load_missing = [r"position_ids"] + + def _init_weights(self, module): + """ Initialize the weights """ + if isinstance(module, (nn.Linear, nn.Embedding)): + # Slightly different from the TF version which uses truncated_normal for initialization + # cf https://github.com/pytorch/pytorch/pull/5617 + module.weight.data.normal_(mean=0.0, std=self.config.initializer_range) + elif isinstance(module, nn.LayerNorm): + module.bias.data.zero_() + module.weight.data.fill_(1.0) + if isinstance(module, nn.Linear) and module.bias is not None: + module.bias.data.zero_() + + +class BertModel(BertPreTrainedModel): + """ + The model can behave as an encoder (with only self-attention) as well as a decoder, in which case a layer of + cross-attention is added between the self-attention layers, following the architecture described in `Attention is + all you need `__ by Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, + Llion Jones, Aidan N. Gomez, Lukasz Kaiser and Illia Polosukhin. + argument and :obj:`add_cross_attention` set to :obj:`True`; an :obj:`encoder_hidden_states` is then expected as an + input to the forward pass. + """ + + def __init__(self, config, add_pooling_layer=True): + super().__init__(config) + self.config = config + + self.embeddings = BertEmbeddings(config) + + self.encoder = BertEncoder(config) + + self.pooler = BertPooler(config) if add_pooling_layer else None + + self.init_weights() + + + def get_input_embeddings(self): + return self.embeddings.word_embeddings + + def set_input_embeddings(self, value): + self.embeddings.word_embeddings = value + + def _prune_heads(self, heads_to_prune): + """ + Prunes heads of the model. heads_to_prune: dict of {layer_num: list of heads to prune in this layer} See base + class PreTrainedModel + """ + for layer, heads in heads_to_prune.items(): + self.encoder.layer[layer].attention.prune_heads(heads) + + + def get_extended_attention_mask(self, attention_mask: Tensor, input_shape: Tuple[int], device: device, is_decoder: bool) -> Tensor: + """ + Makes broadcastable attention and causal masks so that future and masked tokens are ignored. + + Arguments: + attention_mask (:obj:`torch.Tensor`): + Mask with ones indicating tokens to attend to, zeros for tokens to ignore. + input_shape (:obj:`Tuple[int]`): + The shape of the input to the model. + device: (:obj:`torch.device`): + The device of the input to the model. + + Returns: + :obj:`torch.Tensor` The extended attention mask, with a the same dtype as :obj:`attention_mask.dtype`. + """ + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + if attention_mask.dim() == 3: + extended_attention_mask = attention_mask[:, None, :, :] + elif attention_mask.dim() == 2: + # Provided a padding mask of dimensions [batch_size, seq_length] + # - if the model is a decoder, apply a causal mask in addition to the padding mask + # - if the model is an encoder, make the mask broadcastable to [batch_size, num_heads, seq_length, seq_length] + if is_decoder: + batch_size, seq_length = input_shape + + seq_ids = torch.arange(seq_length, device=device) + causal_mask = seq_ids[None, None, :].repeat(batch_size, seq_length, 1) <= seq_ids[None, :, None] + # in case past_key_values are used we need to add a prefix ones mask to the causal mask + # causal and attention masks must have same type with pytorch version < 1.3 + causal_mask = causal_mask.to(attention_mask.dtype) + + if causal_mask.shape[1] < attention_mask.shape[1]: + prefix_seq_len = attention_mask.shape[1] - causal_mask.shape[1] + causal_mask = torch.cat( + [ + torch.ones((batch_size, seq_length, prefix_seq_len), device=device, dtype=causal_mask.dtype), + causal_mask, + ], + axis=-1, + ) + + extended_attention_mask = causal_mask[:, None, :, :] * attention_mask[:, None, None, :] + else: + extended_attention_mask = attention_mask[:, None, None, :] + else: + raise ValueError( + "Wrong shape for input_ids (shape {}) or attention_mask (shape {})".format( + input_shape, attention_mask.shape + ) + ) + + # Since attention_mask is 1.0 for positions we want to attend and 0.0 for + # masked positions, this operation will create a tensor which is 0.0 for + # positions we want to attend and -10000.0 for masked positions. + # Since we are adding it to the raw scores before the softmax, this is + # effectively the same as removing these entirely. + extended_attention_mask = extended_attention_mask.to(dtype=self.dtype) # fp16 compatibility + extended_attention_mask = (1.0 - extended_attention_mask) * -10000.0 + return extended_attention_mask + + def forward( + self, + input_ids=None, + attention_mask=None, + position_ids=None, + head_mask=None, + inputs_embeds=None, + encoder_embeds=None, + encoder_hidden_states=None, + encoder_attention_mask=None, + past_key_values=None, + use_cache=None, + output_attentions=None, + output_hidden_states=None, + return_dict=None, + is_decoder=False, + mode='multimodal', + ): + r""" + encoder_hidden_states (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length, hidden_size)`, `optional`): + Sequence of hidden-states at the output of the last layer of the encoder. Used in the cross-attention if + the model is configured as a decoder. + encoder_attention_mask (:obj:`torch.FloatTensor` of shape :obj:`(batch_size, sequence_length)`, `optional`): + Mask to avoid performing attention on the padding token indices of the encoder input. This mask is used in + the cross-attention if the model is configured as a decoder. Mask values selected in ``[0, 1]``: + - 1 for tokens that are **not masked**, + - 0 for tokens that are **masked**. + past_key_values (:obj:`tuple(tuple(torch.FloatTensor))` of length :obj:`config.n_layers` with each tuple having 4 tensors of shape :obj:`(batch_size, num_heads, sequence_length - 1, embed_size_per_head)`): + Contains precomputed key and value hidden states of the attention blocks. Can be used to speed up decoding. + If :obj:`past_key_values` are used, the user can optionally input only the last :obj:`decoder_input_ids` + (those that don't have their past key value states given to this model) of shape :obj:`(batch_size, 1)` + instead of all :obj:`decoder_input_ids` of shape :obj:`(batch_size, sequence_length)`. + use_cache (:obj:`bool`, `optional`): + If set to :obj:`True`, :obj:`past_key_values` key value states are returned and can be used to speed up + decoding (see :obj:`past_key_values`). + """ + output_attentions = output_attentions if output_attentions is not None else self.config.output_attentions + output_hidden_states = ( + output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states + ) + return_dict = return_dict if return_dict is not None else self.config.use_return_dict + + if is_decoder: + use_cache = use_cache if use_cache is not None else self.config.use_cache + else: + use_cache = False + + if input_ids is not None and inputs_embeds is not None: + raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time") + elif input_ids is not None: + input_shape = input_ids.size() + batch_size, seq_length = input_shape + device = input_ids.device + elif inputs_embeds is not None: + input_shape = inputs_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = inputs_embeds.device + elif encoder_embeds is not None: + input_shape = encoder_embeds.size()[:-1] + batch_size, seq_length = input_shape + device = encoder_embeds.device + else: + raise ValueError("You have to specify either input_ids or inputs_embeds or encoder_embeds") + + # past_key_values_length + past_key_values_length = past_key_values[0][0].shape[2] if past_key_values is not None else 0 + + if attention_mask is None: + attention_mask = torch.ones(((batch_size, seq_length + past_key_values_length)), device=device) + + # We can provide a self-attention mask of dimensions [batch_size, from_seq_length, to_seq_length] + # ourselves in which case we just need to make it broadcastable to all heads. + extended_attention_mask: torch.Tensor = self.get_extended_attention_mask(attention_mask, input_shape, + device, is_decoder) + + # If a 2D or 3D attention mask is provided for the cross-attention + # we need to make broadcastable to [batch_size, num_heads, seq_length, seq_length] + if encoder_hidden_states is not None: + if type(encoder_hidden_states) == list: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states[0].size() + else: + encoder_batch_size, encoder_sequence_length, _ = encoder_hidden_states.size() + encoder_hidden_shape = (encoder_batch_size, encoder_sequence_length) + + if type(encoder_attention_mask) == list: + encoder_extended_attention_mask = [self.invert_attention_mask(mask) for mask in encoder_attention_mask] + elif encoder_attention_mask is None: + encoder_attention_mask = torch.ones(encoder_hidden_shape, device=device) + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = self.invert_attention_mask(encoder_attention_mask) + else: + encoder_extended_attention_mask = None + + # Prepare head mask if needed + # 1.0 in head_mask indicate we keep the head + # attention_probs has shape bsz x n_heads x N x N + # input head_mask has shape [num_heads] or [num_hidden_layers x num_heads] + # and head_mask is converted to shape [num_hidden_layers x batch x num_heads x seq_length x seq_length] + head_mask = self.get_head_mask(head_mask, self.config.num_hidden_layers) + + if encoder_embeds is None: + embedding_output = self.embeddings( + input_ids=input_ids, + position_ids=position_ids, + inputs_embeds=inputs_embeds, + past_key_values_length=past_key_values_length, + ) + else: + embedding_output = encoder_embeds + + encoder_outputs = self.encoder( + embedding_output, + attention_mask=extended_attention_mask, + head_mask=head_mask, + encoder_hidden_states=encoder_hidden_states, + encoder_attention_mask=encoder_extended_attention_mask, + past_key_values=past_key_values, + use_cache=use_cache, + output_attentions=output_attentions, + output_hidden_states=output_hidden_states, + return_dict=return_dict, + mode=mode, + ) + sequence_output = encoder_outputs[0] + pooled_output = self.pooler(sequence_output) if self.pooler is not None else None + + if not return_dict: + return (sequence_output, pooled_output) + encoder_outputs[1:] + + return BaseModelOutputWithPoolingAndCrossAttentions( + last_hidden_state=sequence_output, + pooler_output=pooled_output, + past_key_values=encoder_outputs.past_key_values, + hidden_states=encoder_outputs.hidden_states, + attentions=encoder_outputs.attentions, + cross_attentions=encoder_outputs.cross_attentions, + ) + diff --git a/models/vit.py b/models/vit.py new file mode 100644 index 0000000..cec3d8e --- /dev/null +++ b/models/vit.py @@ -0,0 +1,305 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li + * Based on timm code base + * https://github.com/rwightman/pytorch-image-models/tree/master/timm +''' + +import torch +import torch.nn as nn +import torch.nn.functional as F +from functools import partial + +from timm.models.vision_transformer import _cfg, PatchEmbed +from timm.models.registry import register_model +from timm.models.layers import trunc_normal_, DropPath +from timm.models.helpers import named_apply, adapt_input_conv + +from fairscale.nn.checkpoint.checkpoint_activations import checkpoint_wrapper + +class Mlp(nn.Module): + """ MLP as used in Vision Transformer, MLP-Mixer and related networks + """ + def __init__(self, in_features, hidden_features=None, out_features=None, act_layer=nn.GELU, drop=0.): + super().__init__() + out_features = out_features or in_features + hidden_features = hidden_features or in_features + self.fc1 = nn.Linear(in_features, hidden_features) + self.act = act_layer() + self.fc2 = nn.Linear(hidden_features, out_features) + self.drop = nn.Dropout(drop) + + def forward(self, x): + x = self.fc1(x) + x = self.act(x) + x = self.drop(x) + x = self.fc2(x) + x = self.drop(x) + return x + + +class Attention(nn.Module): + def __init__(self, dim, num_heads=8, qkv_bias=False, qk_scale=None, attn_drop=0., proj_drop=0.): + super().__init__() + self.num_heads = num_heads + head_dim = dim // num_heads + # NOTE scale factor was wrong in my original version, can set manually to be compat with prev weights + self.scale = qk_scale or 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) + self.proj_drop = nn.Dropout(proj_drop) + self.attn_gradients = None + self.attention_map = None + + def save_attn_gradients(self, attn_gradients): + self.attn_gradients = attn_gradients + + def get_attn_gradients(self): + return self.attn_gradients + + def save_attention_map(self, attention_map): + self.attention_map = attention_map + + def get_attention_map(self): + return self.attention_map + + def forward(self, x, register_hook=False): + B, N, C = x.shape + qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4) + q, k, v = qkv[0], qkv[1], qkv[2] # make torchscript happy (cannot use tensor as tuple) + + attn = (q @ k.transpose(-2, -1)) * self.scale + attn = attn.softmax(dim=-1) + attn = self.attn_drop(attn) + + if register_hook: + self.save_attention_map(attn) + attn.register_hook(self.save_attn_gradients) + + x = (attn @ v).transpose(1, 2).reshape(B, N, C) + x = self.proj(x) + x = self.proj_drop(x) + return x + + +class Block(nn.Module): + + def __init__(self, dim, num_heads, mlp_ratio=4., qkv_bias=False, qk_scale=None, drop=0., attn_drop=0., + drop_path=0., act_layer=nn.GELU, norm_layer=nn.LayerNorm, use_grad_checkpointing=False): + super().__init__() + self.norm1 = norm_layer(dim) + self.attn = Attention( + dim, num_heads=num_heads, qkv_bias=qkv_bias, qk_scale=qk_scale, attn_drop=attn_drop, proj_drop=drop) + # NOTE: drop path for stochastic depth, we shall see if this is better than dropout here + self.drop_path = DropPath(drop_path) if drop_path > 0. else nn.Identity() + self.norm2 = norm_layer(dim) + mlp_hidden_dim = int(dim * mlp_ratio) + self.mlp = Mlp(in_features=dim, hidden_features=mlp_hidden_dim, act_layer=act_layer, drop=drop) + + if use_grad_checkpointing: + self.attn = checkpoint_wrapper(self.attn) + self.mlp = checkpoint_wrapper(self.mlp) + + def forward(self, x, register_hook=False): + x = x + self.drop_path(self.attn(self.norm1(x), register_hook=register_hook)) + x = x + self.drop_path(self.mlp(self.norm2(x))) + return x + + +class VisionTransformer(nn.Module): + """ Vision Transformer + A PyTorch impl of : `An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale` - + https://arxiv.org/abs/2010.11929 + """ + def __init__(self, img_size=224, patch_size=16, in_chans=3, num_classes=1000, embed_dim=768, depth=12, + num_heads=12, mlp_ratio=4., qkv_bias=True, qk_scale=None, representation_size=None, + drop_rate=0., attn_drop_rate=0., drop_path_rate=0., norm_layer=None, + use_grad_checkpointing=False, ckpt_layer=0): + """ + Args: + img_size (int, tuple): input image size + patch_size (int, tuple): patch size + in_chans (int): number of input channels + num_classes (int): number of classes for classification head + 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 + qk_scale (float): override default qk scale of head_dim ** -0.5 if set + representation_size (Optional[int]): enable and set representation layer (pre-logits) to this value if set + drop_rate (float): dropout rate + attn_drop_rate (float): attention dropout rate + drop_path_rate (float): stochastic depth rate + norm_layer: (nn.Module): normalization layer + """ + super().__init__() + self.num_features = self.embed_dim = embed_dim # num_features for consistency with other models + norm_layer = norm_layer or partial(nn.LayerNorm, eps=1e-6) + + self.patch_embed = PatchEmbed( + 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 + 1, embed_dim)) + self.pos_drop = nn.Dropout(p=drop_rate) + + dpr = [x.item() for x in torch.linspace(0, drop_path_rate, depth)] # stochastic depth decay rule + self.blocks = nn.ModuleList([ + Block( + dim=embed_dim, num_heads=num_heads, mlp_ratio=mlp_ratio, qkv_bias=qkv_bias, qk_scale=qk_scale, + drop=drop_rate, attn_drop=attn_drop_rate, drop_path=dpr[i], norm_layer=norm_layer, + use_grad_checkpointing=(use_grad_checkpointing and i>=depth-ckpt_layer) + ) + for i in range(depth)]) + self.norm = norm_layer(embed_dim) + + trunc_normal_(self.pos_embed, std=.02) + trunc_normal_(self.cls_token, std=.02) + self.apply(self._init_weights) + + def _init_weights(self, m): + if isinstance(m, nn.Linear): + trunc_normal_(m.weight, std=.02) + if isinstance(m, nn.Linear) and m.bias is not None: + nn.init.constant_(m.bias, 0) + elif isinstance(m, nn.LayerNorm): + nn.init.constant_(m.bias, 0) + nn.init.constant_(m.weight, 1.0) + + @torch.jit.ignore + def no_weight_decay(self): + return {'pos_embed', 'cls_token'} + + def forward(self, x, register_blk=-1): + B = x.shape[0] + x = self.patch_embed(x) + + cls_tokens = self.cls_token.expand(B, -1, -1) # stole cls_tokens impl from Phil Wang, thanks + x = torch.cat((cls_tokens, x), dim=1) + + x = x + self.pos_embed[:,:x.size(1),:] + x = self.pos_drop(x) + + for i,blk in enumerate(self.blocks): + x = blk(x, register_blk==i) + x = self.norm(x) + + return x + + @torch.jit.ignore() + def load_pretrained(self, checkpoint_path, prefix=''): + _load_weights(self, checkpoint_path, prefix) + + +@torch.no_grad() +def _load_weights(model: VisionTransformer, checkpoint_path: str, prefix: str = ''): + """ Load weights from .npz checkpoints for official Google Brain Flax implementation + """ + import numpy as np + + def _n2p(w, t=True): + if w.ndim == 4 and w.shape[0] == w.shape[1] == w.shape[2] == 1: + w = w.flatten() + if t: + if w.ndim == 4: + w = w.transpose([3, 2, 0, 1]) + elif w.ndim == 3: + w = w.transpose([2, 0, 1]) + elif w.ndim == 2: + w = w.transpose([1, 0]) + return torch.from_numpy(w) + + w = np.load(checkpoint_path) + if not prefix and 'opt/target/embedding/kernel' in w: + prefix = 'opt/target/' + + if hasattr(model.patch_embed, 'backbone'): + # hybrid + backbone = model.patch_embed.backbone + stem_only = not hasattr(backbone, 'stem') + stem = backbone if stem_only else backbone.stem + stem.conv.weight.copy_(adapt_input_conv(stem.conv.weight.shape[1], _n2p(w[f'{prefix}conv_root/kernel']))) + stem.norm.weight.copy_(_n2p(w[f'{prefix}gn_root/scale'])) + stem.norm.bias.copy_(_n2p(w[f'{prefix}gn_root/bias'])) + if not stem_only: + for i, stage in enumerate(backbone.stages): + for j, block in enumerate(stage.blocks): + bp = f'{prefix}block{i + 1}/unit{j + 1}/' + for r in range(3): + getattr(block, f'conv{r + 1}').weight.copy_(_n2p(w[f'{bp}conv{r + 1}/kernel'])) + getattr(block, f'norm{r + 1}').weight.copy_(_n2p(w[f'{bp}gn{r + 1}/scale'])) + getattr(block, f'norm{r + 1}').bias.copy_(_n2p(w[f'{bp}gn{r + 1}/bias'])) + if block.downsample is not None: + block.downsample.conv.weight.copy_(_n2p(w[f'{bp}conv_proj/kernel'])) + block.downsample.norm.weight.copy_(_n2p(w[f'{bp}gn_proj/scale'])) + block.downsample.norm.bias.copy_(_n2p(w[f'{bp}gn_proj/bias'])) + embed_conv_w = _n2p(w[f'{prefix}embedding/kernel']) + else: + embed_conv_w = adapt_input_conv( + model.patch_embed.proj.weight.shape[1], _n2p(w[f'{prefix}embedding/kernel'])) + model.patch_embed.proj.weight.copy_(embed_conv_w) + model.patch_embed.proj.bias.copy_(_n2p(w[f'{prefix}embedding/bias'])) + model.cls_token.copy_(_n2p(w[f'{prefix}cls'], t=False)) + pos_embed_w = _n2p(w[f'{prefix}Transformer/posembed_input/pos_embedding'], t=False) + if pos_embed_w.shape != model.pos_embed.shape: + pos_embed_w = resize_pos_embed( # resize pos embedding when different size from pretrained weights + pos_embed_w, model.pos_embed, getattr(model, 'num_tokens', 1), model.patch_embed.grid_size) + model.pos_embed.copy_(pos_embed_w) + model.norm.weight.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/scale'])) + model.norm.bias.copy_(_n2p(w[f'{prefix}Transformer/encoder_norm/bias'])) +# if isinstance(model.head, nn.Linear) and model.head.bias.shape[0] == w[f'{prefix}head/bias'].shape[-1]: +# model.head.weight.copy_(_n2p(w[f'{prefix}head/kernel'])) +# model.head.bias.copy_(_n2p(w[f'{prefix}head/bias'])) +# if isinstance(getattr(model.pre_logits, 'fc', None), nn.Linear) and f'{prefix}pre_logits/bias' in w: +# model.pre_logits.fc.weight.copy_(_n2p(w[f'{prefix}pre_logits/kernel'])) +# model.pre_logits.fc.bias.copy_(_n2p(w[f'{prefix}pre_logits/bias'])) + for i, block in enumerate(model.blocks.children()): + block_prefix = f'{prefix}Transformer/encoderblock_{i}/' + mha_prefix = block_prefix + 'MultiHeadDotProductAttention_1/' + block.norm1.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/scale'])) + block.norm1.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_0/bias'])) + block.attn.qkv.weight.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/kernel'], t=False).flatten(1).T for n in ('query', 'key', 'value')])) + block.attn.qkv.bias.copy_(torch.cat([ + _n2p(w[f'{mha_prefix}{n}/bias'], t=False).reshape(-1) for n in ('query', 'key', 'value')])) + block.attn.proj.weight.copy_(_n2p(w[f'{mha_prefix}out/kernel']).flatten(1)) + block.attn.proj.bias.copy_(_n2p(w[f'{mha_prefix}out/bias'])) + for r in range(2): + getattr(block.mlp, f'fc{r + 1}').weight.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/kernel'])) + getattr(block.mlp, f'fc{r + 1}').bias.copy_(_n2p(w[f'{block_prefix}MlpBlock_3/Dense_{r}/bias'])) + block.norm2.weight.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/scale'])) + block.norm2.bias.copy_(_n2p(w[f'{block_prefix}LayerNorm_2/bias'])) + + +def interpolate_pos_embed(pos_embed_checkpoint, visual_encoder): + # interpolate position embedding + embedding_size = pos_embed_checkpoint.shape[-1] + num_patches = visual_encoder.patch_embed.num_patches + num_extra_tokens = visual_encoder.pos_embed.shape[-2] - num_patches + # height (== width) for the checkpoint position embedding + orig_size = int((pos_embed_checkpoint.shape[-2] - num_extra_tokens) ** 0.5) + # height (== width) for the new position embedding + new_size = int(num_patches ** 0.5) + + if orig_size!=new_size: + # class_token and dist_token are kept unchanged + extra_tokens = pos_embed_checkpoint[:, :num_extra_tokens] + # only the position tokens are interpolated + pos_tokens = pos_embed_checkpoint[:, num_extra_tokens:] + pos_tokens = pos_tokens.reshape(-1, orig_size, orig_size, embedding_size).permute(0, 3, 1, 2) + pos_tokens = torch.nn.functional.interpolate( + pos_tokens, size=(new_size, new_size), mode='bicubic', align_corners=False) + pos_tokens = pos_tokens.permute(0, 2, 3, 1).flatten(1, 2) + new_pos_embed = torch.cat((extra_tokens, pos_tokens), dim=1) + print('reshape position embedding from %d to %d'%(orig_size ** 2,new_size ** 2)) + + return new_pos_embed + else: + return pos_embed_checkpoint \ No newline at end of file diff --git a/pretrain.py b/pretrain.py new file mode 100644 index 0000000..c9490ec --- /dev/null +++ b/pretrain.py @@ -0,0 +1,173 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_pretrain import blip_pretrain +import utils +from utils import warmup_lr_schedule, step_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + metric_logger.add_meter('loss_lm', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + if config['laion_path']: + data_loader.dataset.reload_laion(epoch) + + data_loader.sampler.set_epoch(epoch) + + for i, (image, caption) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + if epoch==0: + warmup_lr_schedule(optimizer, i, config['warmup_steps'], config['warmup_lr'], config['init_lr']) + + optimizer.zero_grad() + + image = image.to(device,non_blocking=True) + + # ramp up alpha in the first 2 epochs + alpha = config['alpha']*min(1,(epoch*len(data_loader)+i)/(2*len(data_loader))) + + loss_ita, loss_itm, loss_lm = model(image, caption, alpha = alpha) + loss = loss_ita + loss_itm + loss_lm + + loss.backward() + optimizer.step() + + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_lm=loss_lm.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = [create_dataset('pretrain', config, min_scale=0.2)] + print('number of training samples: %d'%len(datasets[0])) + + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True], num_tasks, global_rank) + + data_loader = create_loader(datasets,samplers,batch_size=[config['batch_size']], num_workers=[4], is_trains=[True], collate_fns=[None])[0] + + #### Model #### + print("Creating model") + model = blip_pretrain(image_size=config['image_size'], vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], + vit_ckpt_layer=config['vit_ckpt_layer'], queue_size=config['queue_size']) + + model = model.to(device) + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + start_epoch = 0 + if args.checkpoint: + checkpoint = torch.load(args.checkpoint, map_location='cpu') + state_dict = checkpoint['model'] + model.load_state_dict(state_dict) + + optimizer.load_state_dict(checkpoint['optimizer']) + start_epoch = checkpoint['epoch']+1 + print('resume checkpoint from %s'%args.checkpoint) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + print("Start training") + start_time = time.time() + for epoch in range(start_epoch, config['max_epoch']): + + step_lr_schedule(optimizer, epoch, config['init_lr'], config['min_lr'], config['lr_decay_rate']) + + train_stats = train(model, data_loader, optimizer, epoch, device, config) + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/pretrain.yaml') + parser.add_argument('--output_dir', default='output/Pretrain') + parser.add_argument('--checkpoint', default='') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..1fb1e5f --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +timm==0.4.12 +transformers==4.15.0 +fairscale==0.4.4 +pycocotools \ No newline at end of file diff --git a/train_caption.py b/train_caption.py new file mode 100644 index 0000000..7c639ac --- /dev/null +++ b/train_caption.py @@ -0,0 +1,206 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip import blip_decoder +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.utils import save_result, coco_caption_eval + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Caption Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i, (image, caption, _) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device) + + loss = model(image, caption) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # evaluate + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Caption generation:' + print_freq = 10 + + result = [] + for image, image_id in metric_logger.log_every(data_loader, print_freq, header): + + image = image.to(device) + + captions = model.generate(image, sample=False, num_beams=config['num_beams'], max_length=config['max_length'], + min_length=config['min_length']) + + for caption, img_id in zip(captions, image_id): + result.append({"image_id": img_id.item(), "caption": caption}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating captioning dataset") + train_dataset, val_dataset, test_dataset = create_dataset('caption_coco', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset,val_dataset,test_dataset], [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size']]*3,num_workers=[4,4,4], + is_trains=[True, False, False], collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_decoder(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + prompt=config['prompt']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + val_result = evaluate(model_without_ddp, val_loader, device, config) + val_result_file = save_result(val_result, args.result_dir, 'val_epoch%d'%epoch, remove_duplicate='image_id') + + test_result = evaluate(model_without_ddp, test_loader, device, config) + test_result_file = save_result(test_result, args.result_dir, 'test_epoch%d'%epoch, remove_duplicate='image_id') + + if utils.is_main_process(): + coco_val = coco_caption_eval(config['coco_gt_root'],val_result_file,'val') + coco_test = coco_caption_eval(config['coco_gt_root'],test_result_file,'test') + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + + if coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] > best: + best = coco_val.eval['CIDEr'] + coco_val.eval['Bleu_4'] + best_epoch = epoch + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in coco_val.eval.items()}, + **{f'test_{k}': v for k, v in coco_test.eval.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + dist.barrier() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/caption_coco.yaml') + parser.add_argument('--output_dir', default='output/Caption_coco') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/train_nlvr.py b/train_nlvr.py new file mode 100644 index 0000000..84b247b --- /dev/null +++ b/train_nlvr.py @@ -0,0 +1,213 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path +import json +import pickle + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_nlvr import blip_nlvr + +import utils +from utils import cosine_lr_schedule, warmup_lr_schedule +from data import create_dataset, create_sampler, create_loader + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=50, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=50, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + step_size = 10 + + for i,(image0, image1, text, targets) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + loss = model(images, text, targets=targets, train=True) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + metric_logger.update(loss=loss.item()) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluate(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + + header = 'Evaluation:' + print_freq = 50 + + for image0, image1, text, targets in metric_logger.log_every(data_loader, print_freq, header): + images = torch.cat([image0, image1], dim=0) + images, targets = images.to(device), targets.to(device) + + prediction = model(images, text, targets=targets, train=False) + + _, pred_class = prediction.max(1) + accuracy = (targets==pred_class).sum() / targets.size(0) + + metric_logger.meters['acc'].update(accuracy.item(), n=image0.size(0)) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.4f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating dataset") + datasets = create_dataset('nlvr', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True,False,False], num_tasks, global_rank) + else: + samplers = [None, None, None] + + batch_size=[config['batch_size_train'],config['batch_size_test'],config['batch_size_test']] + train_loader, val_loader, test_loader = create_loader(datasets,samplers,batch_size=batch_size, + num_workers=[4,4,4],is_trains=[True,False,False], + collate_fns=[None,None,None]) + + #### Model #### + print("Creating model") + model = blip_nlvr(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + print("Start training") + start_time = time.time() + best = 0 + best_epoch = 0 + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + val_stats = evaluate(model, val_loader, device, config) + test_stats = evaluate(model, test_loader, device, config) + + if utils.is_main_process(): + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_stats.items()}, + **{f'test_{k}': v for k, v in test_stats.items()}, + 'epoch': epoch, + } + + if float(val_stats['acc'])>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = float(val_stats['acc']) + best_epoch = epoch + + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + if args.evaluate: + break + + dist.barrier() + + if utils.is_main_process(): + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write("best epoch: %d"%best_epoch) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/nlvr.yaml') + parser.add_argument('--output_dir', default='output/NLVR') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/train_retrieval.py b/train_retrieval.py new file mode 100644 index 0000000..574f033 --- /dev/null +++ b/train_retrieval.py @@ -0,0 +1,345 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +import torch.backends.cudnn as cudnn +import torch.distributed as dist +from torch.utils.data import DataLoader + +from models.blip_retrieval import blip_retrieval +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader + + +def train(model, data_loader, optimizer, epoch, device, config): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss_itm', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + metric_logger.add_meter('loss_ita', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, caption, idx) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + idx = idx.to(device,non_blocking=True) + + if epoch>0: + alpha = config['alpha'] + else: + alpha = config['alpha']*min(1,i/len(data_loader)) + + loss_ita, loss_itm = model(image, caption, alpha=alpha, idx=idx) + loss = loss_ita + loss_itm + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss_itm=loss_itm.item()) + metric_logger.update(loss_ita=loss_ita.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config): + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Evaluation:' + + print('Computing features for evaluation...') + start_time = time.time() + + texts = data_loader.dataset.text + num_text = len(texts) + text_bs = 256 + text_ids = [] + text_embeds = [] + text_atts = [] + for i in range(0, num_text, text_bs): + text = texts[i: min(num_text, i+text_bs)] + text_input = model.tokenizer(text, padding='max_length', truncation=True, max_length=35, return_tensors="pt").to(device) + text_output = model.text_encoder(text_input.input_ids, attention_mask = text_input.attention_mask, mode='text') + text_embed = F.normalize(model.text_proj(text_output.last_hidden_state[:,0,:])) + text_embeds.append(text_embed) + text_ids.append(text_input.input_ids) + text_atts.append(text_input.attention_mask) + + text_embeds = torch.cat(text_embeds,dim=0) + text_ids = torch.cat(text_ids,dim=0) + text_atts = torch.cat(text_atts,dim=0) + text_ids[:,0] = model.tokenizer.enc_token_id + + image_feats = [] + image_embeds = [] + for image, img_id in data_loader: + image = image.to(device) + image_feat = model.visual_encoder(image) + image_embed = model.vision_proj(image_feat[:,0,:]) + image_embed = F.normalize(image_embed,dim=-1) + + image_feats.append(image_feat.cpu()) + image_embeds.append(image_embed) + + image_feats = torch.cat(image_feats,dim=0) + image_embeds = torch.cat(image_embeds,dim=0) + + sims_matrix = image_embeds @ text_embeds.t() + score_matrix_i2t = torch.full((len(data_loader.dataset.image),len(texts)),-100.0).to(device) + + num_tasks = utils.get_world_size() + rank = utils.get_rank() + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + + encoder_output = image_feats[start+i].repeat(config['k_test'],1,1).to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[topk_idx], + attention_mask = text_atts[topk_idx], + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_i2t[start+i,topk_idx] = score + topk_sim + + sims_matrix = sims_matrix.t() + score_matrix_t2i = torch.full((len(texts),len(data_loader.dataset.image)),-100.0).to(device) + + step = sims_matrix.size(0)//num_tasks + 1 + start = rank*step + end = min(sims_matrix.size(0),start+step) + + for i,sims in enumerate(metric_logger.log_every(sims_matrix[start:end], 50, header)): + + topk_sim, topk_idx = sims.topk(k=config['k_test'], dim=0) + encoder_output = image_feats[topk_idx].to(device) + encoder_att = torch.ones(encoder_output.size()[:-1],dtype=torch.long).to(device) + output = model.text_encoder(text_ids[start+i].repeat(config['k_test'],1), + attention_mask = text_atts[start+i].repeat(config['k_test'],1), + encoder_hidden_states = encoder_output, + encoder_attention_mask = encoder_att, + return_dict = True, + ) + score = model.itm_head(output.last_hidden_state[:,0,:])[:,1] + score_matrix_t2i[start+i,topk_idx] = score + topk_sim + + if args.distributed: + dist.barrier() + torch.distributed.all_reduce(score_matrix_i2t, op=torch.distributed.ReduceOp.SUM) + torch.distributed.all_reduce(score_matrix_t2i, op=torch.distributed.ReduceOp.SUM) + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Evaluation time {}'.format(total_time_str)) + + return score_matrix_i2t.cpu().numpy(), score_matrix_t2i.cpu().numpy() + + + +@torch.no_grad() +def itm_eval(scores_i2t, scores_t2i, txt2img, img2txt): + + #Images->Text + ranks = np.zeros(scores_i2t.shape[0]) + for index,score in enumerate(scores_i2t): + inds = np.argsort(score)[::-1] + # Score + rank = 1e20 + for i in img2txt[index]: + tmp = np.where(inds == i)[0][0] + if tmp < rank: + rank = tmp + ranks[index] = rank + + # Compute metrics + tr1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + tr5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + tr10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + #Text->Images + ranks = np.zeros(scores_t2i.shape[0]) + + for index,score in enumerate(scores_t2i): + inds = np.argsort(score)[::-1] + ranks[index] = np.where(inds == txt2img[index])[0][0] + + # Compute metrics + ir1 = 100.0 * len(np.where(ranks < 1)[0]) / len(ranks) + ir5 = 100.0 * len(np.where(ranks < 5)[0]) / len(ranks) + ir10 = 100.0 * len(np.where(ranks < 10)[0]) / len(ranks) + + tr_mean = (tr1 + tr5 + tr10) / 3 + ir_mean = (ir1 + ir5 + ir10) / 3 + r_mean = (tr_mean + ir_mean) / 2 + + eval_result = {'txt_r1': tr1, + 'txt_r5': tr5, + 'txt_r10': tr10, + 'txt_r_mean': tr_mean, + 'img_r1': ir1, + 'img_r5': ir5, + 'img_r10': ir10, + 'img_r_mean': ir_mean, + 'r_mean': r_mean} + return eval_result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating retrieval dataset") + train_dataset, val_dataset, test_dataset = create_dataset('retrieval_%s'%config['dataset'], config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler([train_dataset], [True], num_tasks, global_rank) + [None, None] + else: + samplers = [None, None, None] + + train_loader, val_loader, test_loader = create_loader([train_dataset, val_dataset, test_dataset],samplers, + batch_size=[config['batch_size_train']]+[config['batch_size_test']]*2, + num_workers=[4,4,4], + is_trains=[True, False, False], + collate_fns=[None,None,None]) + + + #### Model #### + print("Creating model") + model = blip_retrieval(pretrained=config['pretrained'], image_size=config['image_size'], vit=config['vit'], + vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer'], + queue_size=config['queue_size'], negative_all_rank=config['negative_all_rank']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device, config) + + score_val_i2t, score_val_t2i, = evaluation(model_without_ddp, val_loader, device, config) + score_test_i2t, score_test_t2i = evaluation(model_without_ddp, test_loader, device, config) + + if utils.is_main_process(): + + val_result = itm_eval(score_val_i2t, score_val_t2i, val_loader.dataset.txt2img, val_loader.dataset.img2txt) + print(val_result) + + if val_result['r_mean']>best: + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_best.pth')) + best = val_result['r_mean'] + best_epoch = epoch + + test_result = itm_eval(score_test_i2t, score_test_t2i, test_loader.dataset.txt2img, test_loader.dataset.img2txt) + print(test_result) + + if args.evaluate: + log_stats = {**{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + } + with open(os.path.join(args.output_dir, "evaluate.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + else: + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + **{f'val_{k}': v for k, v in val_result.items()}, + **{f'test_{k}': v for k, v in test_result.items()}, + 'epoch': epoch, + 'best_epoch': best_epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + if args.evaluate: + break + + dist.barrier() + torch.cuda.empty_cache() + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/retrieval_flickr.yaml') + parser.add_argument('--output_dir', default='output/Retrieval_flickr') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/train_vqa.py b/train_vqa.py new file mode 100644 index 0000000..89eb749 --- /dev/null +++ b/train_vqa.py @@ -0,0 +1,202 @@ +''' + * Copyright (c) 2022, salesforce.com, inc. + * All rights reserved. + * SPDX-License-Identifier: BSD-3-Clause + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + * By Junnan Li +''' +import argparse +import os +import ruamel_yaml as yaml +import numpy as np +import random +import time +import datetime +import json +from pathlib import Path + +import torch +import torch.nn as nn +import torch.nn.functional as F +from torch.utils.data import DataLoader +import torch.backends.cudnn as cudnn +import torch.distributed as dist + +from models.blip_vqa import blip_vqa +import utils +from utils import cosine_lr_schedule +from data import create_dataset, create_sampler, create_loader +from data.vqa_dataset import vqa_collate_fn +from data.utils import save_result + + +def train(model, data_loader, optimizer, epoch, device): + # train + model.train() + + metric_logger = utils.MetricLogger(delimiter=" ") + metric_logger.add_meter('lr', utils.SmoothedValue(window_size=1, fmt='{value:.6f}')) + metric_logger.add_meter('loss', utils.SmoothedValue(window_size=1, fmt='{value:.4f}')) + + header = 'Train Epoch: [{}]'.format(epoch) + print_freq = 50 + + for i,(image, question, answer, weights, n) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image, weights = image.to(device,non_blocking=True), weights.to(device,non_blocking=True) + + loss = model(image, question, answer, train=True, n=n, weights=weights) + + optimizer.zero_grad() + loss.backward() + optimizer.step() + + metric_logger.update(loss=loss.item()) + metric_logger.update(lr=optimizer.param_groups[0]["lr"]) + + # gather the stats from all processes + metric_logger.synchronize_between_processes() + print("Averaged stats:", metric_logger.global_avg()) + return {k: "{:.3f}".format(meter.global_avg) for k, meter in metric_logger.meters.items()} + + +@torch.no_grad() +def evaluation(model, data_loader, device, config) : + # test + model.eval() + + metric_logger = utils.MetricLogger(delimiter=" ") + header = 'Generate VQA test result:' + print_freq = 50 + + result = [] + + if config['inference']=='rank': + answer_list = data_loader.dataset.answer_list + answer_candidates = model.tokenizer(answer_list, padding='longest', return_tensors='pt').to(device) + answer_candidates.input_ids[:,0] = model.tokenizer.bos_token_id + + for n, (image, question, question_id) in enumerate(metric_logger.log_every(data_loader, print_freq, header)): + image = image.to(device,non_blocking=True) + + if config['inference']=='generate': + answers = model(image, question, train=False, inference='generate') + + for answer, ques_id in zip(answers, question_id): + ques_id = int(ques_id.item()) + result.append({"question_id":ques_id, "answer":answer}) + + elif config['inference']=='rank': + answer_ids = model(image, question, answer_candidates, train=False, inference='rank', k_test=config['k_test']) + + for ques_id, answer_id in zip(question_id, answer_ids): + result.append({"question_id":int(ques_id.item()), "answer":answer_list[answer_id]}) + + return result + + +def main(args, config): + utils.init_distributed_mode(args) + + device = torch.device(args.device) + + # fix the seed for reproducibility + seed = args.seed + utils.get_rank() + torch.manual_seed(seed) + np.random.seed(seed) + random.seed(seed) + cudnn.benchmark = True + + #### Dataset #### + print("Creating vqa datasets") + datasets = create_dataset('vqa', config) + + if args.distributed: + num_tasks = utils.get_world_size() + global_rank = utils.get_rank() + samplers = create_sampler(datasets, [True, False], num_tasks, global_rank) + else: + samplers = [None, None] + + train_loader, test_loader = create_loader(datasets,samplers, + batch_size=[config['batch_size_train'],config['batch_size_test']], + num_workers=[4,4],is_trains=[True, False], + collate_fns=[vqa_collate_fn,None]) + #### Model #### + print("Creating model") + model = blip_vqa(pretrained=config['pretrained'], image_size=config['image_size'], + vit=config['vit'], vit_grad_ckpt=config['vit_grad_ckpt'], vit_ckpt_layer=config['vit_ckpt_layer']) + + model = model.to(device) + + model_without_ddp = model + if args.distributed: + model = torch.nn.parallel.DistributedDataParallel(model, device_ids=[args.gpu]) + model_without_ddp = model.module + + optimizer = torch.optim.AdamW(params=model.parameters(), lr=config['init_lr'], weight_decay=config['weight_decay']) + + best = 0 + best_epoch = 0 + + print("Start training") + start_time = time.time() + for epoch in range(0, config['max_epoch']): + if not args.evaluate: + if args.distributed: + train_loader.sampler.set_epoch(epoch) + + cosine_lr_schedule(optimizer, epoch, config['max_epoch'], config['init_lr'], config['min_lr']) + + train_stats = train(model, train_loader, optimizer, epoch, device) + + else: + break + + if utils.is_main_process(): + log_stats = {**{f'train_{k}': v for k, v in train_stats.items()}, + 'epoch': epoch, + } + with open(os.path.join(args.output_dir, "log.txt"),"a") as f: + f.write(json.dumps(log_stats) + "\n") + + save_obj = { + 'model': model_without_ddp.state_dict(), + 'optimizer': optimizer.state_dict(), + 'config': config, + 'epoch': epoch, + } + torch.save(save_obj, os.path.join(args.output_dir, 'checkpoint_%02d.pth'%epoch)) + + dist.barrier() + + vqa_result = evaluation(model_without_ddp, test_loader, device, config) + result_file = save_result(vqa_result, args.result_dir, 'vqa_result') + + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('Training time {}'.format(total_time_str)) + + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('--config', default='./configs/vqa.yaml') + parser.add_argument('--output_dir', default='output/VQA') + parser.add_argument('--evaluate', action='store_true') + parser.add_argument('--device', default='cuda') + parser.add_argument('--seed', default=42, type=int) + parser.add_argument('--world_size', default=1, type=int, help='number of distributed processes') + parser.add_argument('--dist_url', default='env://', help='url used to set up distributed training') + parser.add_argument('--distributed', default=True, type=bool) + args = parser.parse_args() + + config = yaml.load(open(args.config, 'r'), Loader=yaml.Loader) + + args.result_dir = os.path.join(args.output_dir, 'result') + + Path(args.output_dir).mkdir(parents=True, exist_ok=True) + Path(args.result_dir).mkdir(parents=True, exist_ok=True) + + yaml.dump(config, open(os.path.join(args.output_dir, 'config.yaml'), 'w')) + + main(args, config) \ No newline at end of file diff --git a/transform/__pycache__/randaugment.cpython-36.pyc b/transform/__pycache__/randaugment.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..30e4c1da893637ef737c05b94a96463832dbee23 GIT binary patch literal 10887 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 GQ2aOPX|7-Z literal 0 HcmV?d00001 diff --git a/transform/randaugment.py b/transform/randaugment.py new file mode 100644 index 0000000..094d9f4 --- /dev/null +++ b/transform/randaugment.py @@ -0,0 +1,340 @@ +import cv2 +import numpy as np + + +## aug functions +def identity_func(img): + return img + + +def autocontrast_func(img, cutoff=0): + ''' + same output as PIL.ImageOps.autocontrast + ''' + n_bins = 256 + + def tune_channel(ch): + n = ch.size + cut = cutoff * n // 100 + if cut == 0: + high, low = ch.max(), ch.min() + else: + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + low = np.argwhere(np.cumsum(hist) > cut) + low = 0 if low.shape[0] == 0 else low[0] + high = np.argwhere(np.cumsum(hist[::-1]) > cut) + high = n_bins - 1 if high.shape[0] == 0 else n_bins - 1 - high[0] + if high <= low: + table = np.arange(n_bins) + else: + scale = (n_bins - 1) / (high - low) + offset = -low * scale + table = np.arange(n_bins) * scale + offset + table[table < 0] = 0 + table[table > n_bins - 1] = n_bins - 1 + table = table.clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def equalize_func(img): + ''' + same output as PIL.ImageOps.equalize + PIL's implementation is different from cv2.equalize + ''' + n_bins = 256 + + def tune_channel(ch): + hist = cv2.calcHist([ch], [0], None, [n_bins], [0, n_bins]) + non_zero_hist = hist[hist != 0].reshape(-1) + step = np.sum(non_zero_hist[:-1]) // (n_bins - 1) + if step == 0: return ch + n = np.empty_like(hist) + n[0] = step // 2 + n[1:] = hist[:-1] + table = (np.cumsum(n) // step).clip(0, 255).astype(np.uint8) + return table[ch] + + channels = [tune_channel(ch) for ch in cv2.split(img)] + out = cv2.merge(channels) + return out + + +def rotate_func(img, degree, fill=(0, 0, 0)): + ''' + like PIL, rotate by degree, not radians + ''' + H, W = img.shape[0], img.shape[1] + center = W / 2, H / 2 + M = cv2.getRotationMatrix2D(center, degree, 1) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill) + return out + + +def solarize_func(img, thresh=128): + ''' + same output as PIL.ImageOps.posterize + ''' + table = np.array([el if el < thresh else 255 - el for el in range(256)]) + table = table.clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def color_func(img, factor): + ''' + same output as PIL.ImageEnhance.Color + ''' + ## implementation according to PIL definition, quite slow + # degenerate = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)[:, :, np.newaxis] + # out = blend(degenerate, img, factor) + # M = ( + # np.eye(3) * factor + # + np.float32([0.114, 0.587, 0.299]).reshape(3, 1) * (1. - factor) + # )[np.newaxis, np.newaxis, :] + M = ( + np.float32([ + [0.886, -0.114, -0.114], + [-0.587, 0.413, -0.587], + [-0.299, -0.299, 0.701]]) * factor + + np.float32([[0.114], [0.587], [0.299]]) + ) + out = np.matmul(img, M).clip(0, 255).astype(np.uint8) + return out + + +def contrast_func(img, factor): + """ + same output as PIL.ImageEnhance.Contrast + """ + mean = np.sum(np.mean(img, axis=(0, 1)) * np.array([0.114, 0.587, 0.299])) + table = np.array([( + el - mean) * factor + mean + for el in range(256) + ]).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def brightness_func(img, factor): + ''' + same output as PIL.ImageEnhance.Contrast + ''' + table = (np.arange(256, dtype=np.float32) * factor).clip(0, 255).astype(np.uint8) + out = table[img] + return out + + +def sharpness_func(img, factor): + ''' + The differences the this result and PIL are all on the 4 boundaries, the center + areas are same + ''' + kernel = np.ones((3, 3), dtype=np.float32) + kernel[1][1] = 5 + kernel /= 13 + degenerate = cv2.filter2D(img, -1, kernel) + if factor == 0.0: + out = degenerate + elif factor == 1.0: + out = img + else: + out = img.astype(np.float32) + degenerate = degenerate.astype(np.float32)[1:-1, 1:-1, :] + out[1:-1, 1:-1, :] = degenerate + factor * (out[1:-1, 1:-1, :] - degenerate) + out = out.astype(np.uint8) + return out + + +def shear_x_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, factor, 0], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_x_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, -offset], [0, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def translate_y_func(img, offset, fill=(0, 0, 0)): + ''' + same output as PIL.Image.transform + ''' + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [0, 1, -offset]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def posterize_func(img, bits): + ''' + same output as PIL.ImageOps.posterize + ''' + out = np.bitwise_and(img, np.uint8(255 << (8 - bits))) + return out + + +def shear_y_func(img, factor, fill=(0, 0, 0)): + H, W = img.shape[0], img.shape[1] + M = np.float32([[1, 0, 0], [factor, 1, 0]]) + out = cv2.warpAffine(img, M, (W, H), borderValue=fill, flags=cv2.INTER_LINEAR).astype(np.uint8) + return out + + +def cutout_func(img, pad_size, replace=(0, 0, 0)): + replace = np.array(replace, dtype=np.uint8) + H, W = img.shape[0], img.shape[1] + rh, rw = np.random.random(2) + pad_size = pad_size // 2 + ch, cw = int(rh * H), int(rw * W) + x1, x2 = max(ch - pad_size, 0), min(ch + pad_size, H) + y1, y2 = max(cw - pad_size, 0), min(cw + pad_size, W) + out = img.copy() + out[x1:x2, y1:y2, :] = replace + return out + + +### level to args +def enhance_level_to_args(MAX_LEVEL): + def level_to_args(level): + return ((level / MAX_LEVEL) * 1.8 + 0.1,) + return level_to_args + + +def shear_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 0.3 + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def translate_level_to_args(translate_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * float(translate_const) + if np.random.random() > 0.5: level = -level + return (level, replace_value) + + return level_to_args + + +def cutout_level_to_args(cutout_const, MAX_LEVEL, replace_value): + def level_to_args(level): + level = int((level / MAX_LEVEL) * cutout_const) + return (level, replace_value) + + return level_to_args + + +def solarize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 256) + return (level, ) + return level_to_args + + +def none_level_to_args(level): + return () + + +def posterize_level_to_args(MAX_LEVEL): + def level_to_args(level): + level = int((level / MAX_LEVEL) * 4) + return (level, ) + return level_to_args + + +def rotate_level_to_args(MAX_LEVEL, replace_value): + def level_to_args(level): + level = (level / MAX_LEVEL) * 30 + if np.random.random() < 0.5: + level = -level + return (level, replace_value) + + return level_to_args + + +func_dict = { + 'Identity': identity_func, + 'AutoContrast': autocontrast_func, + 'Equalize': equalize_func, + 'Rotate': rotate_func, + 'Solarize': solarize_func, + 'Color': color_func, + 'Contrast': contrast_func, + 'Brightness': brightness_func, + 'Sharpness': sharpness_func, + 'ShearX': shear_x_func, + 'TranslateX': translate_x_func, + 'TranslateY': translate_y_func, + 'Posterize': posterize_func, + 'ShearY': shear_y_func, +} + +translate_const = 10 +MAX_LEVEL = 10 +replace_value = (128, 128, 128) +arg_dict = { + 'Identity': none_level_to_args, + 'AutoContrast': none_level_to_args, + 'Equalize': none_level_to_args, + 'Rotate': rotate_level_to_args(MAX_LEVEL, replace_value), + 'Solarize': solarize_level_to_args(MAX_LEVEL), + 'Color': enhance_level_to_args(MAX_LEVEL), + 'Contrast': enhance_level_to_args(MAX_LEVEL), + 'Brightness': enhance_level_to_args(MAX_LEVEL), + 'Sharpness': enhance_level_to_args(MAX_LEVEL), + 'ShearX': shear_level_to_args(MAX_LEVEL, replace_value), + 'TranslateX': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'TranslateY': translate_level_to_args( + translate_const, MAX_LEVEL, replace_value + ), + 'Posterize': posterize_level_to_args(MAX_LEVEL), + 'ShearY': shear_level_to_args(MAX_LEVEL, replace_value), +} + + +class RandomAugment(object): + + def __init__(self, N=2, M=10, isPIL=False, augs=[]): + self.N = N + self.M = M + self.isPIL = isPIL + if augs: + self.augs = augs + else: + self.augs = list(arg_dict.keys()) + + def get_random_ops(self): + sampled_ops = np.random.choice(self.augs, self.N) + return [(op, 0.5, self.M) for op in sampled_ops] + + def __call__(self, img): + if self.isPIL: + img = np.array(img) + ops = self.get_random_ops() + for name, prob, level in ops: + if np.random.random() > prob: + continue + args = arg_dict[name](level) + img = func_dict[name](img, *args) + return img + + +if __name__ == '__main__': + a = RandomAugment() + img = np.random.randn(32, 32, 3) + a(img) \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..ebe0e1d --- /dev/null +++ b/utils.py @@ -0,0 +1,278 @@ +import math +def cosine_lr_schedule(optimizer, epoch, max_epoch, init_lr, min_lr): + """Decay the learning rate""" + lr = (init_lr - min_lr) * 0.5 * (1. + math.cos(math.pi * epoch / max_epoch)) + min_lr + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def warmup_lr_schedule(optimizer, step, max_step, init_lr, max_lr): + """Warmup the learning rate""" + lr = min(max_lr, init_lr + (max_lr - init_lr) * step / max_step) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +def step_lr_schedule(optimizer, epoch, init_lr, min_lr, decay_rate): + """Decay the learning rate""" + lr = max(min_lr, init_lr * (decay_rate**epoch)) + for param_group in optimizer.param_groups: + param_group['lr'] = lr + +import numpy as np +import io +import os +import time +from collections import defaultdict, deque +import datetime + +import torch +import torch.distributed as dist + +class SmoothedValue(object): + """Track a series of values and provide access to smoothed values over a + window or the global series average. + """ + + def __init__(self, window_size=20, fmt=None): + if fmt is None: + fmt = "{median:.4f} ({global_avg:.4f})" + self.deque = deque(maxlen=window_size) + self.total = 0.0 + self.count = 0 + self.fmt = fmt + + def update(self, value, n=1): + self.deque.append(value) + self.count += n + self.total += value * n + + def synchronize_between_processes(self): + """ + Warning: does not synchronize the deque! + """ + if not is_dist_avail_and_initialized(): + return + t = torch.tensor([self.count, self.total], dtype=torch.float64, device='cuda') + dist.barrier() + dist.all_reduce(t) + t = t.tolist() + self.count = int(t[0]) + self.total = t[1] + + @property + def median(self): + d = torch.tensor(list(self.deque)) + return d.median().item() + + @property + def avg(self): + d = torch.tensor(list(self.deque), dtype=torch.float32) + return d.mean().item() + + @property + def global_avg(self): + return self.total / self.count + + @property + def max(self): + return max(self.deque) + + @property + def value(self): + return self.deque[-1] + + def __str__(self): + return self.fmt.format( + median=self.median, + avg=self.avg, + global_avg=self.global_avg, + max=self.max, + value=self.value) + + +class MetricLogger(object): + def __init__(self, delimiter="\t"): + self.meters = defaultdict(SmoothedValue) + self.delimiter = delimiter + + def update(self, **kwargs): + for k, v in kwargs.items(): + if isinstance(v, torch.Tensor): + v = v.item() + assert isinstance(v, (float, int)) + self.meters[k].update(v) + + def __getattr__(self, attr): + if attr in self.meters: + return self.meters[attr] + if attr in self.__dict__: + return self.__dict__[attr] + raise AttributeError("'{}' object has no attribute '{}'".format( + type(self).__name__, attr)) + + def __str__(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {}".format(name, str(meter)) + ) + return self.delimiter.join(loss_str) + + def global_avg(self): + loss_str = [] + for name, meter in self.meters.items(): + loss_str.append( + "{}: {:.4f}".format(name, meter.global_avg) + ) + return self.delimiter.join(loss_str) + + def synchronize_between_processes(self): + for meter in self.meters.values(): + meter.synchronize_between_processes() + + def add_meter(self, name, meter): + self.meters[name] = meter + + def log_every(self, iterable, print_freq, header=None): + i = 0 + if not header: + header = '' + start_time = time.time() + end = time.time() + iter_time = SmoothedValue(fmt='{avg:.4f}') + data_time = SmoothedValue(fmt='{avg:.4f}') + space_fmt = ':' + str(len(str(len(iterable)))) + 'd' + log_msg = [ + header, + '[{0' + space_fmt + '}/{1}]', + 'eta: {eta}', + '{meters}', + 'time: {time}', + 'data: {data}' + ] + if torch.cuda.is_available(): + log_msg.append('max mem: {memory:.0f}') + log_msg = self.delimiter.join(log_msg) + MB = 1024.0 * 1024.0 + for obj in iterable: + data_time.update(time.time() - end) + yield obj + iter_time.update(time.time() - end) + if i % print_freq == 0 or i == len(iterable) - 1: + eta_seconds = iter_time.global_avg * (len(iterable) - i) + eta_string = str(datetime.timedelta(seconds=int(eta_seconds))) + if torch.cuda.is_available(): + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time), + memory=torch.cuda.max_memory_allocated() / MB)) + else: + print(log_msg.format( + i, len(iterable), eta=eta_string, + meters=str(self), + time=str(iter_time), data=str(data_time))) + i += 1 + end = time.time() + total_time = time.time() - start_time + total_time_str = str(datetime.timedelta(seconds=int(total_time))) + print('{} Total time: {} ({:.4f} s / it)'.format( + header, total_time_str, total_time / len(iterable))) + + +class AttrDict(dict): + def __init__(self, *args, **kwargs): + super(AttrDict, self).__init__(*args, **kwargs) + self.__dict__ = self + + +def compute_acc(logits, label, reduction='mean'): + ret = (torch.argmax(logits, dim=1) == label).float() + if reduction == 'none': + return ret.detach() + elif reduction == 'mean': + return ret.mean().item() + +def compute_n_params(model, return_str=True): + tot = 0 + for p in model.parameters(): + w = 1 + for x in p.shape: + w *= x + tot += w + if return_str: + if tot >= 1e6: + return '{:.1f}M'.format(tot / 1e6) + else: + return '{:.1f}K'.format(tot / 1e3) + else: + return tot + +def setup_for_distributed(is_master): + """ + This function disables printing when not in master process + """ + import builtins as __builtin__ + builtin_print = __builtin__.print + + def print(*args, **kwargs): + force = kwargs.pop('force', False) + if is_master or force: + builtin_print(*args, **kwargs) + + __builtin__.print = print + + +def is_dist_avail_and_initialized(): + if not dist.is_available(): + return False + if not dist.is_initialized(): + return False + return True + + +def get_world_size(): + if not is_dist_avail_and_initialized(): + return 1 + return dist.get_world_size() + + +def get_rank(): + if not is_dist_avail_and_initialized(): + return 0 + return dist.get_rank() + + +def is_main_process(): + return get_rank() == 0 + + +def save_on_master(*args, **kwargs): + if is_main_process(): + torch.save(*args, **kwargs) + + +def init_distributed_mode(args): + if 'RANK' in os.environ and 'WORLD_SIZE' in os.environ: + args.rank = int(os.environ["RANK"]) + args.world_size = int(os.environ['WORLD_SIZE']) + args.gpu = int(os.environ['LOCAL_RANK']) + elif 'SLURM_PROCID' in os.environ: + args.rank = int(os.environ['SLURM_PROCID']) + args.gpu = args.rank % torch.cuda.device_count() + else: + print('Not using distributed mode') + args.distributed = False + return + + args.distributed = True + + torch.cuda.set_device(args.gpu) + args.dist_backend = 'nccl' + print('| distributed init (rank {}, word {}): {}'.format( + args.rank, args.world_size, args.dist_url), flush=True) + torch.distributed.init_process_group(backend=args.dist_backend, init_method=args.dist_url, + world_size=args.world_size, rank=args.rank) + torch.distributed.barrier() + setup_for_distributed(args.rank == 0) + + \ No newline at end of file