add readme (#10)
* Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * Update Readme.md * remove submodule * add mPLUG MiniGPT4 * Update Readme.md * Update Readme.md * Update Readme.md --------- Co-authored-by: Yuliang Liu <34134635+Yuliang-Liu@users.noreply.github.com>
91
Readme.md
@@ -1,7 +1,90 @@
|
||||
# Evaluation Pipeline
|
||||
[Paper](https://arxiv.org/pdf/2305.07895.pdf).
|
||||
|
||||
[Paper](https://arxiv.org/pdf/2305.07895.pdf). V2 is updating.
|
||||
# Results
|
||||
|
||||
Updating the instruction of evaluating large multimodal models on ocr tasks.
|
||||
Results are available in answer_save folder.
|
||||
|
||||
Feel free to open issues for any suggestion or comment.
|
||||

|
||||
|
||||
Visualization results
|
||||
|
||||

|
||||
|
||||
|
||||
# Data Download
|
||||
| Data file | Size |
|
||||
| --- | ---: |
|
||||
|[text recognition](https://pan.baidu.com/s/1Ba950d94u8RQmtqvkLBk-A) code:iwyn | 1.37GB |
|
||||
|
||||
TextVQA, KIE and HME will be updated soon.
|
||||
|
||||
We assume that your symlinked `data` directory has the following structure:
|
||||
|
||||
```
|
||||
data
|
||||
|_ IC13_857
|
||||
|_ IC15_1811
|
||||
|_ ...
|
||||
|_ ESTVQA
|
||||
|_ textVQA
|
||||
|_ ...
|
||||
|_ FUNSD
|
||||
|_ POIE
|
||||
```
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
eval on all datasets
|
||||
```Shell
|
||||
python eval.py --model_name LLaVA --eval_all
|
||||
```
|
||||
|
||||
eval on one dataset
|
||||
```Shell
|
||||
python eval.py --model_name LLaVA --eval_textVQA
|
||||
```
|
||||
```Shell
|
||||
python eval.py --model_name LLaVA --eval_ocr --ocr_dataset_name "ct80 IIIT5K"
|
||||
```
|
||||
The results will be saved at answer folder.
|
||||
|
||||
If you want to add a new model, please write its inference function under the folder "models", and update the get_model function in eval.py. An example inference code is as follows:
|
||||
|
||||
```Shell
|
||||
import torch
|
||||
from PIL import Image
|
||||
from lavis.models import load_model_and_preprocess
|
||||
from ..process import pad_image, resize_image
|
||||
class lavis:
|
||||
def __init__(self, model_name, model_type, device) -> None:
|
||||
model, vis_processors, txt_processors = load_model_and_preprocess(name = model_name, model_type = model_type, is_eval=True, device=device)
|
||||
self.model_name = model_name
|
||||
self.model = model
|
||||
self.vis_processors = vis_processors
|
||||
self.txt_processors = txt_processors
|
||||
self.device = device
|
||||
def generate(self, image, question, name='resize'):
|
||||
if 'opt' in self.model_name:
|
||||
prompt = f'Question: {question} Answer:'
|
||||
elif 't5' in self.model_name:
|
||||
prompt = f'Question: {question} Short answer:'
|
||||
else:
|
||||
prompt = f'Question: {question} Answer:'
|
||||
image = Image.open(image).convert("RGB")
|
||||
if name == "pad":
|
||||
image = pad_image(image, (224,224))
|
||||
elif name == "resize":
|
||||
image = resize_image(image, (224,224))
|
||||
image = self.vis_processors["eval"](image).unsqueeze(0).to(self.device)
|
||||
prompt = self.txt_processors["eval"](prompt)
|
||||
answer = self.model.predict_answers(samples={"image": image, "text_input": prompt}, inference_method="generate", max_len=48, min_len=1)[0]
|
||||
return answer
|
||||
```
|
||||
|
||||
# Related Projects
|
||||
- [LLaVA](https://github.com/haotian-liu/LLaVA.git)
|
||||
- [MiniGPT4](https://github.com/Vision-CAIR/MiniGPT-4.git)
|
||||
- [mPLUG-Owl](https://github.com/X-PLUG/mPLUG-Owl.git)
|
||||
- [OpenFlamingo](https://github.com/mlfoundations/open_flamingo.git)
|
||||
- [Lavis](https://github.com/salesforce/LAVIS.git)
|
||||
|
14
models/MiniGPT4/LICENSE.md
Normal file
@@ -0,0 +1,14 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright 2023 Deyao Zhu
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
14
models/MiniGPT4/LICENSE_Lavis.md
Normal file
@@ -0,0 +1,14 @@
|
||||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2022 Salesforce, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
50
models/MiniGPT4/MiniGPT4.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
import numpy as np
|
||||
import torch.backends.cudnn as cudnn
|
||||
from minigpt4.common.config import Config
|
||||
from minigpt4.common.dist_utils import get_rank
|
||||
from minigpt4.common.registry import registry
|
||||
from minigpt4.conversation.conversation import Chat, CONV_VISION
|
||||
|
||||
# imports modules for registration
|
||||
from minigpt4.datasets.builders import *
|
||||
from minigpt4.models import *
|
||||
from minigpt4.processors import *
|
||||
from minigpt4.runners import *
|
||||
from minigpt4.tasks import *
|
||||
from ..process import pad_image, resize_image
|
||||
from PIL import Image
|
||||
import torch
|
||||
class MiniGPT4:
|
||||
def __init__(self, args, device='cuda:0') -> None:
|
||||
args.cfg_path = args.MiniGPT4_cfg_path
|
||||
args.options=None
|
||||
cfg = Config(args)
|
||||
model_config = cfg.model_cfg
|
||||
model_config.device_8bit = int(device[-1])
|
||||
model_cls = registry.get_model_class(model_config.arch)
|
||||
model = model_cls.from_config(model_config).to(device)
|
||||
|
||||
vis_processor_cfg = cfg.datasets_cfg.cc_sbu_align.vis_processor.train
|
||||
vis_processor = registry.get_processor_class(vis_processor_cfg.name).from_config(vis_processor_cfg)
|
||||
self.chat = Chat(model, vis_processor, device = device)
|
||||
def generate(self, image, question, name= 'resize', *kargs):
|
||||
chat_state = CONV_VISION.copy()
|
||||
num_beams = 1
|
||||
temperature = 0.9
|
||||
img_list = []
|
||||
image = Image.open(image).convert('RGB')
|
||||
if name == 'resize':
|
||||
image = resize_image(image, (224,224))
|
||||
llm_message = self.chat.upload_img(image, chat_state, img_list)
|
||||
self.chat.ask(question, chat_state)
|
||||
llm_message = self.chat.answer(conv=chat_state,
|
||||
img_list=img_list,
|
||||
num_beams=num_beams,
|
||||
temperature=temperature,
|
||||
max_new_tokens=128,
|
||||
max_length=640)[0]
|
||||
return llm_message
|
||||
|
BIN
models/MiniGPT4/MiniGPT_4.pdf
Normal file
35
models/MiniGPT4/PrepareVicuna.md
Normal file
@@ -0,0 +1,35 @@
|
||||
## How to Prepare Vicuna Weight
|
||||
Vicuna is an open-source LLAMA-based LLM that has a performance close to ChatGPT.
|
||||
We currently use the v0 version of Vicuna-13B.
|
||||
|
||||
To prepare Vicuna’s weight, first download Vicuna’s **delta** weight from [https://huggingface.co/lmsys/vicuna-13b-delta-v0](https://huggingface.co/lmsys/vicuna-13b-delta-v0).
|
||||
In case you have git-lfs installed (https://git-lfs.com), this can be done by
|
||||
|
||||
```
|
||||
git lfs install
|
||||
git clone https://huggingface.co/lmsys/vicuna-13b-delta-v0 # more powerful, need at least 24G gpu memory
|
||||
# or
|
||||
git clone https://huggingface.co/lmsys/vicuna-7b-delta-v0 # smaller, need 12G gpu memory
|
||||
```
|
||||
|
||||
Note that this is not directly the working weight, but the difference between the working weight and the original weight of LLAMA-13B. (Due to LLAMA’s rules, we cannot distribute the weight of LLAMA.)
|
||||
|
||||
Then, you need to obtain the original LLAMA-7B or LLAMA-13B weights in the HuggingFace format
|
||||
either following the instruction provided by HuggingFace
|
||||
[here](https://huggingface.co/docs/transformers/main/model_doc/llama) or from the Internet.
|
||||
|
||||
When these two weights are ready, we can use tools from Vicuna’s team to create the real working weight.
|
||||
First, Install their library that is compatible with v0 Vicuna by
|
||||
|
||||
```
|
||||
pip install git+https://github.com/lm-sys/FastChat.git@v0.1.10
|
||||
```
|
||||
|
||||
Then, run the following command to create the final working weight
|
||||
|
||||
```
|
||||
python -m fastchat.model.apply_delta --base /path/to/llama-13bOR7b-hf/ --target /path/to/save/working/vicuna/weight/ --delta /path/to/vicuna-13bOR7b-delta-v0/
|
||||
```
|
||||
|
||||
Now you are good to go!
|
||||
|
170
models/MiniGPT4/README.md
Normal file
@@ -0,0 +1,170 @@
|
||||
# MiniGPT-4: Enhancing Vision-language Understanding with Advanced Large Language Models
|
||||
[Deyao Zhu](https://tsutikgiau.github.io/)* (On Job Market!), [Jun Chen](https://junchen14.github.io/)* (On Job Market!), [Xiaoqian Shen](https://xiaoqian-shen.github.io), [Xiang Li](https://xiangli.ac.cn), and [Mohamed Elhoseiny](https://www.mohamed-elhoseiny.com/). *Equal Contribution
|
||||
|
||||
**King Abdullah University of Science and Technology**
|
||||
|
||||
<a href='https://minigpt-4.github.io'><img src='https://img.shields.io/badge/Project-Page-Green'></a> <a href='https://arxiv.org/abs/2304.10592'><img src='https://img.shields.io/badge/Paper-Arxiv-red'></a> <a href='https://huggingface.co/spaces/Vision-CAIR/minigpt4'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Spaces-blue'></a> <a href='https://huggingface.co/Vision-CAIR/MiniGPT-4'><img src='https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Model-blue'></a> [](https://colab.research.google.com/drive/1OK4kYsZphwt5DXchKkzMBjYF6jnkqh4R?usp=sharing) [](https://www.youtube.com/watch?v=__tftoxpBAw&feature=youtu.be)
|
||||
|
||||
|
||||
## News
|
||||
We now provide a pretrained MiniGPT-4 aligned with Vicuna-7B! The demo GPU memory consumption now can be as low as 12GB.
|
||||
|
||||
|
||||
## Online Demo
|
||||
|
||||
Click the image to chat with MiniGPT-4 around your images
|
||||
[](https://minigpt-4.github.io)
|
||||
|
||||
|
||||
## Examples
|
||||
| | |
|
||||
:-------------------------:|:-------------------------:
|
||||
 | 
|
||||
 | 
|
||||
|
||||
More examples can be found in the [project page](https://minigpt-4.github.io).
|
||||
|
||||
|
||||
|
||||
## Introduction
|
||||
- MiniGPT-4 aligns a frozen visual encoder from BLIP-2 with a frozen LLM, Vicuna, using just one projection layer.
|
||||
- We train MiniGPT-4 with two stages. The first traditional pretraining stage is trained using roughly 5 million aligned image-text pairs in 10 hours using 4 A100s. After the first stage, Vicuna is able to understand the image. But the generation ability of Vicuna is heavilly impacted.
|
||||
- To address this issue and improve usability, we propose a novel way to create high-quality image-text pairs by the model itself and ChatGPT together. Based on this, we then create a small (3500 pairs in total) yet high-quality dataset.
|
||||
- The second finetuning stage is trained on this dataset in a conversation template to significantly improve its generation reliability and overall usability. To our surprise, this stage is computationally efficient and takes only around 7 minutes with a single A100.
|
||||
- MiniGPT-4 yields many emerging vision-language capabilities similar to those demonstrated in GPT-4.
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
## Getting Started
|
||||
### Installation
|
||||
|
||||
**1. Prepare the code and the environment**
|
||||
|
||||
Git clone our repository, creating a python environment and ativate it via the following command
|
||||
|
||||
```bash
|
||||
git clone https://github.com/Vision-CAIR/MiniGPT-4.git
|
||||
cd MiniGPT-4
|
||||
conda env create -f environment.yml
|
||||
conda activate minigpt4
|
||||
```
|
||||
|
||||
|
||||
**2. Prepare the pretrained Vicuna weights**
|
||||
|
||||
The current version of MiniGPT-4 is built on the v0 versoin of Vicuna-13B.
|
||||
Please refer to our instruction [here](PrepareVicuna.md)
|
||||
to prepare the Vicuna weights.
|
||||
The final weights would be in a single folder in a structure similar to the following:
|
||||
|
||||
```
|
||||
vicuna_weights
|
||||
├── config.json
|
||||
├── generation_config.json
|
||||
├── pytorch_model.bin.index.json
|
||||
├── pytorch_model-00001-of-00003.bin
|
||||
...
|
||||
```
|
||||
|
||||
Then, set the path to the vicuna weight in the model config file
|
||||
[here](minigpt4/configs/models/minigpt4.yaml#L16) at Line 16.
|
||||
|
||||
**3. Prepare the pretrained MiniGPT-4 checkpoint**
|
||||
|
||||
Download the pretrained checkpoints according to the Vicuna model you prepare.
|
||||
|
||||
| Checkpoint Aligned with Vicuna 13B | Checkpoint Aligned with Vicuna 7B |
|
||||
:------------------------------------------------------------------------------------------------:|:----------------------------------------------------------------------------------------------:
|
||||
[Downlad](https://drive.google.com/file/d/1a4zLvaiDBr-36pasffmgpvH5P7CKmpze/view?usp=share_link) | [Download](https://drive.google.com/file/d/1RY9jV0dyqLX-o38LrumkKRh6Jtaop58R/view?usp=sharing)
|
||||
|
||||
|
||||
Then, set the path to the pretrained checkpoint in the evaluation config file
|
||||
in [eval_configs/minigpt4_eval.yaml](eval_configs/minigpt4_eval.yaml#L10) at Line 11.
|
||||
|
||||
|
||||
|
||||
### Launching Demo Locally
|
||||
|
||||
Try out our demo [demo.py](demo.py) on your local machine by running
|
||||
|
||||
```
|
||||
python demo.py --cfg-path eval_configs/minigpt4_eval.yaml --gpu-id 0
|
||||
```
|
||||
|
||||
To save GPU memory, Vicuna loads as 8 bit by default, with a beam search width of 1.
|
||||
This configuration requires about 23G GPU memory for Vicuna 13B and 11.5G GPU memory for Vicuna 7B.
|
||||
For more powerful GPUs, you can run the model
|
||||
in 16 bit by setting low_resource to False in the config file
|
||||
[minigpt4_eval.yaml](eval_configs/minigpt4_eval.yaml) and use a larger beam search width.
|
||||
|
||||
Thanks [@WangRongsheng](https://github.com/WangRongsheng), you can also run our code on [Colab](https://colab.research.google.com/drive/1OK4kYsZphwt5DXchKkzMBjYF6jnkqh4R?usp=sharing)
|
||||
|
||||
|
||||
### Training
|
||||
The training of MiniGPT-4 contains two alignment stages.
|
||||
|
||||
**1. First pretraining stage**
|
||||
|
||||
In the first pretrained stage, the model is trained using image-text pairs from Laion and CC datasets
|
||||
to align the vision and language model. To download and prepare the datasets, please check
|
||||
our [first stage dataset preparation instruction](dataset/README_1_STAGE.md).
|
||||
After the first stage, the visual features are mapped and can be understood by the language
|
||||
model.
|
||||
To launch the first stage training, run the following command. In our experiments, we use 4 A100.
|
||||
You can change the save path in the config file
|
||||
[train_configs/minigpt4_stage1_pretrain.yaml](train_configs/minigpt4_stage1_pretrain.yaml)
|
||||
|
||||
```bash
|
||||
torchrun --nproc-per-node NUM_GPU train.py --cfg-path train_configs/minigpt4_stage1_pretrain.yaml
|
||||
```
|
||||
|
||||
A MiniGPT-4 checkpoint with only stage one training can be downloaded
|
||||
[here (13B)](https://drive.google.com/file/d/1u9FRRBB3VovP1HxCAlpD9Lw4t4P6-Yq8/view?usp=share_link) or [here (7B)](https://drive.google.com/file/d/1HihQtCEXUyBM1i9DQbaK934wW3TZi-h5/view?usp=share_link).
|
||||
Compared to the model after stage two, this checkpoint generate incomplete and repeated sentences frequently.
|
||||
|
||||
|
||||
**2. Second finetuning stage**
|
||||
|
||||
In the second stage, we use a small high quality image-text pair dataset created by ourselves
|
||||
and convert it to a conversation format to further align MiniGPT-4.
|
||||
To download and prepare our second stage dataset, please check our
|
||||
[second stage dataset preparation instruction](dataset/README_2_STAGE.md).
|
||||
To launch the second stage alignment,
|
||||
first specify the path to the checkpoint file trained in stage 1 in
|
||||
[train_configs/minigpt4_stage1_pretrain.yaml](train_configs/minigpt4_stage2_finetune.yaml).
|
||||
You can also specify the output path there.
|
||||
Then, run the following command. In our experiments, we use 1 A100.
|
||||
|
||||
```bash
|
||||
torchrun --nproc-per-node NUM_GPU train.py --cfg-path train_configs/minigpt4_stage2_finetune.yaml
|
||||
```
|
||||
|
||||
After the second stage alignment, MiniGPT-4 is able to talk about the image coherently and user-friendly.
|
||||
|
||||
|
||||
|
||||
|
||||
## Acknowledgement
|
||||
|
||||
+ [BLIP2](https://huggingface.co/docs/transformers/main/model_doc/blip-2) The model architecture of MiniGPT-4 follows BLIP-2. Don't forget to check this great open-source work if you don't know it before!
|
||||
+ [Lavis](https://github.com/salesforce/LAVIS) This repository is built upon Lavis!
|
||||
+ [Vicuna](https://github.com/lm-sys/FastChat) The fantastic language ability of Vicuna with only 13B parameters is just amazing. And it is open-source!
|
||||
|
||||
|
||||
If you're using MiniGPT-4 in your research or applications, please cite using this BibTeX:
|
||||
```bibtex
|
||||
@article{zhu2023minigpt,
|
||||
title={MiniGPT-4: Enhancing Vision-Language Understanding with Advanced Large Language Models},
|
||||
author={Zhu, Deyao and Chen, Jun and Shen, Xiaoqian and Li, Xiang and Elhoseiny, Mohamed},
|
||||
journal={arXiv preprint arXiv:2304.10592},
|
||||
year={2023}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
This repository is under [BSD 3-Clause License](LICENSE.md).
|
||||
Many codes are based on [Lavis](https://github.com/salesforce/LAVIS) with
|
||||
BSD 3-Clause License [here](LICENSE_Lavis.md).
|
BIN
models/MiniGPT4/__pycache__/MiniGPT4.cpython-310.pyc
Normal file
96
models/MiniGPT4/dataset/README_1_STAGE.md
Normal file
@@ -0,0 +1,96 @@
|
||||
## Download the filtered Conceptual Captions, SBU, LAION datasets
|
||||
|
||||
### Pre-training datasets download:
|
||||
We use the filtered synthetic captions prepared by BLIP. For more details about the dataset, please refer to [BLIP](https://github.com/salesforce/BLIP).
|
||||
|
||||
It requires ~2.3T to store LAION and CC3M+CC12M+SBU datasets
|
||||
|
||||
Image source | Filtered synthetic caption by ViT-L
|
||||
--- | :---:
|
||||
CC3M+CC12M+SBU | <a href="https://storage.googleapis.com/sfr-vision-language-research/BLIP/datasets/ccs_synthetic_filtered_large.json">Download</a>
|
||||
LAION115M | <a href="https://storage.googleapis.com/sfr-vision-language-research/BLIP/datasets/laion_synthetic_filtered_large.json">Download</a>
|
||||
|
||||
This will download two json files
|
||||
```
|
||||
ccs_synthetic_filtered_large.json
|
||||
laion_synthetic_filtered_large.json
|
||||
```
|
||||
|
||||
## prepare the data step-by-step
|
||||
|
||||
|
||||
### setup the dataset folder and move the annotation file to the data storage folder
|
||||
```
|
||||
export MINIGPT4_DATASET=/YOUR/PATH/FOR/LARGE/DATASET/
|
||||
mkdir ${MINIGPT4_DATASET}/cc_sbu
|
||||
mkdir ${MINIGPT4_DATASET}/laion
|
||||
mv ccs_synthetic_filtered_large.json ${MINIGPT4_DATASET}/cc_sbu
|
||||
mv laion_synthetic_filtered_large.json ${MINIGPT4_DATASET}/laion
|
||||
```
|
||||
|
||||
### Convert the scripts to data storate folder
|
||||
```
|
||||
cp convert_cc_sbu.py ${MINIGPT4_DATASET}/cc_sbu
|
||||
cp download_cc_sbu.sh ${MINIGPT4_DATASET}/cc_sbu
|
||||
cp convert_laion.py ${MINIGPT4_DATASET}/laion
|
||||
cp download_laion.sh ${MINIGPT4_DATASET}/laion
|
||||
```
|
||||
|
||||
|
||||
### Convert the laion and cc_sbu annotation file format to be img2dataset format
|
||||
```
|
||||
cd ${MINIGPT4_DATASET}/cc_sbu
|
||||
python convert_cc_sbu.py
|
||||
|
||||
cd ${MINIGPT4_DATASET}/laion
|
||||
python convert_laion.py
|
||||
```
|
||||
|
||||
### Download the datasets with img2dataset
|
||||
```
|
||||
cd ${MINIGPT4_DATASET}/cc_sbu
|
||||
sh download_cc_sbu.sh
|
||||
cd ${MINIGPT4_DATASET}/laion
|
||||
sh download_laion.sh
|
||||
```
|
||||
|
||||
|
||||
The final dataset structure
|
||||
|
||||
```
|
||||
.
|
||||
├── ${MINIGPT4_DATASET}
|
||||
│ ├── cc_sbu
|
||||
│ ├── convert_cc_sbu.py
|
||||
│ ├── download_cc_sbu.sh
|
||||
│ ├── ccs_synthetic_filtered_large.json
|
||||
│ ├── ccs_synthetic_filtered_large.tsv
|
||||
│ └── cc_sbu_dataset
|
||||
│ ├── 00000.tar
|
||||
│ ├── 00000.parquet
|
||||
│ ...
|
||||
│ ├── laion
|
||||
│ ├── convert_laion.py
|
||||
│ ├── download_laion.sh
|
||||
│ ├── laion_synthetic_filtered_large.json
|
||||
│ ├── laion_synthetic_filtered_large.tsv
|
||||
│ └── laion_dataset
|
||||
│ ├── 00000.tar
|
||||
│ ├── 00000.parquet
|
||||
│ ...
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
## Set up the dataset configuration files
|
||||
|
||||
Then, set up the LAION dataset loading path in
|
||||
[here](../minigpt4/configs/datasets/laion/defaults.yaml#L5) at Line 5 as
|
||||
${MINIGPT4_DATASET}/laion/laion_dataset/{00000..10488}.tar
|
||||
|
||||
and the Conceptual Captoin and SBU datasets loading path in
|
||||
[here](../minigpt4/configs/datasets/cc_sbu/defaults.yaml#L5) at Line 5 as
|
||||
${MINIGPT4_DATASET}/cc_sbu/cc_sbu_dataset/{00000..01255}.tar
|
||||
|
||||
|
||||
|
19
models/MiniGPT4/dataset/README_2_STAGE.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## Second Stage Data Preparation
|
||||
|
||||
Our second stage dataset can be downloaded from
|
||||
[here](https://drive.google.com/file/d/1nJXhoEcy3KTExr17I7BXqY5Y9Lx_-n-9/view?usp=share_link)
|
||||
After extraction, you will get a data follder with the following structure:
|
||||
|
||||
```
|
||||
cc_sbu_align
|
||||
├── filter_cap.json
|
||||
└── image
|
||||
├── 2.jpg
|
||||
├── 3.jpg
|
||||
...
|
||||
```
|
||||
|
||||
Put the folder to any path you want.
|
||||
Then, set up the dataset path in the dataset config file
|
||||
[here](../minigpt4/configs/datasets/cc_sbu/align.yaml#L5) at Line 5.
|
||||
|
20
models/MiniGPT4/dataset/convert_cc_sbu.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import json
|
||||
import csv
|
||||
|
||||
# specify input and output file paths
|
||||
input_file = 'ccs_synthetic_filtered_large.json'
|
||||
output_file = 'ccs_synthetic_filtered_large.tsv'
|
||||
|
||||
# load JSON data from input file
|
||||
with open(input_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# extract header and data from JSON
|
||||
header = data[0].keys()
|
||||
rows = [x.values() for x in data]
|
||||
|
||||
# write data to TSV file
|
||||
with open(output_file, 'w') as f:
|
||||
writer = csv.writer(f, delimiter='\t')
|
||||
writer.writerow(header)
|
||||
writer.writerows(rows)
|
20
models/MiniGPT4/dataset/convert_laion.py
Normal file
@@ -0,0 +1,20 @@
|
||||
import json
|
||||
import csv
|
||||
|
||||
# specify input and output file paths
|
||||
input_file = 'laion_synthetic_filtered_large.json'
|
||||
output_file = 'laion_synthetic_filtered_large.tsv'
|
||||
|
||||
# load JSON data from input file
|
||||
with open(input_file, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# extract header and data from JSON
|
||||
header = data[0].keys()
|
||||
rows = [x.values() for x in data]
|
||||
|
||||
# write data to TSV file
|
||||
with open(output_file, 'w') as f:
|
||||
writer = csv.writer(f, delimiter='\t')
|
||||
writer.writerow(header)
|
||||
writer.writerows(rows)
|
6
models/MiniGPT4/dataset/download_cc_sbu.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
img2dataset --url_list ccs_synthetic_filtered_large.tsv --input_format "tsv"\
|
||||
--url_col "url" --caption_col "caption" --output_format webdataset\
|
||||
--output_folder cc_sbu_dataset --processes_count 16 --thread_count 128 --image_size 256 \
|
||||
--enable_wandb True
|
6
models/MiniGPT4/dataset/download_laion.sh
Normal file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
img2dataset --url_list laion_synthetic_filtered_large.tsv --input_format "tsv"\
|
||||
--url_col "url" --caption_col "caption" --output_format webdataset\
|
||||
--output_folder laion_dataset --processes_count 16 --thread_count 128 --image_size 256 \
|
||||
--enable_wandb True
|
154
models/MiniGPT4/demo.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.backends.cudnn as cudnn
|
||||
import gradio as gr
|
||||
|
||||
from minigpt4.common.config import Config
|
||||
from minigpt4.common.dist_utils import get_rank
|
||||
from minigpt4.common.registry import registry
|
||||
from minigpt4.conversation.conversation import Chat, CONV_VISION
|
||||
|
||||
# imports modules for registration
|
||||
from minigpt4.datasets.builders import *
|
||||
from minigpt4.models import *
|
||||
from minigpt4.processors import *
|
||||
from minigpt4.runners import *
|
||||
from minigpt4.tasks import *
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description="Demo")
|
||||
parser.add_argument("--cfg-path", required=True, help="path to configuration file.")
|
||||
parser.add_argument("--gpu-id", type=int, default=0, help="specify the gpu to load the model.")
|
||||
parser.add_argument(
|
||||
"--options",
|
||||
nargs="+",
|
||||
help="override some settings in the used config, the key-value pair "
|
||||
"in xxx=yyy format will be merged into config file (deprecate), "
|
||||
"change to --cfg-options instead.",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def setup_seeds(config):
|
||||
import pdb;pdb.set_trace()
|
||||
seed = config.run_cfg.seed + get_rank()
|
||||
|
||||
random.seed(seed)
|
||||
np.random.seed(seed)
|
||||
torch.manual_seed(seed)
|
||||
|
||||
cudnn.benchmark = False
|
||||
cudnn.deterministic = True
|
||||
|
||||
|
||||
# ========================================
|
||||
# Model Initialization
|
||||
# ========================================
|
||||
|
||||
print('Initializing Chat')
|
||||
args = parse_args()
|
||||
cfg = Config(args)
|
||||
import pdb;pdb.set_trace()
|
||||
model_config = cfg.model_cfg
|
||||
model_config.device_8bit = args.gpu_id
|
||||
model_cls = registry.get_model_class(model_config.arch)
|
||||
model = model_cls.from_config(model_config).to('cuda:{}'.format(args.gpu_id))
|
||||
|
||||
vis_processor_cfg = cfg.datasets_cfg.cc_sbu_align.vis_processor.train
|
||||
vis_processor = registry.get_processor_class(vis_processor_cfg.name).from_config(vis_processor_cfg)
|
||||
chat = Chat(model, vis_processor, device='cuda:{}'.format(args.gpu_id))
|
||||
print('Initialization Finished')
|
||||
|
||||
# ========================================
|
||||
# Gradio Setting
|
||||
# ========================================
|
||||
|
||||
def gradio_reset(chat_state, img_list):
|
||||
if chat_state is not None:
|
||||
chat_state.messages = []
|
||||
if img_list is not None:
|
||||
img_list = []
|
||||
return None, gr.update(value=None, interactive=True), gr.update(placeholder='Please upload your image first', interactive=False),gr.update(value="Upload & Start Chat", interactive=True), chat_state, img_list
|
||||
|
||||
def upload_img(gr_img, text_input, chat_state):
|
||||
if gr_img is None:
|
||||
return None, None, gr.update(interactive=True), chat_state, None
|
||||
chat_state = CONV_VISION.copy()
|
||||
img_list = []
|
||||
llm_message = chat.upload_img(gr_img, chat_state, img_list)
|
||||
return gr.update(interactive=False), gr.update(interactive=True, placeholder='Type and press Enter'), gr.update(value="Start Chatting", interactive=False), chat_state, img_list
|
||||
|
||||
def gradio_ask(user_message, chatbot, chat_state):
|
||||
if len(user_message) == 0:
|
||||
return gr.update(interactive=True, placeholder='Input should not be empty!'), chatbot, chat_state
|
||||
chat.ask(user_message, chat_state)
|
||||
chatbot = chatbot + [[user_message, None]]
|
||||
return '', chatbot, chat_state
|
||||
|
||||
|
||||
def gradio_answer(chatbot, chat_state, img_list, num_beams, temperature):
|
||||
llm_message = chat.answer(conv=chat_state,
|
||||
img_list=img_list,
|
||||
num_beams=num_beams,
|
||||
temperature=temperature,
|
||||
max_new_tokens=300,
|
||||
max_length=2000)[0]
|
||||
chatbot[-1][1] = llm_message
|
||||
return chatbot, chat_state, img_list
|
||||
|
||||
title = """<h1 align="center">Demo of MiniGPT-4</h1>"""
|
||||
description = """<h3>This is the demo of MiniGPT-4. Upload your images and start chatting!</h3>"""
|
||||
article = """<p><a href='https://minigpt-4.github.io'><img src='https://img.shields.io/badge/Project-Page-Green'></a></p><p><a href='https://github.com/Vision-CAIR/MiniGPT-4'><img src='https://img.shields.io/badge/Github-Code-blue'></a></p><p><a href='https://raw.githubusercontent.com/Vision-CAIR/MiniGPT-4/main/MiniGPT_4.pdf'><img src='https://img.shields.io/badge/Paper-PDF-red'></a></p>
|
||||
"""
|
||||
|
||||
#TODO show examples below
|
||||
|
||||
with gr.Blocks() as demo:
|
||||
gr.Markdown(title)
|
||||
gr.Markdown(description)
|
||||
gr.Markdown(article)
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=0.5):
|
||||
image = gr.Image(type="pil")
|
||||
upload_button = gr.Button(value="Upload & Start Chat", interactive=True, variant="primary")
|
||||
clear = gr.Button("Restart")
|
||||
|
||||
num_beams = gr.Slider(
|
||||
minimum=1,
|
||||
maximum=10,
|
||||
value=1,
|
||||
step=1,
|
||||
interactive=True,
|
||||
label="beam search numbers)",
|
||||
)
|
||||
|
||||
temperature = gr.Slider(
|
||||
minimum=0.1,
|
||||
maximum=2.0,
|
||||
value=1.0,
|
||||
step=0.1,
|
||||
interactive=True,
|
||||
label="Temperature",
|
||||
)
|
||||
|
||||
with gr.Column():
|
||||
chat_state = gr.State()
|
||||
img_list = gr.State()
|
||||
chatbot = gr.Chatbot(label='MiniGPT-4')
|
||||
text_input = gr.Textbox(label='User', placeholder='Please upload your image first', interactive=False)
|
||||
|
||||
upload_button.click(upload_img, [image, text_input, chat_state], [image, text_input, upload_button, chat_state, img_list])
|
||||
|
||||
text_input.submit(gradio_ask, [text_input, chatbot, chat_state], [text_input, chatbot, chat_state]).then(
|
||||
gradio_answer, [chatbot, chat_state, img_list, num_beams, temperature], [chatbot, chat_state, img_list]
|
||||
)
|
||||
clear.click(gradio_reset, [chat_state, img_list], [chatbot, image, text_input, upload_button, chat_state, img_list], queue=False)
|
||||
|
||||
demo.launch(share=True, enable_queue=True)
|
63
models/MiniGPT4/environment.yml
Normal file
@@ -0,0 +1,63 @@
|
||||
name: minigpt4
|
||||
channels:
|
||||
- pytorch
|
||||
- defaults
|
||||
- anaconda
|
||||
dependencies:
|
||||
- python=3.9
|
||||
- cudatoolkit
|
||||
- pip
|
||||
- pytorch=1.12.1
|
||||
- pytorch-mutex=1.0=cuda
|
||||
- torchaudio=0.12.1
|
||||
- torchvision=0.13.1
|
||||
- pip:
|
||||
- accelerate==0.16.0
|
||||
- aiohttp==3.8.4
|
||||
- aiosignal==1.3.1
|
||||
- async-timeout==4.0.2
|
||||
- attrs==22.2.0
|
||||
- bitsandbytes==0.37.0
|
||||
- cchardet==2.1.7
|
||||
- chardet==5.1.0
|
||||
- contourpy==1.0.7
|
||||
- cycler==0.11.0
|
||||
- filelock==3.9.0
|
||||
- fonttools==4.38.0
|
||||
- frozenlist==1.3.3
|
||||
- huggingface-hub==0.13.4
|
||||
- importlib-resources==5.12.0
|
||||
- kiwisolver==1.4.4
|
||||
- matplotlib==3.7.0
|
||||
- multidict==6.0.4
|
||||
- openai==0.27.0
|
||||
- packaging==23.0
|
||||
- psutil==5.9.4
|
||||
- pycocotools==2.0.6
|
||||
- pyparsing==3.0.9
|
||||
- python-dateutil==2.8.2
|
||||
- pyyaml==6.0
|
||||
- regex==2022.10.31
|
||||
- tokenizers==0.13.2
|
||||
- tqdm==4.64.1
|
||||
- transformers==4.28.0
|
||||
- timm==0.6.13
|
||||
- spacy==3.5.1
|
||||
- webdataset==0.2.48
|
||||
- scikit-learn==1.2.2
|
||||
- scipy==1.10.1
|
||||
- yarl==1.8.2
|
||||
- zipp==3.14.0
|
||||
- omegaconf==2.3.0
|
||||
- opencv-python==4.7.0.72
|
||||
- iopath==0.1.10
|
||||
- decord==0.6.0
|
||||
- tenacity==8.2.2
|
||||
- peft
|
||||
- pycocoevalcap
|
||||
- sentence-transformers
|
||||
- umap-learn
|
||||
- notebook
|
||||
- gradio==3.24.1
|
||||
- gradio-client==0.0.8
|
||||
- wandb
|
25
models/MiniGPT4/eval_configs/minigpt4_eval.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
model:
|
||||
arch: mini_gpt4
|
||||
model_type: pretrain_vicuna
|
||||
freeze_vit: True
|
||||
freeze_qformer: True
|
||||
max_txt_len: 160
|
||||
end_sym: "###"
|
||||
low_resource: True
|
||||
prompt_path: "./models/MiniGPT4/prompts/alignment.txt"
|
||||
prompt_template: '###Human: {} ###Assistant: '
|
||||
ckpt: './models/MiniGPT4/model_weight/projection/pretrained_minigpt4.pth'
|
||||
|
||||
|
||||
datasets:
|
||||
cc_sbu_align:
|
||||
vis_processor:
|
||||
train:
|
||||
name: "blip2_image_eval"
|
||||
image_size: 224
|
||||
text_processor:
|
||||
train:
|
||||
name: "blip_caption"
|
||||
|
||||
run:
|
||||
task: image_text_pretrain
|
BIN
models/MiniGPT4/examples/ad_1.png
Normal file
After Width: | Height: | Size: 380 KiB |
BIN
models/MiniGPT4/examples/ad_2.png
Normal file
After Width: | Height: | Size: 457 KiB |
BIN
models/MiniGPT4/examples/cook_1.png
Normal file
After Width: | Height: | Size: 538 KiB |
BIN
models/MiniGPT4/examples/cook_2.png
Normal file
After Width: | Height: | Size: 586 KiB |
BIN
models/MiniGPT4/examples/describe_1.png
Normal file
After Width: | Height: | Size: 679 KiB |
BIN
models/MiniGPT4/examples/describe_2.png
Normal file
After Width: | Height: | Size: 555 KiB |
BIN
models/MiniGPT4/examples/fact_1.png
Normal file
After Width: | Height: | Size: 468 KiB |
BIN
models/MiniGPT4/examples/fact_2.png
Normal file
After Width: | Height: | Size: 658 KiB |
BIN
models/MiniGPT4/examples/fix_1.png
Normal file
After Width: | Height: | Size: 690 KiB |
BIN
models/MiniGPT4/examples/fix_2.png
Normal file
After Width: | Height: | Size: 586 KiB |
BIN
models/MiniGPT4/examples/fun_1.png
Normal file
After Width: | Height: | Size: 713 KiB |
BIN
models/MiniGPT4/examples/fun_2.png
Normal file
After Width: | Height: | Size: 597 KiB |
BIN
models/MiniGPT4/examples/logo_1.png
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
models/MiniGPT4/examples/op_1.png
Normal file
After Width: | Height: | Size: 603 KiB |
BIN
models/MiniGPT4/examples/op_2.png
Normal file
After Width: | Height: | Size: 634 KiB |
BIN
models/MiniGPT4/examples/people_1.png
Normal file
After Width: | Height: | Size: 249 KiB |
BIN
models/MiniGPT4/examples/people_2.png
Normal file
After Width: | Height: | Size: 305 KiB |
BIN
models/MiniGPT4/examples/rhyme_1.png
Normal file
After Width: | Height: | Size: 588 KiB |
BIN
models/MiniGPT4/examples/rhyme_2.png
Normal file
After Width: | Height: | Size: 805 KiB |
BIN
models/MiniGPT4/examples/story_1.png
Normal file
After Width: | Height: | Size: 853 KiB |
BIN
models/MiniGPT4/examples/story_2.png
Normal file
After Width: | Height: | Size: 567 KiB |
BIN
models/MiniGPT4/examples/web_1.png
Normal file
After Width: | Height: | Size: 712 KiB |
BIN
models/MiniGPT4/examples/wop_1.png
Normal file
After Width: | Height: | Size: 519 KiB |
BIN
models/MiniGPT4/examples/wop_2.png
Normal file
After Width: | Height: | Size: 565 KiB |
BIN
models/MiniGPT4/figs/examples/ad_1.png
Normal file
After Width: | Height: | Size: 380 KiB |
BIN
models/MiniGPT4/figs/examples/ad_2.png
Normal file
After Width: | Height: | Size: 457 KiB |
BIN
models/MiniGPT4/figs/examples/cook_1.png
Normal file
After Width: | Height: | Size: 538 KiB |
BIN
models/MiniGPT4/figs/examples/cook_2.png
Normal file
After Width: | Height: | Size: 586 KiB |
BIN
models/MiniGPT4/figs/examples/describe_1.png
Normal file
After Width: | Height: | Size: 679 KiB |
BIN
models/MiniGPT4/figs/examples/describe_2.png
Normal file
After Width: | Height: | Size: 555 KiB |
BIN
models/MiniGPT4/figs/examples/fact_1.png
Normal file
After Width: | Height: | Size: 468 KiB |
BIN
models/MiniGPT4/figs/examples/fact_2.png
Normal file
After Width: | Height: | Size: 658 KiB |
BIN
models/MiniGPT4/figs/examples/fix_1.png
Normal file
After Width: | Height: | Size: 690 KiB |
BIN
models/MiniGPT4/figs/examples/fix_2.png
Normal file
After Width: | Height: | Size: 586 KiB |
BIN
models/MiniGPT4/figs/examples/fun_1.png
Normal file
After Width: | Height: | Size: 713 KiB |
BIN
models/MiniGPT4/figs/examples/fun_2.png
Normal file
After Width: | Height: | Size: 597 KiB |
BIN
models/MiniGPT4/figs/examples/logo_1.png
Normal file
After Width: | Height: | Size: 190 KiB |
BIN
models/MiniGPT4/figs/examples/op_1.png
Normal file
After Width: | Height: | Size: 603 KiB |
BIN
models/MiniGPT4/figs/examples/op_2.png
Normal file
After Width: | Height: | Size: 634 KiB |
BIN
models/MiniGPT4/figs/examples/people_1.png
Normal file
After Width: | Height: | Size: 249 KiB |
BIN
models/MiniGPT4/figs/examples/people_2.png
Normal file
After Width: | Height: | Size: 305 KiB |
BIN
models/MiniGPT4/figs/examples/rhyme_1.png
Normal file
After Width: | Height: | Size: 588 KiB |
BIN
models/MiniGPT4/figs/examples/rhyme_2.png
Normal file
After Width: | Height: | Size: 805 KiB |
BIN
models/MiniGPT4/figs/examples/story_1.png
Normal file
After Width: | Height: | Size: 853 KiB |
BIN
models/MiniGPT4/figs/examples/story_2.png
Normal file
After Width: | Height: | Size: 567 KiB |
BIN
models/MiniGPT4/figs/examples/web_1.png
Normal file
After Width: | Height: | Size: 712 KiB |
BIN
models/MiniGPT4/figs/examples/wop_1.png
Normal file
After Width: | Height: | Size: 519 KiB |
BIN
models/MiniGPT4/figs/examples/wop_2.png
Normal file
After Width: | Height: | Size: 565 KiB |
BIN
models/MiniGPT4/figs/online_demo.png
Normal file
After Width: | Height: | Size: 1.2 MiB |
BIN
models/MiniGPT4/figs/overview.png
Normal file
After Width: | Height: | Size: 2.4 MiB |
30
models/MiniGPT4/minigpt4/__init__.py
Normal file
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from omegaconf import OmegaConf
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
from minigpt4.datasets.builders import *
|
||||
from minigpt4.models import *
|
||||
from minigpt4.processors import *
|
||||
from minigpt4.tasks import *
|
||||
|
||||
|
||||
root_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
default_cfg = OmegaConf.load(os.path.join(root_dir, "configs/default.yaml"))
|
||||
|
||||
registry.register_path("library_root", root_dir)
|
||||
repo_root = os.path.join(root_dir, "..")
|
||||
registry.register_path("repo_root", repo_root)
|
||||
cache_root = os.path.join(repo_root, default_cfg.env.cache_root)
|
||||
registry.register_path("cache_root", cache_root)
|
||||
|
||||
registry.register("MAX_INT", sys.maxsize)
|
||||
registry.register("SPLIT_NAMES", ["train", "val", "test"])
|
BIN
models/MiniGPT4/minigpt4/__pycache__/__init__.cpython-310.pyc
Normal file
0
models/MiniGPT4/minigpt4/common/__init__.py
Normal file
468
models/MiniGPT4/minigpt4/common/config.py
Normal file
@@ -0,0 +1,468 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import logging
|
||||
import json
|
||||
from typing import Dict
|
||||
|
||||
from omegaconf import OmegaConf
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, args):
|
||||
self.config = {}
|
||||
|
||||
self.args = args
|
||||
|
||||
# Register the config and configuration for setup
|
||||
registry.register("configuration", self)
|
||||
|
||||
user_config = self._build_opt_list(self.args.options)
|
||||
|
||||
config = OmegaConf.load(self.args.cfg_path)
|
||||
|
||||
runner_config = self.build_runner_config(config)
|
||||
model_config = self.build_model_config(config, **user_config)
|
||||
dataset_config = self.build_dataset_config(config)
|
||||
|
||||
# Validate the user-provided runner configuration
|
||||
# model and dataset configuration are supposed to be validated by the respective classes
|
||||
# [TODO] validate the model/dataset configuration
|
||||
# self._validate_runner_config(runner_config)
|
||||
|
||||
# Override the default configuration with user options.
|
||||
self.config = OmegaConf.merge(
|
||||
runner_config, model_config, dataset_config, user_config
|
||||
)
|
||||
|
||||
def _validate_runner_config(self, runner_config):
|
||||
"""
|
||||
This method validates the configuration, such that
|
||||
1) all the user specified options are valid;
|
||||
2) no type mismatches between the user specified options and the config.
|
||||
"""
|
||||
runner_config_validator = create_runner_config_validator()
|
||||
runner_config_validator.validate(runner_config)
|
||||
|
||||
def _build_opt_list(self, opts):
|
||||
opts_dot_list = self._convert_to_dot_list(opts)
|
||||
return OmegaConf.from_dotlist(opts_dot_list)
|
||||
|
||||
@staticmethod
|
||||
def build_model_config(config, **kwargs):
|
||||
model = config.get("model", None)
|
||||
assert model is not None, "Missing model configuration file."
|
||||
|
||||
model_cls = registry.get_model_class(model.arch)
|
||||
assert model_cls is not None, f"Model '{model.arch}' has not been registered."
|
||||
|
||||
model_type = kwargs.get("model.model_type", None)
|
||||
if not model_type:
|
||||
model_type = model.get("model_type", None)
|
||||
# else use the model type selected by user.
|
||||
|
||||
assert model_type is not None, "Missing model_type."
|
||||
|
||||
model_config_path = model_cls.default_config_path(model_type=model_type)
|
||||
|
||||
model_config = OmegaConf.create()
|
||||
# hierarchy override, customized config > default config
|
||||
model_config = OmegaConf.merge(
|
||||
model_config,
|
||||
OmegaConf.load(model_config_path),
|
||||
{"model": config["model"]},
|
||||
)
|
||||
|
||||
return model_config
|
||||
|
||||
@staticmethod
|
||||
def build_runner_config(config):
|
||||
return {"run": config.run}
|
||||
|
||||
@staticmethod
|
||||
def build_dataset_config(config):
|
||||
datasets = config.get("datasets", None)
|
||||
if datasets is None:
|
||||
raise KeyError(
|
||||
"Expecting 'datasets' as the root key for dataset configuration."
|
||||
)
|
||||
|
||||
dataset_config = OmegaConf.create()
|
||||
|
||||
for dataset_name in datasets:
|
||||
builder_cls = registry.get_builder_class(dataset_name)
|
||||
|
||||
dataset_config_type = datasets[dataset_name].get("type", "default")
|
||||
dataset_config_path = builder_cls.default_config_path(
|
||||
type=dataset_config_type
|
||||
)
|
||||
|
||||
# hierarchy override, customized config > default config
|
||||
dataset_config = OmegaConf.merge(
|
||||
dataset_config,
|
||||
OmegaConf.load(dataset_config_path),
|
||||
{"datasets": {dataset_name: config["datasets"][dataset_name]}},
|
||||
)
|
||||
|
||||
return dataset_config
|
||||
|
||||
def _convert_to_dot_list(self, opts):
|
||||
if opts is None:
|
||||
opts = []
|
||||
|
||||
if len(opts) == 0:
|
||||
return opts
|
||||
|
||||
has_equal = opts[0].find("=") != -1
|
||||
|
||||
if has_equal:
|
||||
return opts
|
||||
|
||||
return [(opt + "=" + value) for opt, value in zip(opts[0::2], opts[1::2])]
|
||||
|
||||
def get_config(self):
|
||||
return self.config
|
||||
|
||||
@property
|
||||
def run_cfg(self):
|
||||
return self.config.run
|
||||
|
||||
@property
|
||||
def datasets_cfg(self):
|
||||
return self.config.datasets
|
||||
|
||||
@property
|
||||
def model_cfg(self):
|
||||
return self.config.model
|
||||
|
||||
def pretty_print(self):
|
||||
logging.info("\n===== Running Parameters =====")
|
||||
logging.info(self._convert_node_to_json(self.config.run))
|
||||
|
||||
logging.info("\n====== Dataset Attributes ======")
|
||||
datasets = self.config.datasets
|
||||
|
||||
for dataset in datasets:
|
||||
if dataset in self.config.datasets:
|
||||
logging.info(f"\n======== {dataset} =======")
|
||||
dataset_config = self.config.datasets[dataset]
|
||||
logging.info(self._convert_node_to_json(dataset_config))
|
||||
else:
|
||||
logging.warning(f"No dataset named '{dataset}' in config. Skipping")
|
||||
|
||||
logging.info(f"\n====== Model Attributes ======")
|
||||
logging.info(self._convert_node_to_json(self.config.model))
|
||||
|
||||
def _convert_node_to_json(self, node):
|
||||
container = OmegaConf.to_container(node, resolve=True)
|
||||
return json.dumps(container, indent=4, sort_keys=True)
|
||||
|
||||
def to_dict(self):
|
||||
return OmegaConf.to_container(self.config)
|
||||
|
||||
|
||||
def node_to_dict(node):
|
||||
return OmegaConf.to_container(node)
|
||||
|
||||
|
||||
class ConfigValidator:
|
||||
"""
|
||||
This is a preliminary implementation to centralize and validate the configuration.
|
||||
May be altered in the future.
|
||||
|
||||
A helper class to validate configurations from yaml file.
|
||||
|
||||
This serves the following purposes:
|
||||
1. Ensure all the options in the yaml are defined, raise error if not.
|
||||
2. when type mismatches are found, the validator will raise an error.
|
||||
3. a central place to store and display helpful messages for supported configurations.
|
||||
|
||||
"""
|
||||
|
||||
class _Argument:
|
||||
def __init__(self, name, choices=None, type=None, help=None):
|
||||
self.name = name
|
||||
self.val = None
|
||||
self.choices = choices
|
||||
self.type = type
|
||||
self.help = help
|
||||
|
||||
def __str__(self):
|
||||
s = f"{self.name}={self.val}"
|
||||
if self.type is not None:
|
||||
s += f", ({self.type})"
|
||||
if self.choices is not None:
|
||||
s += f", choices: {self.choices}"
|
||||
if self.help is not None:
|
||||
s += f", ({self.help})"
|
||||
return s
|
||||
|
||||
def __init__(self, description):
|
||||
self.description = description
|
||||
|
||||
self.arguments = dict()
|
||||
|
||||
self.parsed_args = None
|
||||
|
||||
def __getitem__(self, key):
|
||||
assert self.parsed_args is not None, "No arguments parsed yet."
|
||||
|
||||
return self.parsed_args[key]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.format_help()
|
||||
|
||||
def add_argument(self, *args, **kwargs):
|
||||
"""
|
||||
Assume the first argument is the name of the argument.
|
||||
"""
|
||||
self.arguments[args[0]] = self._Argument(*args, **kwargs)
|
||||
|
||||
def validate(self, config=None):
|
||||
"""
|
||||
Convert yaml config (dict-like) to list, required by argparse.
|
||||
"""
|
||||
for k, v in config.items():
|
||||
assert (
|
||||
k in self.arguments
|
||||
), f"""{k} is not a valid argument. Support arguments are {self.format_arguments()}."""
|
||||
|
||||
if self.arguments[k].type is not None:
|
||||
try:
|
||||
self.arguments[k].val = self.arguments[k].type(v)
|
||||
except ValueError:
|
||||
raise ValueError(f"{k} is not a valid {self.arguments[k].type}.")
|
||||
|
||||
if self.arguments[k].choices is not None:
|
||||
assert (
|
||||
v in self.arguments[k].choices
|
||||
), f"""{k} must be one of {self.arguments[k].choices}."""
|
||||
|
||||
return config
|
||||
|
||||
def format_arguments(self):
|
||||
return str([f"{k}" for k in sorted(self.arguments.keys())])
|
||||
|
||||
def format_help(self):
|
||||
# description + key-value pair string for each argument
|
||||
help_msg = str(self.description)
|
||||
return help_msg + ", available arguments: " + self.format_arguments()
|
||||
|
||||
def print_help(self):
|
||||
# display help message
|
||||
print(self.format_help())
|
||||
|
||||
|
||||
def create_runner_config_validator():
|
||||
validator = ConfigValidator(description="Runner configurations")
|
||||
|
||||
validator.add_argument(
|
||||
"runner",
|
||||
type=str,
|
||||
choices=["runner_base", "runner_iter"],
|
||||
help="""Runner to use. The "runner_base" uses epoch-based training while iter-based
|
||||
runner runs based on iters. Default: runner_base""",
|
||||
)
|
||||
# add argumetns for training dataset ratios
|
||||
validator.add_argument(
|
||||
"train_dataset_ratios",
|
||||
type=Dict[str, float],
|
||||
help="""Ratios of training dataset. This is used in iteration-based runner.
|
||||
Do not support for epoch-based runner because how to define an epoch becomes tricky.
|
||||
Default: None""",
|
||||
)
|
||||
validator.add_argument(
|
||||
"max_iters",
|
||||
type=float,
|
||||
help="Maximum number of iterations to run.",
|
||||
)
|
||||
validator.add_argument(
|
||||
"max_epoch",
|
||||
type=int,
|
||||
help="Maximum number of epochs to run.",
|
||||
)
|
||||
# add arguments for iters_per_inner_epoch
|
||||
validator.add_argument(
|
||||
"iters_per_inner_epoch",
|
||||
type=float,
|
||||
help="Number of iterations per inner epoch. This is required when runner is runner_iter.",
|
||||
)
|
||||
lr_scheds_choices = registry.list_lr_schedulers()
|
||||
validator.add_argument(
|
||||
"lr_sched",
|
||||
type=str,
|
||||
choices=lr_scheds_choices,
|
||||
help="Learning rate scheduler to use, from {}".format(lr_scheds_choices),
|
||||
)
|
||||
task_choices = registry.list_tasks()
|
||||
validator.add_argument(
|
||||
"task",
|
||||
type=str,
|
||||
choices=task_choices,
|
||||
help="Task to use, from {}".format(task_choices),
|
||||
)
|
||||
# add arguments for init_lr
|
||||
validator.add_argument(
|
||||
"init_lr",
|
||||
type=float,
|
||||
help="Initial learning rate. This will be the learning rate after warmup and before decay.",
|
||||
)
|
||||
# add arguments for min_lr
|
||||
validator.add_argument(
|
||||
"min_lr",
|
||||
type=float,
|
||||
help="Minimum learning rate (after decay).",
|
||||
)
|
||||
# add arguments for warmup_lr
|
||||
validator.add_argument(
|
||||
"warmup_lr",
|
||||
type=float,
|
||||
help="Starting learning rate for warmup.",
|
||||
)
|
||||
# add arguments for learning rate decay rate
|
||||
validator.add_argument(
|
||||
"lr_decay_rate",
|
||||
type=float,
|
||||
help="Learning rate decay rate. Required if using a decaying learning rate scheduler.",
|
||||
)
|
||||
# add arguments for weight decay
|
||||
validator.add_argument(
|
||||
"weight_decay",
|
||||
type=float,
|
||||
help="Weight decay rate.",
|
||||
)
|
||||
# add arguments for training batch size
|
||||
validator.add_argument(
|
||||
"batch_size_train",
|
||||
type=int,
|
||||
help="Training batch size.",
|
||||
)
|
||||
# add arguments for evaluation batch size
|
||||
validator.add_argument(
|
||||
"batch_size_eval",
|
||||
type=int,
|
||||
help="Evaluation batch size, including validation and testing.",
|
||||
)
|
||||
# add arguments for number of workers for data loading
|
||||
validator.add_argument(
|
||||
"num_workers",
|
||||
help="Number of workers for data loading.",
|
||||
)
|
||||
# add arguments for warm up steps
|
||||
validator.add_argument(
|
||||
"warmup_steps",
|
||||
type=int,
|
||||
help="Number of warmup steps. Required if a warmup schedule is used.",
|
||||
)
|
||||
# add arguments for random seed
|
||||
validator.add_argument(
|
||||
"seed",
|
||||
type=int,
|
||||
help="Random seed.",
|
||||
)
|
||||
# add arguments for output directory
|
||||
validator.add_argument(
|
||||
"output_dir",
|
||||
type=str,
|
||||
help="Output directory to save checkpoints and logs.",
|
||||
)
|
||||
# add arguments for whether only use evaluation
|
||||
validator.add_argument(
|
||||
"evaluate",
|
||||
help="Whether to only evaluate the model. If true, training will not be performed.",
|
||||
)
|
||||
# add arguments for splits used for training, e.g. ["train", "val"]
|
||||
validator.add_argument(
|
||||
"train_splits",
|
||||
type=list,
|
||||
help="Splits to use for training.",
|
||||
)
|
||||
# add arguments for splits used for validation, e.g. ["val"]
|
||||
validator.add_argument(
|
||||
"valid_splits",
|
||||
type=list,
|
||||
help="Splits to use for validation. If not provided, will skip the validation.",
|
||||
)
|
||||
# add arguments for splits used for testing, e.g. ["test"]
|
||||
validator.add_argument(
|
||||
"test_splits",
|
||||
type=list,
|
||||
help="Splits to use for testing. If not provided, will skip the testing.",
|
||||
)
|
||||
# add arguments for accumulating gradient for iterations
|
||||
validator.add_argument(
|
||||
"accum_grad_iters",
|
||||
type=int,
|
||||
help="Number of iterations to accumulate gradient for.",
|
||||
)
|
||||
|
||||
# ====== distributed training ======
|
||||
validator.add_argument(
|
||||
"device",
|
||||
type=str,
|
||||
choices=["cpu", "cuda"],
|
||||
help="Device to use. Support 'cuda' or 'cpu' as for now.",
|
||||
)
|
||||
validator.add_argument(
|
||||
"world_size",
|
||||
type=int,
|
||||
help="Number of processes participating in the job.",
|
||||
)
|
||||
validator.add_argument("dist_url", type=str)
|
||||
validator.add_argument("distributed", type=bool)
|
||||
# add arguments to opt using distributed sampler during evaluation or not
|
||||
validator.add_argument(
|
||||
"use_dist_eval_sampler",
|
||||
type=bool,
|
||||
help="Whether to use distributed sampler during evaluation or not.",
|
||||
)
|
||||
|
||||
# ====== task specific ======
|
||||
# generation task specific arguments
|
||||
# add arguments for maximal length of text output
|
||||
validator.add_argument(
|
||||
"max_len",
|
||||
type=int,
|
||||
help="Maximal length of text output.",
|
||||
)
|
||||
# add arguments for minimal length of text output
|
||||
validator.add_argument(
|
||||
"min_len",
|
||||
type=int,
|
||||
help="Minimal length of text output.",
|
||||
)
|
||||
# add arguments number of beams
|
||||
validator.add_argument(
|
||||
"num_beams",
|
||||
type=int,
|
||||
help="Number of beams used for beam search.",
|
||||
)
|
||||
|
||||
# vqa task specific arguments
|
||||
# add arguments for number of answer candidates
|
||||
validator.add_argument(
|
||||
"num_ans_candidates",
|
||||
type=int,
|
||||
help="""For ALBEF and BLIP, these models first rank answers according to likelihood to select answer candidates.""",
|
||||
)
|
||||
# add arguments for inference method
|
||||
validator.add_argument(
|
||||
"inference_method",
|
||||
type=str,
|
||||
choices=["genearte", "rank"],
|
||||
help="""Inference method to use for question answering. If rank, requires a answer list.""",
|
||||
)
|
||||
|
||||
# ====== model specific ======
|
||||
validator.add_argument(
|
||||
"k_test",
|
||||
type=int,
|
||||
help="Number of top k most similar samples from ITC/VTC selection to be tested.",
|
||||
)
|
||||
|
||||
return validator
|
137
models/MiniGPT4/minigpt4/common/dist_utils.py
Normal file
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import functools
|
||||
import os
|
||||
|
||||
import torch
|
||||
import torch.distributed as dist
|
||||
import timm.models.hub as timm_hub
|
||||
|
||||
|
||||
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 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 {}, world {}): {}".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,
|
||||
timeout=datetime.timedelta(
|
||||
days=365
|
||||
), # allow auto-downloading and de-compressing
|
||||
)
|
||||
torch.distributed.barrier()
|
||||
setup_for_distributed(args.rank == 0)
|
||||
|
||||
|
||||
def get_dist_info():
|
||||
if torch.__version__ < "1.0":
|
||||
initialized = dist._initialized
|
||||
else:
|
||||
initialized = dist.is_initialized()
|
||||
if initialized:
|
||||
rank = dist.get_rank()
|
||||
world_size = dist.get_world_size()
|
||||
else: # non-distributed training
|
||||
rank = 0
|
||||
world_size = 1
|
||||
return rank, world_size
|
||||
|
||||
|
||||
def main_process(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
rank, _ = get_dist_info()
|
||||
if rank == 0:
|
||||
return func(*args, **kwargs)
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def download_cached_file(url, check_hash=True, progress=False):
|
||||
"""
|
||||
Download a file from a URL and cache it locally. If the file already exists, it is not downloaded again.
|
||||
If distributed, only the main process downloads the file, and the other processes wait for the file to be downloaded.
|
||||
"""
|
||||
|
||||
def get_cached_file_path():
|
||||
# a hack to sync the file path across processes
|
||||
parts = torch.hub.urlparse(url)
|
||||
filename = os.path.basename(parts.path)
|
||||
cached_file = os.path.join(timm_hub.get_cache_dir(), filename)
|
||||
|
||||
return cached_file
|
||||
|
||||
if is_main_process():
|
||||
timm_hub.download_cached_file(url, check_hash, progress)
|
||||
|
||||
if is_dist_avail_and_initialized():
|
||||
dist.barrier()
|
||||
|
||||
return get_cached_file_path()
|
24
models/MiniGPT4/minigpt4/common/gradcam.py
Normal file
@@ -0,0 +1,24 @@
|
||||
import numpy as np
|
||||
from matplotlib import pyplot as plt
|
||||
from scipy.ndimage import filters
|
||||
from skimage import transform as skimage_transform
|
||||
|
||||
|
||||
def getAttMap(img, attMap, blur=True, overlap=True):
|
||||
attMap -= attMap.min()
|
||||
if attMap.max() > 0:
|
||||
attMap /= attMap.max()
|
||||
attMap = skimage_transform.resize(attMap, (img.shape[:2]), order=3, mode="constant")
|
||||
if blur:
|
||||
attMap = filters.gaussian_filter(attMap, 0.02 * max(img.shape[:2]))
|
||||
attMap -= attMap.min()
|
||||
attMap /= attMap.max()
|
||||
cmap = plt.get_cmap("jet")
|
||||
attMapV = cmap(attMap)
|
||||
attMapV = np.delete(attMapV, 3, 2)
|
||||
if overlap:
|
||||
attMap = (
|
||||
1 * (1 - attMap**0.7).reshape(attMap.shape + (1,)) * img
|
||||
+ (attMap**0.7).reshape(attMap.shape + (1,)) * attMapV
|
||||
)
|
||||
return attMap
|
195
models/MiniGPT4/minigpt4/common/logger.py
Normal file
@@ -0,0 +1,195 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import logging
|
||||
import time
|
||||
from collections import defaultdict, deque
|
||||
|
||||
import torch
|
||||
import torch.distributed as dist
|
||||
|
||||
from minigpt4.common import dist_utils
|
||||
|
||||
|
||||
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 dist_utils.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 setup_logger():
|
||||
logging.basicConfig(
|
||||
level=logging.INFO if dist_utils.is_main_process() else logging.WARN,
|
||||
format="%(asctime)s [%(levelname)s] %(message)s",
|
||||
handlers=[logging.StreamHandler()],
|
||||
)
|
119
models/MiniGPT4/minigpt4/common/optims.py
Normal file
@@ -0,0 +1,119 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
|
||||
@registry.register_lr_scheduler("linear_warmup_step_lr")
|
||||
class LinearWarmupStepLRScheduler:
|
||||
def __init__(
|
||||
self,
|
||||
optimizer,
|
||||
max_epoch,
|
||||
min_lr,
|
||||
init_lr,
|
||||
decay_rate=1,
|
||||
warmup_start_lr=-1,
|
||||
warmup_steps=0,
|
||||
**kwargs
|
||||
):
|
||||
self.optimizer = optimizer
|
||||
|
||||
self.max_epoch = max_epoch
|
||||
self.min_lr = min_lr
|
||||
|
||||
self.decay_rate = decay_rate
|
||||
|
||||
self.init_lr = init_lr
|
||||
self.warmup_steps = warmup_steps
|
||||
self.warmup_start_lr = warmup_start_lr if warmup_start_lr >= 0 else init_lr
|
||||
|
||||
def step(self, cur_epoch, cur_step):
|
||||
if cur_epoch == 0:
|
||||
warmup_lr_schedule(
|
||||
step=cur_step,
|
||||
optimizer=self.optimizer,
|
||||
max_step=self.warmup_steps,
|
||||
init_lr=self.warmup_start_lr,
|
||||
max_lr=self.init_lr,
|
||||
)
|
||||
else:
|
||||
step_lr_schedule(
|
||||
epoch=cur_epoch,
|
||||
optimizer=self.optimizer,
|
||||
init_lr=self.init_lr,
|
||||
min_lr=self.min_lr,
|
||||
decay_rate=self.decay_rate,
|
||||
)
|
||||
|
||||
|
||||
@registry.register_lr_scheduler("linear_warmup_cosine_lr")
|
||||
class LinearWarmupCosineLRScheduler:
|
||||
def __init__(
|
||||
self,
|
||||
optimizer,
|
||||
max_epoch,
|
||||
iters_per_epoch,
|
||||
min_lr,
|
||||
init_lr,
|
||||
warmup_steps=0,
|
||||
warmup_start_lr=-1,
|
||||
**kwargs
|
||||
):
|
||||
self.optimizer = optimizer
|
||||
|
||||
self.max_epoch = max_epoch
|
||||
self.iters_per_epoch = iters_per_epoch
|
||||
self.min_lr = min_lr
|
||||
|
||||
self.init_lr = init_lr
|
||||
self.warmup_steps = warmup_steps
|
||||
self.warmup_start_lr = warmup_start_lr if warmup_start_lr >= 0 else init_lr
|
||||
|
||||
def step(self, cur_epoch, cur_step):
|
||||
total_cur_step = cur_epoch * self.iters_per_epoch + cur_step
|
||||
if total_cur_step < self.warmup_steps:
|
||||
warmup_lr_schedule(
|
||||
step=cur_step,
|
||||
optimizer=self.optimizer,
|
||||
max_step=self.warmup_steps,
|
||||
init_lr=self.warmup_start_lr,
|
||||
max_lr=self.init_lr,
|
||||
)
|
||||
else:
|
||||
cosine_lr_schedule(
|
||||
epoch=total_cur_step,
|
||||
optimizer=self.optimizer,
|
||||
max_epoch=self.max_epoch * self.iters_per_epoch,
|
||||
init_lr=self.init_lr,
|
||||
min_lr=self.min_lr,
|
||||
)
|
||||
|
||||
|
||||
def cosine_lr_schedule(optimizer, epoch, max_epoch, init_lr, min_lr):
|
||||
"""Decay the learning rate"""
|
||||
lr = (init_lr - min_lr) * 0.5 * (
|
||||
1.0 + 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(max_step, 1))
|
||||
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
|
329
models/MiniGPT4/minigpt4/common/registry.py
Normal file
@@ -0,0 +1,329 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
|
||||
class Registry:
|
||||
mapping = {
|
||||
"builder_name_mapping": {},
|
||||
"task_name_mapping": {},
|
||||
"processor_name_mapping": {},
|
||||
"model_name_mapping": {},
|
||||
"lr_scheduler_name_mapping": {},
|
||||
"runner_name_mapping": {},
|
||||
"state": {},
|
||||
"paths": {},
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def register_builder(cls, name):
|
||||
r"""Register a dataset builder to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the builder will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
from minigpt4.datasets.base_dataset_builder import BaseDatasetBuilder
|
||||
"""
|
||||
|
||||
def wrap(builder_cls):
|
||||
from minigpt4.datasets.builders.base_dataset_builder import BaseDatasetBuilder
|
||||
|
||||
assert issubclass(
|
||||
builder_cls, BaseDatasetBuilder
|
||||
), "All builders must inherit BaseDatasetBuilder class, found {}".format(
|
||||
builder_cls
|
||||
)
|
||||
if name in cls.mapping["builder_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["builder_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["builder_name_mapping"][name] = builder_cls
|
||||
return builder_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_task(cls, name):
|
||||
r"""Register a task to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the task will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
|
||||
def wrap(task_cls):
|
||||
from minigpt4.tasks.base_task import BaseTask
|
||||
|
||||
assert issubclass(
|
||||
task_cls, BaseTask
|
||||
), "All tasks must inherit BaseTask class"
|
||||
if name in cls.mapping["task_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["task_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["task_name_mapping"][name] = task_cls
|
||||
return task_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_model(cls, name):
|
||||
r"""Register a task to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the task will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
|
||||
def wrap(model_cls):
|
||||
from minigpt4.models import BaseModel
|
||||
|
||||
assert issubclass(
|
||||
model_cls, BaseModel
|
||||
), "All models must inherit BaseModel class"
|
||||
if name in cls.mapping["model_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["model_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["model_name_mapping"][name] = model_cls
|
||||
return model_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_processor(cls, name):
|
||||
r"""Register a processor to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the task will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
|
||||
def wrap(processor_cls):
|
||||
from minigpt4.processors import BaseProcessor
|
||||
|
||||
assert issubclass(
|
||||
processor_cls, BaseProcessor
|
||||
), "All processors must inherit BaseProcessor class"
|
||||
if name in cls.mapping["processor_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["processor_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["processor_name_mapping"][name] = processor_cls
|
||||
return processor_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_lr_scheduler(cls, name):
|
||||
r"""Register a model to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the task will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
|
||||
def wrap(lr_sched_cls):
|
||||
if name in cls.mapping["lr_scheduler_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["lr_scheduler_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["lr_scheduler_name_mapping"][name] = lr_sched_cls
|
||||
return lr_sched_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_runner(cls, name):
|
||||
r"""Register a model to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the task will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
|
||||
def wrap(runner_cls):
|
||||
if name in cls.mapping["runner_name_mapping"]:
|
||||
raise KeyError(
|
||||
"Name '{}' already registered for {}.".format(
|
||||
name, cls.mapping["runner_name_mapping"][name]
|
||||
)
|
||||
)
|
||||
cls.mapping["runner_name_mapping"][name] = runner_cls
|
||||
return runner_cls
|
||||
|
||||
return wrap
|
||||
|
||||
@classmethod
|
||||
def register_path(cls, name, path):
|
||||
r"""Register a path to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the path will be registered.
|
||||
|
||||
Usage:
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
"""
|
||||
assert isinstance(path, str), "All path must be str."
|
||||
if name in cls.mapping["paths"]:
|
||||
raise KeyError("Name '{}' already registered.".format(name))
|
||||
cls.mapping["paths"][name] = path
|
||||
|
||||
@classmethod
|
||||
def register(cls, name, obj):
|
||||
r"""Register an item to registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key with which the item will be registered.
|
||||
|
||||
Usage::
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
registry.register("config", {})
|
||||
"""
|
||||
path = name.split(".")
|
||||
current = cls.mapping["state"]
|
||||
|
||||
for part in path[:-1]:
|
||||
if part not in current:
|
||||
current[part] = {}
|
||||
current = current[part]
|
||||
|
||||
current[path[-1]] = obj
|
||||
|
||||
# @classmethod
|
||||
# def get_trainer_class(cls, name):
|
||||
# return cls.mapping["trainer_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_builder_class(cls, name):
|
||||
return cls.mapping["builder_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_model_class(cls, name):
|
||||
return cls.mapping["model_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_task_class(cls, name):
|
||||
return cls.mapping["task_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_processor_class(cls, name):
|
||||
return cls.mapping["processor_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_lr_scheduler_class(cls, name):
|
||||
return cls.mapping["lr_scheduler_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get_runner_class(cls, name):
|
||||
return cls.mapping["runner_name_mapping"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def list_runners(cls):
|
||||
return sorted(cls.mapping["runner_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def list_models(cls):
|
||||
return sorted(cls.mapping["model_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def list_tasks(cls):
|
||||
return sorted(cls.mapping["task_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def list_processors(cls):
|
||||
return sorted(cls.mapping["processor_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def list_lr_schedulers(cls):
|
||||
return sorted(cls.mapping["lr_scheduler_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def list_datasets(cls):
|
||||
return sorted(cls.mapping["builder_name_mapping"].keys())
|
||||
|
||||
@classmethod
|
||||
def get_path(cls, name):
|
||||
return cls.mapping["paths"].get(name, None)
|
||||
|
||||
@classmethod
|
||||
def get(cls, name, default=None, no_warning=False):
|
||||
r"""Get an item from registry with key 'name'
|
||||
|
||||
Args:
|
||||
name (string): Key whose value needs to be retrieved.
|
||||
default: If passed and key is not in registry, default value will
|
||||
be returned with a warning. Default: None
|
||||
no_warning (bool): If passed as True, warning when key doesn't exist
|
||||
will not be generated. Useful for MMF's
|
||||
internal operations. Default: False
|
||||
"""
|
||||
original_name = name
|
||||
name = name.split(".")
|
||||
value = cls.mapping["state"]
|
||||
for subname in name:
|
||||
value = value.get(subname, default)
|
||||
if value is default:
|
||||
break
|
||||
|
||||
if (
|
||||
"writer" in cls.mapping["state"]
|
||||
and value == default
|
||||
and no_warning is False
|
||||
):
|
||||
cls.mapping["state"]["writer"].warning(
|
||||
"Key {} is not present in registry, returning default value "
|
||||
"of {}".format(original_name, default)
|
||||
)
|
||||
return value
|
||||
|
||||
@classmethod
|
||||
def unregister(cls, name):
|
||||
r"""Remove an item from registry with key 'name'
|
||||
|
||||
Args:
|
||||
name: Key which needs to be removed.
|
||||
Usage::
|
||||
|
||||
from mmf.common.registry import registry
|
||||
|
||||
config = registry.unregister("config")
|
||||
"""
|
||||
return cls.mapping["state"].pop(name, None)
|
||||
|
||||
|
||||
registry = Registry()
|
424
models/MiniGPT4/minigpt4/common/utils.py
Normal file
@@ -0,0 +1,424 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
import shutil
|
||||
import urllib
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from typing import Optional
|
||||
from urllib.parse import urlparse
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import yaml
|
||||
from iopath.common.download import download
|
||||
from iopath.common.file_io import file_lock, g_pathmgr
|
||||
from minigpt4.common.registry import registry
|
||||
from torch.utils.model_zoo import tqdm
|
||||
from torchvision.datasets.utils import (
|
||||
check_integrity,
|
||||
download_file_from_google_drive,
|
||||
extract_archive,
|
||||
)
|
||||
|
||||
|
||||
def now():
|
||||
from datetime import datetime
|
||||
|
||||
return datetime.now().strftime("%Y%m%d%H%M")[:-1]
|
||||
|
||||
|
||||
def is_url(url_or_filename):
|
||||
parsed = urlparse(url_or_filename)
|
||||
return parsed.scheme in ("http", "https")
|
||||
|
||||
|
||||
def get_cache_path(rel_path):
|
||||
return os.path.expanduser(os.path.join(registry.get_path("cache_root"), rel_path))
|
||||
|
||||
|
||||
def get_abs_path(rel_path):
|
||||
return os.path.join(registry.get_path("library_root"), rel_path)
|
||||
|
||||
|
||||
def load_json(filename):
|
||||
with open(filename, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
# The following are adapted from torchvision and vissl
|
||||
# torchvision: https://github.com/pytorch/vision
|
||||
# vissl: https://github.com/facebookresearch/vissl/blob/main/vissl/utils/download.py
|
||||
|
||||
|
||||
def makedir(dir_path):
|
||||
"""
|
||||
Create the directory if it does not exist.
|
||||
"""
|
||||
is_success = False
|
||||
try:
|
||||
if not g_pathmgr.exists(dir_path):
|
||||
g_pathmgr.mkdirs(dir_path)
|
||||
is_success = True
|
||||
except BaseException:
|
||||
print(f"Error creating directory: {dir_path}")
|
||||
return is_success
|
||||
|
||||
|
||||
def get_redirected_url(url: str):
|
||||
"""
|
||||
Given a URL, returns the URL it redirects to or the
|
||||
original URL in case of no indirection
|
||||
"""
|
||||
import requests
|
||||
|
||||
with requests.Session() as session:
|
||||
with session.get(url, stream=True, allow_redirects=True) as response:
|
||||
if response.history:
|
||||
return response.url
|
||||
else:
|
||||
return url
|
||||
|
||||
|
||||
def to_google_drive_download_url(view_url: str) -> str:
|
||||
"""
|
||||
Utility function to transform a view URL of google drive
|
||||
to a download URL for google drive
|
||||
Example input:
|
||||
https://drive.google.com/file/d/137RyRjvTBkBiIfeYBNZBtViDHQ6_Ewsp/view
|
||||
Example output:
|
||||
https://drive.google.com/uc?export=download&id=137RyRjvTBkBiIfeYBNZBtViDHQ6_Ewsp
|
||||
"""
|
||||
splits = view_url.split("/")
|
||||
assert splits[-1] == "view"
|
||||
file_id = splits[-2]
|
||||
return f"https://drive.google.com/uc?export=download&id={file_id}"
|
||||
|
||||
|
||||
def download_google_drive_url(url: str, output_path: str, output_file_name: str):
|
||||
"""
|
||||
Download a file from google drive
|
||||
Downloading an URL from google drive requires confirmation when
|
||||
the file of the size is too big (google drive notifies that
|
||||
anti-viral checks cannot be performed on such files)
|
||||
"""
|
||||
import requests
|
||||
|
||||
with requests.Session() as session:
|
||||
|
||||
# First get the confirmation token and append it to the URL
|
||||
with session.get(url, stream=True, allow_redirects=True) as response:
|
||||
for k, v in response.cookies.items():
|
||||
if k.startswith("download_warning"):
|
||||
url = url + "&confirm=" + v
|
||||
|
||||
# Then download the content of the file
|
||||
with session.get(url, stream=True, verify=True) as response:
|
||||
makedir(output_path)
|
||||
path = os.path.join(output_path, output_file_name)
|
||||
total_size = int(response.headers.get("Content-length", 0))
|
||||
with open(path, "wb") as file:
|
||||
from tqdm import tqdm
|
||||
|
||||
with tqdm(total=total_size) as progress_bar:
|
||||
for block in response.iter_content(
|
||||
chunk_size=io.DEFAULT_BUFFER_SIZE
|
||||
):
|
||||
file.write(block)
|
||||
progress_bar.update(len(block))
|
||||
|
||||
|
||||
def _get_google_drive_file_id(url: str) -> Optional[str]:
|
||||
parts = urlparse(url)
|
||||
|
||||
if re.match(r"(drive|docs)[.]google[.]com", parts.netloc) is None:
|
||||
return None
|
||||
|
||||
match = re.match(r"/file/d/(?P<id>[^/]*)", parts.path)
|
||||
if match is None:
|
||||
return None
|
||||
|
||||
return match.group("id")
|
||||
|
||||
|
||||
def _urlretrieve(url: str, filename: str, chunk_size: int = 1024) -> None:
|
||||
with open(filename, "wb") as fh:
|
||||
with urllib.request.urlopen(
|
||||
urllib.request.Request(url, headers={"User-Agent": "vissl"})
|
||||
) as response:
|
||||
with tqdm(total=response.length) as pbar:
|
||||
for chunk in iter(lambda: response.read(chunk_size), ""):
|
||||
if not chunk:
|
||||
break
|
||||
pbar.update(chunk_size)
|
||||
fh.write(chunk)
|
||||
|
||||
|
||||
def download_url(
|
||||
url: str,
|
||||
root: str,
|
||||
filename: Optional[str] = None,
|
||||
md5: Optional[str] = None,
|
||||
) -> None:
|
||||
"""Download a file from a url and place it in root.
|
||||
Args:
|
||||
url (str): URL to download file from
|
||||
root (str): Directory to place downloaded file in
|
||||
filename (str, optional): Name to save the file under.
|
||||
If None, use the basename of the URL.
|
||||
md5 (str, optional): MD5 checksum of the download. If None, do not check
|
||||
"""
|
||||
root = os.path.expanduser(root)
|
||||
if not filename:
|
||||
filename = os.path.basename(url)
|
||||
fpath = os.path.join(root, filename)
|
||||
|
||||
makedir(root)
|
||||
|
||||
# check if file is already present locally
|
||||
if check_integrity(fpath, md5):
|
||||
print("Using downloaded and verified file: " + fpath)
|
||||
return
|
||||
|
||||
# expand redirect chain if needed
|
||||
url = get_redirected_url(url)
|
||||
|
||||
# check if file is located on Google Drive
|
||||
file_id = _get_google_drive_file_id(url)
|
||||
if file_id is not None:
|
||||
return download_file_from_google_drive(file_id, root, filename, md5)
|
||||
|
||||
# download the file
|
||||
try:
|
||||
print("Downloading " + url + " to " + fpath)
|
||||
_urlretrieve(url, fpath)
|
||||
except (urllib.error.URLError, IOError) as e: # type: ignore[attr-defined]
|
||||
if url[:5] == "https":
|
||||
url = url.replace("https:", "http:")
|
||||
print(
|
||||
"Failed download. Trying https -> http instead."
|
||||
" Downloading " + url + " to " + fpath
|
||||
)
|
||||
_urlretrieve(url, fpath)
|
||||
else:
|
||||
raise e
|
||||
|
||||
# check integrity of downloaded file
|
||||
if not check_integrity(fpath, md5):
|
||||
raise RuntimeError("File not found or corrupted.")
|
||||
|
||||
|
||||
def download_and_extract_archive(
|
||||
url: str,
|
||||
download_root: str,
|
||||
extract_root: Optional[str] = None,
|
||||
filename: Optional[str] = None,
|
||||
md5: Optional[str] = None,
|
||||
remove_finished: bool = False,
|
||||
) -> None:
|
||||
download_root = os.path.expanduser(download_root)
|
||||
if extract_root is None:
|
||||
extract_root = download_root
|
||||
if not filename:
|
||||
filename = os.path.basename(url)
|
||||
|
||||
download_url(url, download_root, filename, md5)
|
||||
|
||||
archive = os.path.join(download_root, filename)
|
||||
print("Extracting {} to {}".format(archive, extract_root))
|
||||
extract_archive(archive, extract_root, remove_finished)
|
||||
|
||||
|
||||
def cache_url(url: str, cache_dir: str) -> str:
|
||||
"""
|
||||
This implementation downloads the remote resource and caches it locally.
|
||||
The resource will only be downloaded if not previously requested.
|
||||
"""
|
||||
parsed_url = urlparse(url)
|
||||
dirname = os.path.join(cache_dir, os.path.dirname(parsed_url.path.lstrip("/")))
|
||||
makedir(dirname)
|
||||
filename = url.split("/")[-1]
|
||||
cached = os.path.join(dirname, filename)
|
||||
with file_lock(cached):
|
||||
if not os.path.isfile(cached):
|
||||
logging.info(f"Downloading {url} to {cached} ...")
|
||||
cached = download(url, dirname, filename=filename)
|
||||
logging.info(f"URL {url} cached in {cached}")
|
||||
return cached
|
||||
|
||||
|
||||
# TODO (prigoyal): convert this into RAII-style API
|
||||
def create_file_symlink(file1, file2):
|
||||
"""
|
||||
Simply create the symlinks for a given file1 to file2.
|
||||
Useful during model checkpointing to symlinks to the
|
||||
latest successful checkpoint.
|
||||
"""
|
||||
try:
|
||||
if g_pathmgr.exists(file2):
|
||||
g_pathmgr.rm(file2)
|
||||
g_pathmgr.symlink(file1, file2)
|
||||
except Exception as e:
|
||||
logging.info(f"Could NOT create symlink. Error: {e}")
|
||||
|
||||
|
||||
def save_file(data, filename, append_to_json=True, verbose=True):
|
||||
"""
|
||||
Common i/o utility to handle saving data to various file formats.
|
||||
Supported:
|
||||
.pkl, .pickle, .npy, .json
|
||||
Specifically for .json, users have the option to either append (default)
|
||||
or rewrite by passing in Boolean value to append_to_json.
|
||||
"""
|
||||
if verbose:
|
||||
logging.info(f"Saving data to file: {filename}")
|
||||
file_ext = os.path.splitext(filename)[1]
|
||||
if file_ext in [".pkl", ".pickle"]:
|
||||
with g_pathmgr.open(filename, "wb") as fopen:
|
||||
pickle.dump(data, fopen, pickle.HIGHEST_PROTOCOL)
|
||||
elif file_ext == ".npy":
|
||||
with g_pathmgr.open(filename, "wb") as fopen:
|
||||
np.save(fopen, data)
|
||||
elif file_ext == ".json":
|
||||
if append_to_json:
|
||||
with g_pathmgr.open(filename, "a") as fopen:
|
||||
fopen.write(json.dumps(data, sort_keys=True) + "\n")
|
||||
fopen.flush()
|
||||
else:
|
||||
with g_pathmgr.open(filename, "w") as fopen:
|
||||
fopen.write(json.dumps(data, sort_keys=True) + "\n")
|
||||
fopen.flush()
|
||||
elif file_ext == ".yaml":
|
||||
with g_pathmgr.open(filename, "w") as fopen:
|
||||
dump = yaml.dump(data)
|
||||
fopen.write(dump)
|
||||
fopen.flush()
|
||||
else:
|
||||
raise Exception(f"Saving {file_ext} is not supported yet")
|
||||
|
||||
if verbose:
|
||||
logging.info(f"Saved data to file: {filename}")
|
||||
|
||||
|
||||
def load_file(filename, mmap_mode=None, verbose=True, allow_pickle=False):
|
||||
"""
|
||||
Common i/o utility to handle loading data from various file formats.
|
||||
Supported:
|
||||
.pkl, .pickle, .npy, .json
|
||||
For the npy files, we support reading the files in mmap_mode.
|
||||
If the mmap_mode of reading is not successful, we load data without the
|
||||
mmap_mode.
|
||||
"""
|
||||
if verbose:
|
||||
logging.info(f"Loading data from file: {filename}")
|
||||
|
||||
file_ext = os.path.splitext(filename)[1]
|
||||
if file_ext == ".txt":
|
||||
with g_pathmgr.open(filename, "r") as fopen:
|
||||
data = fopen.readlines()
|
||||
elif file_ext in [".pkl", ".pickle"]:
|
||||
with g_pathmgr.open(filename, "rb") as fopen:
|
||||
data = pickle.load(fopen, encoding="latin1")
|
||||
elif file_ext == ".npy":
|
||||
if mmap_mode:
|
||||
try:
|
||||
with g_pathmgr.open(filename, "rb") as fopen:
|
||||
data = np.load(
|
||||
fopen,
|
||||
allow_pickle=allow_pickle,
|
||||
encoding="latin1",
|
||||
mmap_mode=mmap_mode,
|
||||
)
|
||||
except ValueError as e:
|
||||
logging.info(
|
||||
f"Could not mmap {filename}: {e}. Trying without g_pathmgr"
|
||||
)
|
||||
data = np.load(
|
||||
filename,
|
||||
allow_pickle=allow_pickle,
|
||||
encoding="latin1",
|
||||
mmap_mode=mmap_mode,
|
||||
)
|
||||
logging.info("Successfully loaded without g_pathmgr")
|
||||
except Exception:
|
||||
logging.info("Could not mmap without g_pathmgr. Trying without mmap")
|
||||
with g_pathmgr.open(filename, "rb") as fopen:
|
||||
data = np.load(fopen, allow_pickle=allow_pickle, encoding="latin1")
|
||||
else:
|
||||
with g_pathmgr.open(filename, "rb") as fopen:
|
||||
data = np.load(fopen, allow_pickle=allow_pickle, encoding="latin1")
|
||||
elif file_ext == ".json":
|
||||
with g_pathmgr.open(filename, "r") as fopen:
|
||||
data = json.load(fopen)
|
||||
elif file_ext == ".yaml":
|
||||
with g_pathmgr.open(filename, "r") as fopen:
|
||||
data = yaml.load(fopen, Loader=yaml.FullLoader)
|
||||
elif file_ext == ".csv":
|
||||
with g_pathmgr.open(filename, "r") as fopen:
|
||||
data = pd.read_csv(fopen)
|
||||
else:
|
||||
raise Exception(f"Reading from {file_ext} is not supported yet")
|
||||
return data
|
||||
|
||||
|
||||
def abspath(resource_path: str):
|
||||
"""
|
||||
Make a path absolute, but take into account prefixes like
|
||||
"http://" or "manifold://"
|
||||
"""
|
||||
regex = re.compile(r"^\w+://")
|
||||
if regex.match(resource_path) is None:
|
||||
return os.path.abspath(resource_path)
|
||||
else:
|
||||
return resource_path
|
||||
|
||||
|
||||
def makedir(dir_path):
|
||||
"""
|
||||
Create the directory if it does not exist.
|
||||
"""
|
||||
is_success = False
|
||||
try:
|
||||
if not g_pathmgr.exists(dir_path):
|
||||
g_pathmgr.mkdirs(dir_path)
|
||||
is_success = True
|
||||
except BaseException:
|
||||
logging.info(f"Error creating directory: {dir_path}")
|
||||
return is_success
|
||||
|
||||
|
||||
def is_url(input_url):
|
||||
"""
|
||||
Check if an input string is a url. look for http(s):// and ignoring the case
|
||||
"""
|
||||
is_url = re.match(r"^(?:http)s?://", input_url, re.IGNORECASE) is not None
|
||||
return is_url
|
||||
|
||||
|
||||
def cleanup_dir(dir):
|
||||
"""
|
||||
Utility for deleting a directory. Useful for cleaning the storage space
|
||||
that contains various training artifacts like checkpoints, data etc.
|
||||
"""
|
||||
if os.path.exists(dir):
|
||||
logging.info(f"Deleting directory: {dir}")
|
||||
shutil.rmtree(dir)
|
||||
logging.info(f"Deleted contents of directory: {dir}")
|
||||
|
||||
|
||||
def get_file_size(filename):
|
||||
"""
|
||||
Given a file, get the size of file in MB
|
||||
"""
|
||||
size_in_mb = os.path.getsize(filename) / float(1024**2)
|
||||
return size_in_mb
|
@@ -0,0 +1,5 @@
|
||||
datasets:
|
||||
cc_sbu_align:
|
||||
data_type: images
|
||||
build_info:
|
||||
storage: /path/to/cc_sbu_align/
|
@@ -0,0 +1,5 @@
|
||||
datasets:
|
||||
cc_sbu:
|
||||
data_type: images
|
||||
build_info:
|
||||
storage: /path/to/cc_sbu_dataset/{00000..01255}.tar
|
@@ -0,0 +1,5 @@
|
||||
datasets:
|
||||
laion:
|
||||
data_type: images
|
||||
build_info:
|
||||
storage: /path/to/laion_dataset/{00000..10488}.tar
|
5
models/MiniGPT4/minigpt4/configs/default.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
env:
|
||||
# For default users
|
||||
# cache_root: "cache"
|
||||
# For internal use with persistent storage
|
||||
cache_root: "/export/home/.cache/minigpt4"
|
33
models/MiniGPT4/minigpt4/configs/models/minigpt4.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
model:
|
||||
arch: mini_gpt4
|
||||
|
||||
# vit encoder
|
||||
image_size: 224
|
||||
drop_path_rate: 0
|
||||
use_grad_checkpoint: False
|
||||
vit_precision: "fp16"
|
||||
freeze_vit: True
|
||||
freeze_qformer: True
|
||||
|
||||
# Q-Former
|
||||
num_query_token: 32
|
||||
|
||||
# Vicuna
|
||||
llama_model: "./models/MiniGPT4/model_weight/model"
|
||||
|
||||
# generation configs
|
||||
prompt: ""
|
||||
|
||||
preprocess:
|
||||
vis_processor:
|
||||
train:
|
||||
name: "blip2_image_train"
|
||||
image_size: 224
|
||||
eval:
|
||||
name: "blip2_image_eval"
|
||||
image_size: 224
|
||||
text_processor:
|
||||
train:
|
||||
name: "blip_caption"
|
||||
eval:
|
||||
name: "blip_caption"
|
0
models/MiniGPT4/minigpt4/conversation/__init__.py
Normal file
207
models/MiniGPT4/minigpt4/conversation/conversation.py
Normal file
@@ -0,0 +1,207 @@
|
||||
import argparse
|
||||
import time
|
||||
from PIL import Image
|
||||
|
||||
import torch
|
||||
from transformers import AutoTokenizer, AutoModelForCausalLM, LlamaTokenizer
|
||||
from transformers import StoppingCriteria, StoppingCriteriaList
|
||||
|
||||
import dataclasses
|
||||
from enum import auto, Enum
|
||||
from typing import List, Tuple, Any
|
||||
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
|
||||
class SeparatorStyle(Enum):
|
||||
"""Different separator style."""
|
||||
SINGLE = auto()
|
||||
TWO = auto()
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class Conversation:
|
||||
"""A class that keeps all conversation history."""
|
||||
system: str
|
||||
roles: List[str]
|
||||
messages: List[List[str]]
|
||||
offset: int
|
||||
# system_img: List[Image.Image] = []
|
||||
sep_style: SeparatorStyle = SeparatorStyle.SINGLE
|
||||
sep: str = "###"
|
||||
sep2: str = None
|
||||
|
||||
skip_next: bool = False
|
||||
conv_id: Any = None
|
||||
|
||||
def get_prompt(self):
|
||||
if self.sep_style == SeparatorStyle.SINGLE:
|
||||
ret = self.system + self.sep
|
||||
for role, message in self.messages:
|
||||
if message:
|
||||
ret += role + ": " + message + self.sep
|
||||
else:
|
||||
ret += role + ":"
|
||||
return ret
|
||||
elif self.sep_style == SeparatorStyle.TWO:
|
||||
seps = [self.sep, self.sep2]
|
||||
ret = self.system + seps[0]
|
||||
for i, (role, message) in enumerate(self.messages):
|
||||
if message:
|
||||
ret += role + ": " + message + seps[i % 2]
|
||||
else:
|
||||
ret += role + ":"
|
||||
return ret
|
||||
else:
|
||||
raise ValueError(f"Invalid style: {self.sep_style}")
|
||||
|
||||
def append_message(self, role, message):
|
||||
self.messages.append([role, message])
|
||||
|
||||
def to_gradio_chatbot(self):
|
||||
ret = []
|
||||
for i, (role, msg) in enumerate(self.messages[self.offset:]):
|
||||
if i % 2 == 0:
|
||||
ret.append([msg, None])
|
||||
else:
|
||||
ret[-1][-1] = msg
|
||||
return ret
|
||||
|
||||
def copy(self):
|
||||
return Conversation(
|
||||
system=self.system,
|
||||
# system_img=self.system_img,
|
||||
roles=self.roles,
|
||||
messages=[[x, y] for x, y in self.messages],
|
||||
offset=self.offset,
|
||||
sep_style=self.sep_style,
|
||||
sep=self.sep,
|
||||
sep2=self.sep2,
|
||||
conv_id=self.conv_id)
|
||||
|
||||
def dict(self):
|
||||
return {
|
||||
"system": self.system,
|
||||
# "system_img": self.system_img,
|
||||
"roles": self.roles,
|
||||
"messages": self.messages,
|
||||
"offset": self.offset,
|
||||
"sep": self.sep,
|
||||
"sep2": self.sep2,
|
||||
"conv_id": self.conv_id,
|
||||
}
|
||||
|
||||
|
||||
class StoppingCriteriaSub(StoppingCriteria):
|
||||
|
||||
def __init__(self, stops=[], encounters=1):
|
||||
super().__init__()
|
||||
self.stops = stops
|
||||
|
||||
def __call__(self, input_ids: torch.LongTensor, scores: torch.FloatTensor):
|
||||
for stop in self.stops:
|
||||
if torch.all((stop == input_ids[0][-len(stop):])).item():
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
CONV_VISION = Conversation(
|
||||
system="Give the following image: <Img>ImageContent</Img>. "
|
||||
"You will be able to see the image once I provide it to you. Please answer my questions.",
|
||||
roles=("Human", "Assistant"),
|
||||
messages=[],
|
||||
offset=2,
|
||||
sep_style=SeparatorStyle.SINGLE,
|
||||
sep="###",
|
||||
)
|
||||
|
||||
|
||||
|
||||
class Chat:
|
||||
def __init__(self, model, vis_processor, device='cuda:0'):
|
||||
self.device = device
|
||||
self.model = model
|
||||
self.vis_processor = vis_processor
|
||||
stop_words_ids = [torch.tensor([835]).to(self.device),
|
||||
torch.tensor([2277, 29937]).to(self.device)] # '###' can be encoded in two different ways.
|
||||
self.stopping_criteria = StoppingCriteriaList([StoppingCriteriaSub(stops=stop_words_ids)])
|
||||
|
||||
def ask(self, text, conv):
|
||||
if len(conv.messages) > 0 and conv.messages[-1][0] == conv.roles[0] \
|
||||
and conv.messages[-1][1][-6:] == '</Img>': # last message is image.
|
||||
conv.messages[-1][1] = ' '.join([conv.messages[-1][1], text])
|
||||
else:
|
||||
conv.append_message(conv.roles[0], text)
|
||||
|
||||
def answer(self, conv, img_list, max_new_tokens=300, num_beams=1, min_length=1, top_p=0.9,
|
||||
repetition_penalty=1.0, length_penalty=1, temperature=1.0, max_length=2000):
|
||||
conv.append_message(conv.roles[1], None)
|
||||
embs = self.get_context_emb(conv, img_list)
|
||||
|
||||
current_max_len = embs.shape[1] + max_new_tokens
|
||||
if current_max_len - max_length > 0:
|
||||
print('Warning: The number of tokens in current conversation exceeds the max length. '
|
||||
'The model will not see the contexts outside the range.')
|
||||
begin_idx = max(0, current_max_len - max_length)
|
||||
|
||||
embs = embs[:, begin_idx:]
|
||||
|
||||
outputs = self.model.llama_model.generate(
|
||||
inputs_embeds=embs,
|
||||
max_new_tokens=max_new_tokens,
|
||||
stopping_criteria=self.stopping_criteria,
|
||||
num_beams=num_beams,
|
||||
do_sample=True,
|
||||
min_length=min_length,
|
||||
top_p=top_p,
|
||||
repetition_penalty=repetition_penalty,
|
||||
length_penalty=length_penalty,
|
||||
temperature=temperature,
|
||||
)
|
||||
output_token = outputs[0]
|
||||
if output_token[0] == 0: # the model might output a unknow token <unk> at the beginning. remove it
|
||||
output_token = output_token[1:]
|
||||
if output_token[0] == 1: # some users find that there is a start token <s> at the beginning. remove it
|
||||
output_token = output_token[1:]
|
||||
output_text = self.model.llama_tokenizer.decode(output_token, add_special_tokens=False)
|
||||
output_text = output_text.split('###')[0] # remove the stop sign '###'
|
||||
output_text = output_text.split('Assistant:')[-1].strip()
|
||||
conv.messages[-1][1] = output_text
|
||||
return output_text, output_token.cpu().numpy()
|
||||
|
||||
def upload_img(self, image, conv, img_list):
|
||||
if isinstance(image, str): # is a image path
|
||||
raw_image = Image.open(image).convert('RGB')
|
||||
image = self.vis_processor(raw_image).unsqueeze(0).to(self.device)
|
||||
elif isinstance(image, Image.Image):
|
||||
raw_image = image
|
||||
image = self.vis_processor(raw_image).unsqueeze(0).to(self.device)
|
||||
elif isinstance(image, torch.Tensor):
|
||||
if len(image.shape) == 3:
|
||||
image = image.unsqueeze(0)
|
||||
image = image.to(self.device)
|
||||
|
||||
image_emb, _ = self.model.encode_img(image)
|
||||
img_list.append(image_emb)
|
||||
conv.append_message(conv.roles[0], "<Img><ImageHere></Img>")
|
||||
msg = "Received."
|
||||
# self.conv.append_message(self.conv.roles[1], msg)
|
||||
return msg
|
||||
|
||||
def get_context_emb(self, conv, img_list):
|
||||
prompt = conv.get_prompt()
|
||||
prompt_segs = prompt.split('<ImageHere>')
|
||||
assert len(prompt_segs) == len(img_list) + 1, "Unmatched numbers of image placeholders and images."
|
||||
seg_tokens = [
|
||||
self.model.llama_tokenizer(
|
||||
seg, return_tensors="pt", add_special_tokens=i == 0).to(self.device).input_ids
|
||||
# only add bos to the first seg
|
||||
for i, seg in enumerate(prompt_segs)
|
||||
]
|
||||
seg_embs = [self.model.llama_model.model.embed_tokens(seg_t) for seg_t in seg_tokens]
|
||||
mixed_embs = [emb for pair in zip(seg_embs[:-1], img_list) for emb in pair] + [seg_embs[-1]]
|
||||
mixed_embs = torch.cat(mixed_embs, dim=1)
|
||||
return mixed_embs
|
||||
|
||||
|
0
models/MiniGPT4/minigpt4/datasets/__init__.py
Normal file
71
models/MiniGPT4/minigpt4/datasets/builders/__init__.py
Normal file
@@ -0,0 +1,71 @@
|
||||
"""
|
||||
Copyright (c) 2022, salesforce.com, inc.
|
||||
All rights reserved.
|
||||
SPDX-License-Identifier: BSD-3-Clause
|
||||
For full license text, see the LICENSE_Lavis file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
||||
"""
|
||||
from minigpt4.datasets.builders.base_dataset_builder import load_dataset_config
|
||||
from minigpt4.datasets.builders.image_text_pair_builder import (
|
||||
CCSBUBuilder,
|
||||
LaionBuilder,
|
||||
CCSBUAlignBuilder
|
||||
)
|
||||
from minigpt4.common.registry import registry
|
||||
|
||||
__all__ = [
|
||||
"CCSBUBuilder",
|
||||
"LaionBuilder",
|
||||
"CCSBUAlignBuilder"
|
||||
]
|
||||
|
||||
|
||||
def load_dataset(name, cfg_path=None, vis_path=None, data_type=None):
|
||||
"""
|
||||
Example
|
||||
|
||||
>>> dataset = load_dataset("coco_caption", cfg=None)
|
||||
>>> splits = dataset.keys()
|
||||
>>> print([len(dataset[split]) for split in splits])
|
||||
|
||||
"""
|
||||
if cfg_path is None:
|
||||
cfg = None
|
||||
else:
|
||||
cfg = load_dataset_config(cfg_path)
|
||||
|
||||
try:
|
||||
builder = registry.get_builder_class(name)(cfg)
|
||||
except TypeError:
|
||||
print(
|
||||
f"Dataset {name} not found. Available datasets:\n"
|
||||
+ ", ".join([str(k) for k in dataset_zoo.get_names()])
|
||||
)
|
||||
exit(1)
|
||||
|
||||
if vis_path is not None:
|
||||
if data_type is None:
|
||||
# use default data type in the config
|
||||
data_type = builder.config.data_type
|
||||
|
||||
assert (
|
||||
data_type in builder.config.build_info
|
||||
), f"Invalid data_type {data_type} for {name}."
|
||||
|
||||
builder.config.build_info.get(data_type).storage = vis_path
|
||||
|
||||
dataset = builder.build_datasets()
|
||||
return dataset
|
||||
|
||||
|
||||
class DatasetZoo:
|
||||
def __init__(self) -> None:
|
||||
self.dataset_zoo = {
|
||||
k: list(v.DATASET_CONFIG_DICT.keys())
|
||||
for k, v in sorted(registry.mapping["builder_name_mapping"].items())
|
||||
}
|
||||
|
||||
def get_names(self):
|
||||
return list(self.dataset_zoo.keys())
|
||||
|
||||
|
||||
dataset_zoo = DatasetZoo()
|