Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes.
See raw diff
- Ovis/.ipynb_checkpoints/README-checkpoint.md +110 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__init__.py +0 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/configuration_ovis.cpython-310.pyc +0 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/configuration_ovis.cpython-39.pyc +0 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/modeling_ovis.cpython-310.pyc +0 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/modeling_ovis.cpython-39.pyc +0 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/configuration_ovis.py +201 -0
- Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/modeling_ovis.py +625 -0
- Ovis/Ovis1.6-Gemma2-27B/__init__.py +0 -0
- Ovis/docs/license/QWEN_LICENSE +53 -0
- Ovis/ovis.egg-info/PKG-INFO +30 -0
- Ovis/ovis.egg-info/SOURCES.txt +24 -0
- Ovis/ovis.egg-info/dependency_links.txt +1 -0
- Ovis/ovis.egg-info/requires.txt +26 -0
- Ovis/ovis.egg-info/top_level.txt +1 -0
- Ovis/ovis/__init__.py +3 -0
- Ovis/ovis/model/__init__.py +2 -0
- Ovis/ovis/model/__pycache__/conversation_formatter.cpython-311.pyc +0 -0
- Ovis/ovis/model/__pycache__/modeling_ovis.cpython-310.pyc +0 -0
- Ovis/ovis/model/modeling_ovis.py +434 -0
- Ovis/ovis/model/visual_tokenizer/base_visual_tokenizer.py +264 -0
- Ovis/ovis/model/visual_tokenizer/clip_visual_tokenizer.py +41 -0
- Ovis/ovis/model/visual_tokenizer/siglip_visual_tokenizer.py +43 -0
- Ovis/ovis/serve/__pycache__/runner.cpython-310.pyc +0 -0
- Ovis/ovis/serve/__pycache__/runner.cpython-311.pyc +0 -0
- Ovis/ovis/train/dataset/__init__.py +0 -0
- Ovis/ovis/train/dataset/caption_dataset.py +67 -0
- Ovis/ovis/train/dataset/conversation_dataset.py +67 -0
- Ovis/ovis/train/dataset/multimodal_dataset.py +72 -0
- Ovis/ovis/util/__init__.py +0 -0
- Ovis/ovis/util/__pycache__/__init__.cpython-310.pyc +0 -0
- Ovis/ovis/util/__pycache__/__init__.cpython-311.pyc +0 -0
- Ovis/ovis/util/__pycache__/constants.cpython-310.pyc +0 -0
- Ovis/ovis/util/__pycache__/constants.cpython-311.pyc +0 -0
- Ovis/ovis/util/__pycache__/utils.cpython-311.pyc +0 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/.ipynb_checkpoints/Qwen2-VL-2B-Instruct_DSPAR_MINI_6345-checkpoint.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6275.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6278.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6385.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6481.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6500.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6513.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6515.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6517.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6521.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6533.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6539.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6580.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6589.json +1 -0
- VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6608.json +1 -0
Ovis/.ipynb_checkpoints/README-checkpoint.md
ADDED
@@ -0,0 +1,110 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Ovis: Structural Embedding Alignment for Multimodal Large Language Model
|
2 |
+
|
3 |
+
Ovis (Open VISion) is a novel Multimodal Large Language Model (MLLM) architecture, designed to structurally align visual and textual embeddings. For a comprehensive introduction, please refer to the [Ovis paper](https://arxiv.org/abs/2405.20797).
|
4 |
+
|
5 |
+
<div style="text-align: center;">
|
6 |
+
<img style="max-width: 100%;" src="docs/ovis-illustration.png" alt="Ovis Illustration"/>
|
7 |
+
</div>
|
8 |
+
|
9 |
+
## Release
|
10 |
+
- [11/26] 🔥 Announcing [Ovis1.6-Gemma2-27B](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-27B)!
|
11 |
+
- [11/04] 🔥 Announcing quantized versions of Ovis1.6: [Ovis1.6-Gemma2-9B-GPTQ-Int4](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-9B-GPTQ-Int4) and [Ovis1.6-Llama3.2-3B-GPTQ-Int4](https://huggingface.co/AIDC-AI/Ovis1.6-Llama3.2-3B-GPTQ-Int4)!
|
12 |
+
- [10/22] 🔥 Announcing Ovis1.6-Llama3.2-3B ([Model](https://huggingface.co/AIDC-AI/Ovis1.6-Llama3.2-3B), [Demo](https://huggingface.co/spaces/AIDC-AI/Ovis1.6-Llama3.2-3B))!
|
13 |
+
- [09/19] 🔥 Announcing Ovis1.6-Gemma2-9B ([Model](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-9B), [Demo](https://huggingface.co/spaces/AIDC-AI/Ovis1.6-Gemma2-9B))! This latest release further enhances high-resolution image processing, is trained on a larger, more diverse, and higher-quality dataset, and refines the training process with DPO training following instruction-tuning.
|
14 |
+
- [07/24] 🔥 Introducing Ovis1.5, featuring improved high-resolution image processing and optimized training data for enhanced performance.
|
15 |
+
- [06/14] 🔥 Launch of Ovis1.0, the inaugural version of the Ovis model.
|
16 |
+
|
17 |
+
## Contents
|
18 |
+
- [Install](#install)
|
19 |
+
- [Model](#model)
|
20 |
+
- [Performance](#performance)
|
21 |
+
- [Finetune](#finetune)
|
22 |
+
- [Inference](#inference)
|
23 |
+
- [Quantization](#quantization)
|
24 |
+
- [Citation](#citation)
|
25 |
+
- [Team](#team)
|
26 |
+
- [License](#license)
|
27 |
+
|
28 |
+
## Install
|
29 |
+
Ovis has been tested with Python 3.10, Torch 2.4.0, Transformers 4.46.2, and DeepSpeed 0.15.4. For a comprehensive list of package dependencies, please consult the `requirements.txt` file. Before finetuning or inference, please install Ovis as follows.
|
30 |
+
```bash
|
31 |
+
git clone [email protected]:AIDC-AI/Ovis.git
|
32 |
+
conda create -n ovis python=3.10 -y
|
33 |
+
conda activate ovis
|
34 |
+
cd Ovis
|
35 |
+
pip install -r requirements.txt
|
36 |
+
pip install -e .
|
37 |
+
```
|
38 |
+
|
39 |
+
## Model
|
40 |
+
Ovis can be instantiated with popular LLMs. We provide the following Ovis MLLMs:
|
41 |
+
|
42 |
+
| Ovis MLLMs | ViT | LLM | Model Weights | Demo |
|
43 |
+
|:------------------|:-----------:|:------------------:|:---------------------------------------------------------------:|:----------------------------------------------------------------:|
|
44 |
+
| Ovis1.6-Gemma2-27B | Siglip-400M | Gemma2-27B-It | [Huggingface](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-27B) | - |
|
45 |
+
| Ovis1.6-Gemma2-9B | Siglip-400M | Gemma2-9B-It | [Huggingface](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-9B) | [Space](https://huggingface.co/spaces/AIDC-AI/Ovis1.6-Gemma2-9B) |
|
46 |
+
| Ovis1.6-Llama3.2-3B | Siglip-400M | Llama-3.2-3B-Instruct | [Huggingface](https://huggingface.co/AIDC-AI/Ovis1.6-Llama3.2-3B) | [Space](https://huggingface.co/spaces/AIDC-AI/Ovis1.6-Llama3.2-3B) |
|
47 |
+
|
48 |
+
## Performance
|
49 |
+
With **29B** parameters, **Ovis1.6-Gemma2-27B** achieves exceptional performance in the [OpenCompass](https://github.com/open-compass/VLMEvalKit) benchmark, ranking among the top-tier open-source MLLMs.
|
50 |
+
|
51 |
+

|
52 |
+
|
53 |
+
With just **10B** parameters, **Ovis1.6-Gemma2-9B** leads the [OpenCompass](https://github.com/open-compass/VLMEvalKit) benchmark among open-source MLLMs within **30B** parameters.
|
54 |
+
|
55 |
+

|
56 |
+
|
57 |
+
**Ovis1.6-Llama3.2-3B** leads the [OpenCompass](https://github.com/open-compass/VLMEvalKit) benchmark among open-source MLLMs under **4B** parameters, even surpassing Llama-3.2-11B-Vision-Instruct.
|
58 |
+
|
59 |
+

|
60 |
+
|
61 |
+
## Finetune
|
62 |
+
Finetuning Ovis1.6-Gemma2-9B is supported in [ms-swift](https://github.com/modelscope/ms-swift).
|
63 |
+
|
64 |
+
## Inference
|
65 |
+
We provide an inference wrapper in `ovis/serve/runner.py`, which can be used as:
|
66 |
+
```python
|
67 |
+
from PIL import Image
|
68 |
+
from ovis.serve.runner import RunnerArguments, OvisRunner
|
69 |
+
image = Image.open('temp.png')
|
70 |
+
text = 'PROMPT'
|
71 |
+
runner_args = RunnerArguments(model_path='AIDC-AI/Ovis1.6-Gemma2-27B')
|
72 |
+
runner = OvisRunner(runner_args)
|
73 |
+
generation = runner.run([image, text])
|
74 |
+
```
|
75 |
+
Based on [Gradio](https://github.com/gradio-app/gradio), Ovis can also be accessed via a web user interface:
|
76 |
+
```bash
|
77 |
+
python ovis/serve/server.py --model_path MODEL_PATH --port PORT
|
78 |
+
```
|
79 |
+
|
80 |
+
## Quantization
|
81 |
+
We quantized Ovis1.6 using AutoGPTQ. For detailed information on running and creating your own quantized version, please refer to the respective Huggingface model cards: [Ovis1.6-Gemma2-9B-GPTQ-Int4](https://huggingface.co/AIDC-AI/Ovis1.6-Gemma2-9B-GPTQ-Int4) and [Ovis1.6-Llama3.2-3B-GPTQ-Int4](https://huggingface.co/AIDC-AI/Ovis1.6-Llama3.2-3B-GPTQ-Int4). Quantized Ovis1.6 maintains performance comparable to its non-quantized counterpart while requiring less GPU memory:
|
82 |
+
|
83 |
+
- Benchmark performance:
|
84 |
+

|
85 |
+

|
86 |
+
|
87 |
+
- GPU memory usage (max_partition=9):
|
88 |
+

|
89 |
+
|
90 |
+
## Citation
|
91 |
+
If you find Ovis useful, please cite the paper
|
92 |
+
```
|
93 |
+
@article{lu2024ovis,
|
94 |
+
title={Ovis: Structural Embedding Alignment for Multimodal Large Language Model},
|
95 |
+
author={Shiyin Lu and Yang Li and Qing-Guo Chen and Zhao Xu and Weihua Luo and Kaifu Zhang and Han-Jia Ye},
|
96 |
+
year={2024},
|
97 |
+
journal={arXiv:2405.20797}
|
98 |
+
}
|
99 |
+
```
|
100 |
+
|
101 |
+
## Team
|
102 |
+
This work is a collaborative effort by the MarcoVL team. We would also like to provide links to the following MLLM papers from our team:
|
103 |
+
- [Parrot: Multilingual Visual Instruction Tuning](https://arxiv.org/abs/2406.02539)
|
104 |
+
- [Wings: Learning Multimodal LLMs without Text-only Forgetting](https://arxiv.org/abs/2406.03496)
|
105 |
+
|
106 |
+
## License
|
107 |
+
This project is licensed under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) (SPDX-License-Identifier: Apache-2.0).
|
108 |
+
|
109 |
+
## Disclaimer
|
110 |
+
We used compliance-checking algorithms during the training process, to ensure the compliance of the trained model to the best of our ability. Due to the complexity of the data and the diversity of language model usage scenarios, we cannot guarantee that the model is completely free of copyright issues or improper content. If you believe anything infringes on your rights or generates improper content, please contact us, and we will promptly address the matter.
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__init__.py
ADDED
File without changes
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/configuration_ovis.cpython-310.pyc
ADDED
Binary file (6.66 kB). View file
|
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/configuration_ovis.cpython-39.pyc
ADDED
Binary file (6.6 kB). View file
|
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/modeling_ovis.cpython-310.pyc
ADDED
Binary file (21.1 kB). View file
|
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/__pycache__/modeling_ovis.cpython-39.pyc
ADDED
Binary file (21.1 kB). View file
|
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/configuration_ovis.py
ADDED
@@ -0,0 +1,201 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from abc import ABC, abstractmethod
|
2 |
+
from typing import List, Dict, Union, Optional
|
3 |
+
|
4 |
+
from transformers import PretrainedConfig, AutoConfig
|
5 |
+
|
6 |
+
IGNORE_ID = -100
|
7 |
+
IMAGE_TOKEN_ID = -200
|
8 |
+
IMAGE_TOKEN = "<image>"
|
9 |
+
IMAGE_ATOM_ID = -300
|
10 |
+
IMAGE_INDICATOR_IDS = [-301, -302, -303, -304, -305]
|
11 |
+
|
12 |
+
|
13 |
+
# ----------------------------------------------------------------------
|
14 |
+
# Visual Tokenizer Configuration
|
15 |
+
# ----------------------------------------------------------------------
|
16 |
+
class BaseVisualTokenizerConfig(PretrainedConfig):
|
17 |
+
def __init__(
|
18 |
+
self,
|
19 |
+
vocab_size=16384,
|
20 |
+
tokenize_function="softmax",
|
21 |
+
tau=1.0,
|
22 |
+
depths=None,
|
23 |
+
drop_cls_token=False,
|
24 |
+
backbone_config: Optional[Union[PretrainedConfig, dict]] = None,
|
25 |
+
hidden_stride: int = 1,
|
26 |
+
**kwargs
|
27 |
+
):
|
28 |
+
super().__init__(**kwargs)
|
29 |
+
self.vocab_size = vocab_size
|
30 |
+
self.tokenize_function = tokenize_function
|
31 |
+
self.tau = tau
|
32 |
+
if isinstance(depths, str):
|
33 |
+
depths = [int(x) for x in depths.split('|')]
|
34 |
+
self.depths = depths
|
35 |
+
self.backbone_kwargs = {}
|
36 |
+
self.drop_cls_token = drop_cls_token
|
37 |
+
if backbone_config is not None:
|
38 |
+
assert isinstance(backbone_config, (PretrainedConfig, dict)), \
|
39 |
+
f"expect `backbone_config` to be instance of PretrainedConfig or dict, but got {type(backbone_config)} type"
|
40 |
+
if not isinstance(backbone_config, PretrainedConfig):
|
41 |
+
model_type = backbone_config['model_type']
|
42 |
+
backbone_config.pop('model_type')
|
43 |
+
backbone_config = AutoConfig.for_model(model_type, **backbone_config)
|
44 |
+
self.backbone_config = backbone_config
|
45 |
+
self.hidden_stride = hidden_stride
|
46 |
+
|
47 |
+
|
48 |
+
class SiglipVisualTokenizerConfig(BaseVisualTokenizerConfig):
|
49 |
+
model_type = "siglip_visual_tokenizer"
|
50 |
+
|
51 |
+
def __init__(self, **kwargs):
|
52 |
+
super().__init__(**kwargs)
|
53 |
+
if self.drop_cls_token:
|
54 |
+
self.drop_cls_token = False
|
55 |
+
if self.depths:
|
56 |
+
assert len(self.depths) == 1
|
57 |
+
self.backbone_kwargs['num_hidden_layers'] = self.depths[0]
|
58 |
+
|
59 |
+
|
60 |
+
AutoConfig.register("siglip_visual_tokenizer", SiglipVisualTokenizerConfig)
|
61 |
+
|
62 |
+
|
63 |
+
# ----------------------------------------------------------------------
|
64 |
+
# Ovis Configuration
|
65 |
+
# ----------------------------------------------------------------------
|
66 |
+
class OvisConfig(PretrainedConfig):
|
67 |
+
model_type = "ovis"
|
68 |
+
|
69 |
+
def __init__(
|
70 |
+
self,
|
71 |
+
llm_config: Optional[Union[PretrainedConfig, dict]] = None,
|
72 |
+
visual_tokenizer_config: Optional[Union[PretrainedConfig, dict]] = None,
|
73 |
+
multimodal_max_length=8192,
|
74 |
+
hidden_size=None,
|
75 |
+
conversation_formatter_class=None,
|
76 |
+
llm_attn_implementation=None,
|
77 |
+
disable_tie_weight=False,
|
78 |
+
**kwargs
|
79 |
+
):
|
80 |
+
super().__init__(**kwargs)
|
81 |
+
if llm_config is not None:
|
82 |
+
assert isinstance(llm_config, (PretrainedConfig, dict)), \
|
83 |
+
f"expect `llm_config` to be instance of PretrainedConfig or dict, but got {type(llm_config)} type"
|
84 |
+
if not isinstance(llm_config, PretrainedConfig):
|
85 |
+
model_type = llm_config['model_type']
|
86 |
+
llm_config.pop('model_type')
|
87 |
+
llm_config = AutoConfig.for_model(model_type, **llm_config)
|
88 |
+
self.llm_config = llm_config
|
89 |
+
if visual_tokenizer_config is not None:
|
90 |
+
assert isinstance(visual_tokenizer_config, (PretrainedConfig, dict)), \
|
91 |
+
f"expect `visual_tokenizer_config` to be instance of PretrainedConfig or dict, but got {type(visual_tokenizer_config)} type"
|
92 |
+
if not isinstance(visual_tokenizer_config, PretrainedConfig):
|
93 |
+
model_type = visual_tokenizer_config['model_type']
|
94 |
+
visual_tokenizer_config.pop('model_type')
|
95 |
+
visual_tokenizer_config = AutoConfig.for_model(model_type, **visual_tokenizer_config)
|
96 |
+
self.visual_tokenizer_config = visual_tokenizer_config
|
97 |
+
self.multimodal_max_length = multimodal_max_length
|
98 |
+
self.hidden_size = hidden_size
|
99 |
+
self.conversation_formatter_class = conversation_formatter_class
|
100 |
+
self.llm_attn_implementation = llm_attn_implementation
|
101 |
+
self.disable_tie_weight = disable_tie_weight
|
102 |
+
|
103 |
+
|
104 |
+
# ----------------------------------------------------------------------
|
105 |
+
# Conversation Formatter
|
106 |
+
# ----------------------------------------------------------------------
|
107 |
+
class ConversationFormatter(ABC):
|
108 |
+
support_tokenizer_types = None
|
109 |
+
|
110 |
+
def __init__(self, tokenizer):
|
111 |
+
tokenizer_type = type(tokenizer).__name__
|
112 |
+
assert tokenizer_type in self.support_tokenizer_types, \
|
113 |
+
f'Invalid tokenizer type, expected one from `{self.support_tokenizer_types}`, but got `{tokenizer_type}`'
|
114 |
+
self.tokenizer = tokenizer
|
115 |
+
self.image_token = IMAGE_TOKEN
|
116 |
+
self.image_token_id = IMAGE_TOKEN_ID
|
117 |
+
self.ignore_id = IGNORE_ID
|
118 |
+
|
119 |
+
def _tokenize_with_image_symbol(self, text):
|
120 |
+
text_chunks = [self.tokenizer(chunk, add_special_tokens=False).input_ids for chunk in
|
121 |
+
text.split(self.image_token)]
|
122 |
+
token_ids = []
|
123 |
+
num_chuck = len(text_chunks)
|
124 |
+
for i, chunk in enumerate(text_chunks):
|
125 |
+
token_ids.extend(chunk)
|
126 |
+
if i < num_chuck - 1:
|
127 |
+
token_ids.append(self.image_token_id)
|
128 |
+
return token_ids
|
129 |
+
|
130 |
+
@abstractmethod
|
131 |
+
def format(self, conversations: List[Dict], generation_preface=None):
|
132 |
+
pass
|
133 |
+
|
134 |
+
@abstractmethod
|
135 |
+
def format_query(self, query, generation_preface=""):
|
136 |
+
pass
|
137 |
+
|
138 |
+
|
139 |
+
class GemmaConversationFormatter(ConversationFormatter):
|
140 |
+
support_tokenizer_types = ['GemmaTokenizer', 'GemmaTokenizerFast']
|
141 |
+
|
142 |
+
def __init__(self, tokenizer):
|
143 |
+
super().__init__(tokenizer)
|
144 |
+
# Gemma does not support system prompt
|
145 |
+
self.from2role = {
|
146 |
+
"human": "<start_of_turn>user\n",
|
147 |
+
"gpt": "<start_of_turn>model\n",
|
148 |
+
}
|
149 |
+
self.gpt_token_num = None
|
150 |
+
self.im_end = "<end_of_turn>\n"
|
151 |
+
self.bos_token = "<bos>"
|
152 |
+
self.bos_token_ids = None
|
153 |
+
|
154 |
+
def format(self, conversations: List[Dict], generation_preface=None):
|
155 |
+
if self.gpt_token_num is None:
|
156 |
+
self.gpt_token_num = len(self.tokenizer(self.from2role["gpt"], add_special_tokens=False).input_ids)
|
157 |
+
|
158 |
+
if self.bos_token_ids is None:
|
159 |
+
self.bos_token_ids = self.tokenizer(self.bos_token, add_special_tokens=False).input_ids
|
160 |
+
|
161 |
+
if conversations[0]["from"] == "system":
|
162 |
+
raise ValueError("Gemma does not support system prompt")
|
163 |
+
|
164 |
+
if generation_preface is not None:
|
165 |
+
conversations.append({
|
166 |
+
"from": "gpt",
|
167 |
+
"value": generation_preface
|
168 |
+
})
|
169 |
+
|
170 |
+
prompt = "" + self.bos_token
|
171 |
+
input_ids = [] + self.bos_token_ids
|
172 |
+
labels = [] + [IGNORE_ID] * len(input_ids)
|
173 |
+
num_conversation = len(conversations)
|
174 |
+
for i, conversation in enumerate(conversations):
|
175 |
+
frm = conversation["from"]
|
176 |
+
role = self.from2role[frm]
|
177 |
+
message = conversation["value"].strip()
|
178 |
+
text = role + message
|
179 |
+
if i < num_conversation - 1 or generation_preface is None:
|
180 |
+
text += self.im_end
|
181 |
+
prompt += text
|
182 |
+
token_ids = self._tokenize_with_image_symbol(text)
|
183 |
+
input_ids.extend(token_ids)
|
184 |
+
label_ids = [self.ignore_id] * len(token_ids)
|
185 |
+
if frm == "gpt":
|
186 |
+
# learning `\n` following `im_end` is meaningless, so the last `\n` token is ignored in label
|
187 |
+
label_ids[self.gpt_token_num:-1] = token_ids[self.gpt_token_num:-1]
|
188 |
+
labels.extend(label_ids)
|
189 |
+
|
190 |
+
assert self._tokenize_with_image_symbol(prompt) == input_ids
|
191 |
+
assert len(input_ids) == len(labels)
|
192 |
+
|
193 |
+
return prompt, input_ids, labels
|
194 |
+
|
195 |
+
def format_query(self, query, generation_preface=""):
|
196 |
+
prompt, input_ids, _ = self.format([{
|
197 |
+
"from": "human",
|
198 |
+
"value": query
|
199 |
+
}], generation_preface=generation_preface)
|
200 |
+
|
201 |
+
return prompt, input_ids
|
Ovis/Ovis1.6-Gemma2-27B/1c18c1e92281df303545f22c27200f046fc44ec4/modeling_ovis.py
ADDED
@@ -0,0 +1,625 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Copyright (C) 2024 AIDC-AI
|
2 |
+
#
|
3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4 |
+
# you may not use this file except in compliance with the License.
|
5 |
+
# You may obtain a copy of the License at
|
6 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
7 |
+
#
|
8 |
+
# Unless required by applicable law or agreed to in writing, software
|
9 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
10 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11 |
+
#
|
12 |
+
# See the License for the specific language governing permissions and
|
13 |
+
# limitations under the License.
|
14 |
+
|
15 |
+
import logging
|
16 |
+
import os
|
17 |
+
import importlib.metadata
|
18 |
+
|
19 |
+
from packaging import version
|
20 |
+
from importlib import import_module
|
21 |
+
from typing import List, Callable, Union, Optional, Dict
|
22 |
+
|
23 |
+
import PIL.Image
|
24 |
+
import torch
|
25 |
+
import transformers
|
26 |
+
from torch import Tensor
|
27 |
+
from torch.nn import init
|
28 |
+
from torch.nn.functional import softmax, gumbel_softmax, pad
|
29 |
+
from transformers.utils import is_flash_attn_2_available
|
30 |
+
from transformers import PreTrainedModel, AutoModel, AutoTokenizer, AutoModelForCausalLM, AutoImageProcessor
|
31 |
+
from transformers import SiglipImageProcessor, SiglipVisionModel
|
32 |
+
from transformers.cache_utils import HybridCache
|
33 |
+
from transformers.generation.utils import GenerateOutput
|
34 |
+
|
35 |
+
from .configuration_ovis import BaseVisualTokenizerConfig, SiglipVisualTokenizerConfig
|
36 |
+
from .configuration_ovis import OvisConfig, ConversationFormatter
|
37 |
+
from .configuration_ovis import IGNORE_ID, IMAGE_ATOM_ID, IMAGE_INDICATOR_IDS, IMAGE_TOKEN_ID
|
38 |
+
|
39 |
+
|
40 |
+
# ----------------------------------------------------------------------
|
41 |
+
# Visual Tokenizer
|
42 |
+
# ----------------------------------------------------------------------
|
43 |
+
class BaseVisualTokenizer(PreTrainedModel):
|
44 |
+
base_model_prefix = "backbone"
|
45 |
+
main_input_name = None
|
46 |
+
_image_processor_class = None
|
47 |
+
_image_processor_kwargs = {}
|
48 |
+
_backbone_class = None
|
49 |
+
_backbone_name_or_path = None
|
50 |
+
|
51 |
+
def __init__(self, config: BaseVisualTokenizerConfig, *inputs, **kwargs):
|
52 |
+
super().__init__(config, *inputs, **kwargs)
|
53 |
+
self.image_processor = AutoImageProcessor.from_pretrained(kwargs['image_processor_name_or_path'])
|
54 |
+
self.backbone = AutoModel.from_config(self.config.backbone_config)
|
55 |
+
head_dim = self.config.vocab_size - len(IMAGE_INDICATOR_IDS) # reserved tokens for IMAGE_INDICATORS
|
56 |
+
self.head = torch.nn.Sequential(
|
57 |
+
torch.nn.Linear(
|
58 |
+
self.backbone.config.hidden_size * self.config.hidden_stride * self.config.hidden_stride, head_dim,
|
59 |
+
bias=False
|
60 |
+
),
|
61 |
+
torch.nn.LayerNorm(head_dim)
|
62 |
+
)
|
63 |
+
|
64 |
+
assert all((self.image_processor.do_resize,
|
65 |
+
not getattr(self.image_processor, 'do_center_crop', False),
|
66 |
+
self.image_processor.do_rescale,
|
67 |
+
self.image_processor.do_normalize
|
68 |
+
)), f"image_processor `{self.image_processor}` is not supported currently"
|
69 |
+
|
70 |
+
def get_backbone(self):
|
71 |
+
return self.backbone
|
72 |
+
|
73 |
+
def get_image_processor(self):
|
74 |
+
return self.image_processor
|
75 |
+
|
76 |
+
def mock_input(self):
|
77 |
+
height, width = self.get_image_size()
|
78 |
+
return torch.zeros(1, 3, height, width), self.construct_image_placeholders((1, 1))
|
79 |
+
|
80 |
+
def get_head(self):
|
81 |
+
return self.head
|
82 |
+
|
83 |
+
def get_image_size(self):
|
84 |
+
raise NotImplementedError
|
85 |
+
|
86 |
+
@staticmethod
|
87 |
+
def construct_image_placeholders(grid):
|
88 |
+
image_placeholders = [IMAGE_INDICATOR_IDS[0], IMAGE_ATOM_ID, IMAGE_INDICATOR_IDS[1]]
|
89 |
+
if grid[0] * grid[1] > 1:
|
90 |
+
for r in range(grid[0]):
|
91 |
+
for c in range(grid[1]):
|
92 |
+
image_placeholders.append(IMAGE_ATOM_ID)
|
93 |
+
if c < grid[1] - 1:
|
94 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[2])
|
95 |
+
if r < grid[0] - 1:
|
96 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[3])
|
97 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[4])
|
98 |
+
return image_placeholders
|
99 |
+
|
100 |
+
def preprocess_image(self, image: PIL.Image.Image, max_partition=9, covering_threshold=0.9, convert_to_rgb=True):
|
101 |
+
def _preprocess(img: PIL.Image.Image, side):
|
102 |
+
# first resize and preprocess
|
103 |
+
w, h = img.size
|
104 |
+
if w == h:
|
105 |
+
new_width = new_height = side
|
106 |
+
elif w > h:
|
107 |
+
new_width = side
|
108 |
+
new_height = int(h / w * new_width)
|
109 |
+
else:
|
110 |
+
new_height = side
|
111 |
+
new_width = int(w / h * new_height)
|
112 |
+
new_size = dict(height=new_height, width=new_width)
|
113 |
+
pixel_values = self.image_processor.preprocess(img, size=new_size, return_tensors='pt')['pixel_values']
|
114 |
+
|
115 |
+
# then pad to square
|
116 |
+
square_values = torch.zeros([1, 3, side, side], dtype=pixel_values.dtype, device=pixel_values.device)
|
117 |
+
new_height, new_width = pixel_values.shape[2:]
|
118 |
+
if new_height == new_width:
|
119 |
+
square_values[:, :, :, :] = pixel_values
|
120 |
+
elif new_height > new_width:
|
121 |
+
from_index = (side - new_width) // 2
|
122 |
+
square_values[:, :, :, from_index:from_index + new_width] = pixel_values
|
123 |
+
else:
|
124 |
+
from_index = (side - new_height) // 2
|
125 |
+
square_values[:, :, from_index:from_index + new_height, :] = pixel_values
|
126 |
+
|
127 |
+
return square_values
|
128 |
+
|
129 |
+
def _partition(img, grid):
|
130 |
+
w, h = img.size
|
131 |
+
row_height = h // grid[0]
|
132 |
+
col_width = w // grid[1]
|
133 |
+
|
134 |
+
partition = []
|
135 |
+
for row in range(grid[0]):
|
136 |
+
for col in range(grid[1]):
|
137 |
+
left = col * col_width
|
138 |
+
upper = row * row_height
|
139 |
+
right = w if col == grid[1] - 1 else (col + 1) * col_width
|
140 |
+
lower = h if row == grid[0] - 1 else (row + 1) * row_height
|
141 |
+
partition.append((left, upper, right, lower))
|
142 |
+
|
143 |
+
return partition
|
144 |
+
|
145 |
+
def _covering_area(left, upper, right, lower, side):
|
146 |
+
w = right - left
|
147 |
+
h = lower - upper
|
148 |
+
w, h = max(w, h), min(w, h)
|
149 |
+
if w > side:
|
150 |
+
h = h / w * side
|
151 |
+
w = side
|
152 |
+
return w * h
|
153 |
+
|
154 |
+
def _get_best_grid(img, side):
|
155 |
+
img_area = img.size[0] * img.size[1]
|
156 |
+
|
157 |
+
candidate_grids = []
|
158 |
+
for i in range(1, max_partition + 1):
|
159 |
+
for j in range(1, max_partition + 1):
|
160 |
+
if i * j <= max_partition:
|
161 |
+
candidate_grids.append((i, j))
|
162 |
+
|
163 |
+
all_grids = []
|
164 |
+
good_grids = []
|
165 |
+
for grid in candidate_grids:
|
166 |
+
partition = _partition(img, grid)
|
167 |
+
covering_ratio = sum([_covering_area(*p, side) for p in partition]) / img_area
|
168 |
+
assert covering_ratio <= 1.0
|
169 |
+
all_grids.append((grid, covering_ratio))
|
170 |
+
if covering_ratio > covering_threshold:
|
171 |
+
good_grids.append((grid, covering_ratio))
|
172 |
+
|
173 |
+
if len(good_grids) > 0:
|
174 |
+
# pick the good partition with minimum #sub_images and break the tie using covering_ratio
|
175 |
+
return sorted(good_grids, key=lambda x: (x[0][0] * x[0][1], -x[1]))[0][0]
|
176 |
+
else:
|
177 |
+
# pick the partition with maximum covering_ratio and break the tie using #sub_images
|
178 |
+
return sorted(all_grids, key=lambda x: (-x[1], x[0][0] * x[0][1]))[0][0]
|
179 |
+
|
180 |
+
if convert_to_rgb and image.mode != 'RGB':
|
181 |
+
image = image.convert('RGB')
|
182 |
+
|
183 |
+
sides = self.get_image_size()
|
184 |
+
if sides[0] != sides[1]:
|
185 |
+
raise ValueError('get_image_size() returns non-square size')
|
186 |
+
side = sides[0]
|
187 |
+
grid = _get_best_grid(image, side)
|
188 |
+
partition = _partition(image, grid)
|
189 |
+
crops = [image.crop(p) for p in partition]
|
190 |
+
if len(crops) > 1:
|
191 |
+
crops.insert(0, image)
|
192 |
+
pixel_values = torch.cat([_preprocess(crop, side) for crop in crops], dim=0)
|
193 |
+
image_placeholders = self.construct_image_placeholders(grid)
|
194 |
+
return pixel_values, image_placeholders
|
195 |
+
|
196 |
+
def tokenize(self, logits):
|
197 |
+
def st_argmax(y_soft, dim): # straight-through softmax
|
198 |
+
index = y_soft.max(dim, keepdim=True)[1]
|
199 |
+
y_hard = torch.zeros_like(y_soft, memory_format=torch.legacy_contiguous_format).scatter_(dim, index, 1.0)
|
200 |
+
ret = y_hard - y_soft.detach() + y_soft
|
201 |
+
return ret
|
202 |
+
|
203 |
+
if self.config.tokenize_function == 'softmax':
|
204 |
+
tokens = softmax(logits, dim=-1)
|
205 |
+
elif self.config.tokenize_function == 'gumbel_argmax':
|
206 |
+
tokens = gumbel_softmax(logits, tau=self.config.tau, hard=True)
|
207 |
+
elif self.config.tokenize_function == 'st_argmax':
|
208 |
+
tokens = st_argmax(logits, dim=-1)
|
209 |
+
else:
|
210 |
+
raise ValueError(
|
211 |
+
f'Invalid `max_type`, expected softmax or gumbel_argmax or st_argmax, but got {self.config.tokenize_function}')
|
212 |
+
return tokens
|
213 |
+
|
214 |
+
def encode(self, pixel_values):
|
215 |
+
output = self.backbone(pixel_values, output_hidden_states=True, return_dict=True)
|
216 |
+
features = output.hidden_states[-1]
|
217 |
+
if self.config.drop_cls_token:
|
218 |
+
features = features[:, 1:, :]
|
219 |
+
|
220 |
+
# merge number of `hidden_stride * hidden_stride` hidden states together to reduce token sequence length
|
221 |
+
# e.g., for hidden_stride=3, this leads to a token length reduction: 729 -> 81 for siglip
|
222 |
+
if self.config.hidden_stride > 1:
|
223 |
+
n, l, d = features.shape # this `d` maybe different from the above `d
|
224 |
+
sqrt_l = int(l ** 0.5)
|
225 |
+
assert sqrt_l ** 2 == l, "The token sequence length should be a perfect square."
|
226 |
+
features = features.reshape(n, sqrt_l, sqrt_l, d)
|
227 |
+
pl = (self.config.hidden_stride - (sqrt_l % self.config.hidden_stride)) % self.config.hidden_stride
|
228 |
+
features = pad(features, (0, 0, 0, pl, 0, pl), "constant", 0)
|
229 |
+
sqrt_l += pl
|
230 |
+
features = features.reshape(n, sqrt_l // self.config.hidden_stride, self.config.hidden_stride,
|
231 |
+
sqrt_l // self.config.hidden_stride, self.config.hidden_stride, d)
|
232 |
+
features = features.permute(0, 1, 3, 2, 4, 5) # [n, sqrt_l/hs, sqrt_l/hs, hs, hs, d]
|
233 |
+
features = features.flatten(3) # [n, sqrt_l/hs, sqrt_l/hs, hs*hs*d]
|
234 |
+
features = features.reshape(
|
235 |
+
n, -1, self.config.hidden_stride * self.config.hidden_stride * d)
|
236 |
+
|
237 |
+
return features
|
238 |
+
|
239 |
+
def forward(self, pixel_values) -> torch.Tensor: # [BatchSize, ImageShape] -> [BatchSize, #Token, VocabSize]
|
240 |
+
features = self.encode(pixel_values)
|
241 |
+
logits = self.head(features)
|
242 |
+
tokens = self.tokenize(logits)
|
243 |
+
# tokens' shape is [BatchSize, #Token, VocabSize-5], so padding with [BatchSize, #Token, 5], after
|
244 |
+
# which, tokens' shape should become [BatchSize, #Token, VocabSize]
|
245 |
+
batch_size, token_len, _ = tokens.shape
|
246 |
+
padding_tensor = torch.zeros(size=(batch_size, token_len, len(IMAGE_INDICATOR_IDS)),
|
247 |
+
dtype=tokens.dtype,
|
248 |
+
device=tokens.device,
|
249 |
+
layout=tokens.layout,
|
250 |
+
requires_grad=False)
|
251 |
+
tokens = torch.cat((tokens, padding_tensor), dim=2)
|
252 |
+
return tokens
|
253 |
+
|
254 |
+
|
255 |
+
class SiglipVisualTokenizer(BaseVisualTokenizer):
|
256 |
+
config_class = SiglipVisualTokenizerConfig
|
257 |
+
supports_gradient_checkpointing = True
|
258 |
+
_no_split_modules = ["SiglipVisionTransformer"]
|
259 |
+
_image_processor_class = SiglipImageProcessor
|
260 |
+
_image_processor_kwargs = {}
|
261 |
+
_backbone_class = SiglipVisionModel
|
262 |
+
_backbone_name_or_path = "google/siglip-so400m-patch14-384"
|
263 |
+
|
264 |
+
def get_image_size(self):
|
265 |
+
height = self.image_processor.size["height"]
|
266 |
+
width = self.image_processor.size["width"]
|
267 |
+
return height, width
|
268 |
+
|
269 |
+
|
270 |
+
AutoModel.register(SiglipVisualTokenizerConfig, SiglipVisualTokenizer)
|
271 |
+
|
272 |
+
|
273 |
+
# ----------------------------------------------------------------------
|
274 |
+
# Ovis
|
275 |
+
# ----------------------------------------------------------------------
|
276 |
+
class VisualEmbedding(torch.nn.Embedding):
|
277 |
+
def forward(self, visual_tokens: Tensor) -> Tensor:
|
278 |
+
if visual_tokens.dtype in [torch.int8, torch.int16, torch.int32, torch.int64, torch.long]:
|
279 |
+
return super().forward(visual_tokens)
|
280 |
+
return torch.matmul(visual_tokens, self.weight)
|
281 |
+
|
282 |
+
def reset_parameters(self, mean=0., std=1.) -> None:
|
283 |
+
init.normal_(self.weight, mean=mean, std=std)
|
284 |
+
self._fill_padding_idx_with_zero()
|
285 |
+
|
286 |
+
|
287 |
+
class OvisPreTrainedModel(PreTrainedModel):
|
288 |
+
config_class = OvisConfig
|
289 |
+
base_model_prefix = "ovis"
|
290 |
+
|
291 |
+
|
292 |
+
class Ovis(OvisPreTrainedModel):
|
293 |
+
|
294 |
+
def __init__(self, config: OvisConfig, *inputs, **kwargs):
|
295 |
+
super().__init__(config, *inputs, **kwargs)
|
296 |
+
attn_kwargs = dict()
|
297 |
+
if self.config.llm_attn_implementation:
|
298 |
+
if self.config.llm_attn_implementation == "sdpa":
|
299 |
+
raise ValueError("`sdpa` is currently not supported")
|
300 |
+
elif self.config.llm_attn_implementation == "flash_attention_2":
|
301 |
+
assert (is_flash_attn_2_available() and
|
302 |
+
version.parse(importlib.metadata.version("flash_attn")) >= version.parse("2.6.3")), \
|
303 |
+
"Using `flash_attention_2` requires having `flash_attn>=2.6.3` installed."
|
304 |
+
attn_kwargs["attn_implementation"] = self.config.llm_attn_implementation
|
305 |
+
self.llm = AutoModelForCausalLM.from_config(self.config.llm_config, **attn_kwargs)
|
306 |
+
assert self.config.hidden_size == self.llm.config.hidden_size, "hidden size mismatch"
|
307 |
+
self.text_tokenizer = AutoTokenizer.from_pretrained(self.config.name_or_path)
|
308 |
+
self.visual_tokenizer = AutoModel.from_config(self.config.visual_tokenizer_config,
|
309 |
+
image_processor_name_or_path=self.config.name_or_path)
|
310 |
+
self.vte = VisualEmbedding(
|
311 |
+
self.config.visual_tokenizer_config.vocab_size,
|
312 |
+
self.config.hidden_size,
|
313 |
+
device=self.visual_tokenizer.device,
|
314 |
+
dtype=self.visual_tokenizer.dtype
|
315 |
+
)
|
316 |
+
|
317 |
+
def _merge_modules(modules_list: tuple):
|
318 |
+
merged_modules = []
|
319 |
+
for modules in modules_list:
|
320 |
+
merged_modules.extend(modules if modules else [])
|
321 |
+
return merged_modules
|
322 |
+
|
323 |
+
self._no_split_modules = _merge_modules((self.llm._no_split_modules, self.visual_tokenizer._no_split_modules))
|
324 |
+
self._skip_keys_device_placement = self.llm._skip_keys_device_placement
|
325 |
+
self._keep_in_fp32_modules = _merge_modules(
|
326 |
+
(self.llm._keep_in_fp32_modules, self.visual_tokenizer._keep_in_fp32_modules))
|
327 |
+
self.is_parallelizable = all((self.llm.is_parallelizable, self.visual_tokenizer.is_parallelizable))
|
328 |
+
self.supports_gradient_checkpointing = all(
|
329 |
+
(self.llm.supports_gradient_checkpointing, self.visual_tokenizer.supports_gradient_checkpointing))
|
330 |
+
self._supports_flash_attn_2 = True
|
331 |
+
self._supports_sdpa = False
|
332 |
+
|
333 |
+
def get_text_tokenizer(self):
|
334 |
+
return self.text_tokenizer
|
335 |
+
|
336 |
+
def get_visual_tokenizer(self):
|
337 |
+
return self.visual_tokenizer
|
338 |
+
|
339 |
+
def tie_weights(self):
|
340 |
+
if not self.config.disable_tie_weight:
|
341 |
+
self.get_llm().tie_weights()
|
342 |
+
|
343 |
+
def get_llm(self):
|
344 |
+
return self.llm
|
345 |
+
|
346 |
+
def get_vte(self):
|
347 |
+
return self.vte
|
348 |
+
|
349 |
+
def get_wte(self):
|
350 |
+
return self.llm.get_input_embeddings()
|
351 |
+
|
352 |
+
def get_conversation_formatter(self) -> ConversationFormatter:
|
353 |
+
if getattr(self, 'conversation_formatter', None) is None:
|
354 |
+
self.conversation_formatter = getattr(import_module(".configuration_ovis", __package__),
|
355 |
+
self.config.conversation_formatter_class)(self.text_tokenizer)
|
356 |
+
return self.conversation_formatter
|
357 |
+
|
358 |
+
def forward(
|
359 |
+
self,
|
360 |
+
input_ids: torch.Tensor,
|
361 |
+
attention_mask: torch.Tensor,
|
362 |
+
labels: Optional[torch.Tensor],
|
363 |
+
pixel_values: List[Optional[torch.Tensor]],
|
364 |
+
**kwargs
|
365 |
+
):
|
366 |
+
assert self.training, "`forward` can only be used in training. For inference, use `generate`."
|
367 |
+
_, inputs_embeds, labels, attention_mask = self.merge_multimodal(
|
368 |
+
text_input_ids=input_ids,
|
369 |
+
text_attention_masks=attention_mask,
|
370 |
+
text_labels=labels,
|
371 |
+
pixel_values=pixel_values
|
372 |
+
)
|
373 |
+
return self.llm(inputs_embeds=inputs_embeds, labels=labels, attention_mask=attention_mask, **kwargs)
|
374 |
+
|
375 |
+
def merge_multimodal(
|
376 |
+
self,
|
377 |
+
text_input_ids: torch.Tensor,
|
378 |
+
text_attention_masks: torch.Tensor,
|
379 |
+
text_labels: Optional[torch.Tensor],
|
380 |
+
pixel_values: List[Optional[torch.Tensor]],
|
381 |
+
left_padding: bool = False
|
382 |
+
):
|
383 |
+
input_device = text_input_ids.device
|
384 |
+
visual_vocab_szie = self.get_visual_tokenizer().config.vocab_size
|
385 |
+
visual_indicator_embeds = self.get_vte()(
|
386 |
+
torch.tensor(
|
387 |
+
list(range(visual_vocab_szie - 5, visual_vocab_szie)),
|
388 |
+
dtype=torch.long,
|
389 |
+
device=self.get_visual_tokenizer().device
|
390 |
+
)
|
391 |
+
).to(device=input_device)
|
392 |
+
|
393 |
+
if self.training:
|
394 |
+
# When training, to be compatible with deepspeed zero, each sample has to include pixel_value tensor.
|
395 |
+
# For text-only sample, one can simply use a full zero tensor as pixel_value, which will be ignored
|
396 |
+
# (see below in this function); so, the gradient will not be affected.
|
397 |
+
num_images = [x.shape[0] for x in pixel_values]
|
398 |
+
visual_tokens = self.visual_tokenizer(torch.cat([x for x in pixel_values], dim=0))
|
399 |
+
visual_embeds = torch.split(self.get_vte()(visual_tokens).to(dtype=self.dtype, device=input_device),
|
400 |
+
split_size_or_sections=num_images, dim=0)
|
401 |
+
visual_input_ids = torch.split(torch.argmax(visual_tokens, dim=-1).to(device=input_device),
|
402 |
+
split_size_or_sections=num_images, dim=0)
|
403 |
+
visual_labels = [torch.full(x.shape, IGNORE_ID, dtype=torch.long, device=input_device) for x in
|
404 |
+
visual_input_ids]
|
405 |
+
else:
|
406 |
+
# When inference, sample can include only text with `None` pixel_value
|
407 |
+
num_images = [x.shape[0] if x is not None else 0 for x in pixel_values]
|
408 |
+
if sum(num_images) > 0:
|
409 |
+
visual_tokens = self.visual_tokenizer(torch.cat([x for x in pixel_values if x is not None], dim=0))
|
410 |
+
visual_embeds = torch.split(self.get_vte()(visual_tokens).to(dtype=self.dtype, device=input_device),
|
411 |
+
split_size_or_sections=num_images, dim=0)
|
412 |
+
visual_input_ids = torch.split(torch.argmax(visual_tokens, dim=-1).to(device=input_device),
|
413 |
+
split_size_or_sections=num_images, dim=0)
|
414 |
+
visual_labels = [torch.full(x.shape, IGNORE_ID, dtype=torch.long, device=input_device) for x in
|
415 |
+
visual_input_ids]
|
416 |
+
else:
|
417 |
+
# just placeholders
|
418 |
+
visual_embeds = [None] * len(num_images)
|
419 |
+
visual_input_ids = [None] * len(num_images)
|
420 |
+
visual_labels = [None] * len(num_images)
|
421 |
+
if text_labels is None:
|
422 |
+
text_labels = torch.full(text_input_ids.shape, IGNORE_ID, dtype=torch.long, device=input_device)
|
423 |
+
|
424 |
+
input_embeds = []
|
425 |
+
attention_masks = []
|
426 |
+
labels = []
|
427 |
+
for text_input_id, text_label, text_attention_mask, visual_embed, visual_input_id, visual_label in zip(
|
428 |
+
text_input_ids, text_labels, text_attention_masks, visual_embeds, visual_input_ids, visual_labels
|
429 |
+
):
|
430 |
+
placeholder_token_mask = torch.lt(text_input_id, 0)
|
431 |
+
text_embed = self.get_wte()(torch.masked_fill(text_input_id, placeholder_token_mask, 0))
|
432 |
+
for i, indicator_id in enumerate(IMAGE_INDICATOR_IDS):
|
433 |
+
text_embed[text_input_id == indicator_id] = visual_indicator_embeds[i]
|
434 |
+
image_atom_positions = torch.where(torch.eq(text_input_id, IMAGE_ATOM_ID))[0].tolist()
|
435 |
+
if len(image_atom_positions) > 0:
|
436 |
+
input_embed_parts = []
|
437 |
+
attention_mask_parts = []
|
438 |
+
label_parts = []
|
439 |
+
prev_image_atom_position = -1
|
440 |
+
for index, image_atom_position in enumerate(image_atom_positions):
|
441 |
+
input_embed_parts.append(
|
442 |
+
text_embed[prev_image_atom_position + 1:image_atom_position, :])
|
443 |
+
label_parts.append(
|
444 |
+
text_label[prev_image_atom_position + 1:image_atom_position])
|
445 |
+
attention_mask_parts.append(
|
446 |
+
text_attention_mask[prev_image_atom_position + 1:image_atom_position])
|
447 |
+
input_embed_parts.append(visual_embed[index])
|
448 |
+
attention_mask_parts.append(
|
449 |
+
torch.ones_like(visual_label[index], dtype=torch.bool))
|
450 |
+
label_parts.append(visual_label[index])
|
451 |
+
prev_image_atom_position = image_atom_position
|
452 |
+
if prev_image_atom_position + 1 < text_input_id.shape[0]:
|
453 |
+
input_embed_parts.append(
|
454 |
+
text_embed[prev_image_atom_position + 1:, :])
|
455 |
+
attention_mask_parts.append(
|
456 |
+
text_attention_mask[prev_image_atom_position + 1:])
|
457 |
+
label_parts.append(
|
458 |
+
text_label[prev_image_atom_position + 1:])
|
459 |
+
input_embed = torch.cat(input_embed_parts, dim=0)
|
460 |
+
attention_mask = torch.cat(attention_mask_parts, dim=0)
|
461 |
+
label = torch.cat(label_parts, dim=0)
|
462 |
+
else:
|
463 |
+
input_embed = text_embed
|
464 |
+
attention_mask = text_attention_mask
|
465 |
+
label = text_label
|
466 |
+
if self.training:
|
467 |
+
# Make visual_embed & visual_indicator_embeds involved in the backward graph,
|
468 |
+
# to be compatible with deepspeed zero and ddp.
|
469 |
+
input_embed += torch.sum(visual_embed * 0.0) + torch.sum(visual_indicator_embeds * 0.0)
|
470 |
+
input_embeds.append(input_embed)
|
471 |
+
attention_masks.append(attention_mask)
|
472 |
+
labels.append(label)
|
473 |
+
|
474 |
+
if self.training: # padding to self.config.multimodal_max_length for increased training speed
|
475 |
+
padding_size = max(0, self.config.multimodal_max_length - len(input_embeds[0]))
|
476 |
+
input_embeds[0] = torch.nn.ConstantPad2d((0, 0, 0, padding_size), 0.0)(input_embeds[0])
|
477 |
+
attention_masks[0] = torch.nn.ConstantPad1d((0, padding_size), False)(attention_masks[0])
|
478 |
+
labels[0] = torch.nn.ConstantPad1d((0, padding_size), IGNORE_ID)(labels[0])
|
479 |
+
batch_input_embeds = self.pad_truncate_sequence(input_embeds, batch_first=True, padding_value=0.0, left_padding=left_padding)
|
480 |
+
batch_attention_mask = self.pad_truncate_sequence(attention_masks, batch_first=True, padding_value=False, left_padding=left_padding)
|
481 |
+
batch_labels = self.pad_truncate_sequence(labels, batch_first=True, padding_value=IGNORE_ID, left_padding=left_padding)
|
482 |
+
|
483 |
+
return visual_input_ids, batch_input_embeds, batch_labels, batch_attention_mask
|
484 |
+
|
485 |
+
def pad_truncate_sequence(self, sequences: List[torch.Tensor], batch_first: bool = True, padding_value: float = 0.0, left_padding: bool = False) -> torch.Tensor:
|
486 |
+
if left_padding == False:
|
487 |
+
pad_sequence = torch.nn.utils.rnn.pad_sequence(sequences, batch_first=batch_first, padding_value=padding_value)
|
488 |
+
return pad_sequence[:,:self.config.multimodal_max_length]
|
489 |
+
else:
|
490 |
+
pad_sequence = torch.nn.utils.rnn.pad_sequence([i.flip(dims=[0]) for i in sequences],batch_first=True, padding_value=padding_value).flip(dims=[1])
|
491 |
+
return pad_sequence[:,-self.config.multimodal_max_length:]
|
492 |
+
|
493 |
+
def preprocess_inputs(
|
494 |
+
self,
|
495 |
+
text_or_conversations: Union[List[Dict], str],
|
496 |
+
images: Optional[List[PIL.Image.Image]],
|
497 |
+
max_partition=9,
|
498 |
+
generation_preface='',
|
499 |
+
return_labels=False,
|
500 |
+
propagate_exception=True
|
501 |
+
):
|
502 |
+
# convert text to conversations
|
503 |
+
if isinstance(text_or_conversations, str):
|
504 |
+
conversations = [{
|
505 |
+
"from": "human",
|
506 |
+
"value": text_or_conversations
|
507 |
+
}]
|
508 |
+
elif isinstance(text_or_conversations, list):
|
509 |
+
conversations = text_or_conversations
|
510 |
+
else:
|
511 |
+
raise ValueError(f'Invalid type of `text_or_conversations`, expected `List[Dict]` or `str`,'
|
512 |
+
f' but got {type(text_or_conversations)}')
|
513 |
+
|
514 |
+
# format conversations
|
515 |
+
prompt, raw_input_ids, raw_labels = self.get_conversation_formatter().format(
|
516 |
+
conversations, generation_preface=generation_preface)
|
517 |
+
|
518 |
+
# place image placeholders
|
519 |
+
input_ids = []
|
520 |
+
labels = []
|
521 |
+
pixel_values = []
|
522 |
+
invalidate_label = False
|
523 |
+
image_token_indices = [i for i, v in enumerate(raw_input_ids) if v == IMAGE_TOKEN_ID]
|
524 |
+
last_image_token_index = -1
|
525 |
+
for i in range(len(image_token_indices)):
|
526 |
+
head = 0 if i == 0 else image_token_indices[i - 1] + 1
|
527 |
+
tail = image_token_indices[i]
|
528 |
+
last_image_token_index = tail
|
529 |
+
input_ids.extend(raw_input_ids[head:tail])
|
530 |
+
labels.extend(raw_labels[head:tail])
|
531 |
+
try:
|
532 |
+
image = images[i]
|
533 |
+
raw_pixel_values, image_placeholders = self.visual_tokenizer.preprocess_image(
|
534 |
+
image, max_partition=max_partition)
|
535 |
+
except Exception as e:
|
536 |
+
if propagate_exception:
|
537 |
+
raise e
|
538 |
+
logging.exception(e)
|
539 |
+
invalidate_label = True
|
540 |
+
raw_pixel_values, image_placeholders = self.visual_tokenizer.mock_input()
|
541 |
+
input_ids.extend(image_placeholders)
|
542 |
+
labels.extend([IGNORE_ID] * len(image_placeholders))
|
543 |
+
pixel_values.append(raw_pixel_values)
|
544 |
+
input_ids.extend(raw_input_ids[last_image_token_index + 1:])
|
545 |
+
labels.extend(raw_labels[last_image_token_index + 1:])
|
546 |
+
|
547 |
+
# return tensors
|
548 |
+
input_ids = torch.tensor(input_ids, dtype=torch.long)
|
549 |
+
labels = torch.tensor([IGNORE_ID] * len(labels) if invalidate_label else labels, dtype=torch.long)
|
550 |
+
pixel_values = torch.cat(pixel_values, dim=0) if len(pixel_values) > 0 else None
|
551 |
+
|
552 |
+
if return_labels:
|
553 |
+
return prompt, input_ids, pixel_values, labels
|
554 |
+
else:
|
555 |
+
return prompt, input_ids, pixel_values
|
556 |
+
|
557 |
+
def save_pretrained(
|
558 |
+
self,
|
559 |
+
save_directory: Union[str, os.PathLike],
|
560 |
+
is_main_process: bool = True,
|
561 |
+
state_dict: Optional[dict] = None,
|
562 |
+
save_function: Callable = torch.save,
|
563 |
+
push_to_hub: bool = False,
|
564 |
+
max_shard_size: Union[int, str] = "5GB",
|
565 |
+
safe_serialization: bool = True,
|
566 |
+
variant: Optional[str] = None,
|
567 |
+
token: Optional[Union[str, bool]] = None,
|
568 |
+
save_peft_format: bool = True,
|
569 |
+
**kwargs
|
570 |
+
):
|
571 |
+
super().save_pretrained(save_directory,
|
572 |
+
is_main_process=is_main_process,
|
573 |
+
state_dict=state_dict,
|
574 |
+
save_function=save_function,
|
575 |
+
safe_serialization=safe_serialization)
|
576 |
+
self.get_text_tokenizer().save_pretrained(save_directory)
|
577 |
+
self.get_visual_tokenizer().get_image_processor().save_pretrained(save_directory)
|
578 |
+
|
579 |
+
def _get_hybrid_cache_for_llm(self, batch_size: int, max_cache_len: int):
|
580 |
+
cache_cls = HybridCache
|
581 |
+
llm = self.get_llm()
|
582 |
+
|
583 |
+
need_new_cache = (
|
584 |
+
not hasattr(llm, "_cache")
|
585 |
+
or (not isinstance(llm._cache, cache_cls))
|
586 |
+
or llm._cache.batch_size != batch_size
|
587 |
+
or llm._cache.max_cache_len < max_cache_len
|
588 |
+
)
|
589 |
+
|
590 |
+
if need_new_cache:
|
591 |
+
if hasattr(llm.config, "_pre_quantization_dtype"):
|
592 |
+
cache_dtype = llm.config._pre_quantization_dtype
|
593 |
+
else:
|
594 |
+
cache_dtype = llm.dtype
|
595 |
+
llm._cache = cache_cls(
|
596 |
+
config=llm.config,
|
597 |
+
batch_size=batch_size,
|
598 |
+
max_cache_len=max_cache_len,
|
599 |
+
device=llm.device,
|
600 |
+
dtype=cache_dtype,
|
601 |
+
)
|
602 |
+
else:
|
603 |
+
llm._cache.reset()
|
604 |
+
return llm._cache
|
605 |
+
|
606 |
+
# TODO: support batch generation
|
607 |
+
def generate(
|
608 |
+
self,
|
609 |
+
inputs: Optional[torch.Tensor] = None,
|
610 |
+
**kwargs
|
611 |
+
) -> Union[GenerateOutput, torch.LongTensor]:
|
612 |
+
_, inputs_embeds, labels, attention_mask = self.merge_multimodal(
|
613 |
+
text_input_ids=inputs,
|
614 |
+
text_attention_masks=kwargs.pop('attention_mask'),
|
615 |
+
text_labels=None,
|
616 |
+
pixel_values=kwargs.pop('pixel_values'),
|
617 |
+
left_padding=True
|
618 |
+
)
|
619 |
+
if getattr(self.generation_config, 'cache_implementation') == 'hybrid': # mainly for Gemma2
|
620 |
+
kwargs['past_key_values'] = self._get_hybrid_cache_for_llm(
|
621 |
+
getattr(kwargs, "num_beams", inputs_embeds.shape[0]), kwargs['max_new_tokens'] + inputs_embeds.shape[-2])
|
622 |
+
self.get_llm()._supports_cache_class = True
|
623 |
+
kwargs['cache_implementation'] = None
|
624 |
+
|
625 |
+
return self.llm.generate(inputs=None, inputs_embeds=inputs_embeds, attention_mask=attention_mask, **kwargs)
|
Ovis/Ovis1.6-Gemma2-27B/__init__.py
ADDED
File without changes
|
Ovis/docs/license/QWEN_LICENSE
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Tongyi Qianwen LICENSE AGREEMENT
|
2 |
+
|
3 |
+
Tongyi Qianwen Release Date: August 3, 2023
|
4 |
+
|
5 |
+
By clicking to agree or by using or distributing any portion or element of the Tongyi Qianwen Materials, you will be deemed to have recognized and accepted the content of this Agreement, which is effective immediately.
|
6 |
+
|
7 |
+
1. Definitions
|
8 |
+
a. This Tongyi Qianwen LICENSE AGREEMENT (this "Agreement") shall mean the terms and conditions for use, reproduction, distribution and modification of the Materials as defined by this Agreement.
|
9 |
+
b. "We"(or "Us") shall mean Alibaba Cloud.
|
10 |
+
c. "You" (or "Your") shall mean a natural person or legal entity exercising the rights granted by this Agreement and/or using the Materials for any purpose and in any field of use.
|
11 |
+
d. "Third Parties" shall mean individuals or legal entities that are not under common control with Us or You.
|
12 |
+
e. "Tongyi Qianwen" shall mean the large language models (including Qwen model and Qwen-Chat model), and software and algorithms, consisting of trained model weights, parameters (including optimizer states), machine-learning model code, inference-enabling code, training-enabling code, fine-tuning enabling code and other elements of the foregoing distributed by Us.
|
13 |
+
f. "Materials" shall mean, collectively, Alibaba Cloud's proprietary Tongyi Qianwen and Documentation (and any portion thereof) made available under this Agreement.
|
14 |
+
g. "Source" form shall mean the preferred form for making modifications, including but not limited to model source code, documentation source, and configuration files.
|
15 |
+
h. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation,
|
16 |
+
and conversions to other media types.
|
17 |
+
|
18 |
+
2. Grant of Rights
|
19 |
+
You are granted a non-exclusive, worldwide, non-transferable and royalty-free limited license under Alibaba Cloud's intellectual property or other rights owned by Us embodied in the Materials to use, reproduce, distribute, copy, create derivative works of, and make modifications to the Materials.
|
20 |
+
|
21 |
+
3. Redistribution
|
22 |
+
You may reproduce and distribute copies of the Materials or derivative works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
|
23 |
+
a. You shall give any other recipients of the Materials or derivative works a copy of this Agreement;
|
24 |
+
b. You shall cause any modified files to carry prominent notices stating that You changed the files;
|
25 |
+
c. You shall retain in all copies of the Materials that You distribute the following attribution notices within a "Notice" text file distributed as a part of such copies: "Tongyi Qianwen is licensed under the Tongyi Qianwen LICENSE AGREEMENT, Copyright (c) Alibaba Cloud. All Rights Reserved."; and
|
26 |
+
d. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such derivative works as a whole, provided Your use, reproduction, and distribution of the work otherwise complies with the terms and conditions of this Agreement.
|
27 |
+
|
28 |
+
4. Restrictions
|
29 |
+
If you are commercially using the Materials, and your product or service has more than 100 million monthly active users, You shall request a license from Us. You cannot exercise your rights under this Agreement without our express authorization.
|
30 |
+
|
31 |
+
5. Rules of use
|
32 |
+
a. The Materials may be subject to export controls or restrictions in China, the United States or other countries or regions. You shall comply with applicable laws and regulations in your use of the Materials.
|
33 |
+
b. You can not use the Materials or any output therefrom to improve any other large language model (excluding Tongyi Qianwen or derivative works thereof).
|
34 |
+
|
35 |
+
6. Intellectual Property
|
36 |
+
a. We retain ownership of all intellectual property rights in and to the Materials and derivatives made by or for Us. Conditioned upon compliance with the terms and conditions of this Agreement, with respect to any derivative works and modifications of the Materials that are made by you, you are and will be the owner of such derivative works and modifications.
|
37 |
+
b. No trademark license is granted to use the trade names, trademarks, service marks, or product names of Us, except as required to fulfill notice requirements under this Agreement or as required for reasonable and customary use in describing and redistributing the Materials.
|
38 |
+
c. If you commence a lawsuit or other proceedings (including a cross-claim or counterclaim in a lawsuit) against Us or any entity alleging that the Materials or any output therefrom, or any part of the foregoing, infringe any intellectual property or other right owned or licensable by you, then all licences granted to you under this Agreement shall terminate as of the date such lawsuit or other proceeding is commenced or brought.
|
39 |
+
|
40 |
+
7. Disclaimer of Warranty and Limitation of Liability
|
41 |
+
|
42 |
+
a. We are not obligated to support, update, provide training for, or develop any further version of the Tongyi Qianwen Materials or to grant any license thereto.
|
43 |
+
b. THE MATERIALS ARE PROVIDED "AS IS" WITHOUT ANY EXPRESS OR IMPLIED WARRANTY OF ANY KIND INCLUDING WARRANTIES OF MERCHANTABILITY, NONINFRINGEMENT, OR FITNESS FOR A PARTICULAR PURPOSE. WE MAKE NO WARRANTY AND ASSUME NO RESPONSIBILITY FOR THE SAFETY OR STABILITY OF THE MATERIALS AND ANY OUTPUT THEREFROM.
|
44 |
+
c. IN NO EVENT SHALL WE BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE MATERIALS OR ANY OUTPUT OF IT, NO MATTER HOW IT’S CAUSED.
|
45 |
+
d. You will defend, indemnify and hold harmless Us from and against any claim by any third party arising out of or related to your use or distribution of the Materials.
|
46 |
+
|
47 |
+
8. Survival and Termination.
|
48 |
+
a. The term of this Agreement shall commence upon your acceptance of this Agreement or access to the Materials and will continue in full force and effect until terminated in accordance with the terms and conditions herein.
|
49 |
+
b. We may terminate this Agreement if you breach any of the terms or conditions of this Agreement. Upon termination of this Agreement, you must delete and cease use of the Materials. Sections 7 and 9 shall survive the termination of this Agreement.
|
50 |
+
|
51 |
+
9. Governing Law and Jurisdiction.
|
52 |
+
a. This Agreement and any dispute arising out of or relating to it will be governed by the laws of China, without regard to conflict of law principles, and the UN Convention on Contracts for the International Sale of Goods does not apply to this Agreement.
|
53 |
+
b. The People's Courts in Hangzhou City shall have exclusive jurisdiction over any dispute arising out of this Agreement.
|
Ovis/ovis.egg-info/PKG-INFO
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Metadata-Version: 2.1
|
2 |
+
Name: ovis
|
3 |
+
Version: 1.6.0
|
4 |
+
License-File: LICENSE
|
5 |
+
Requires-Dist: torch==2.4.0
|
6 |
+
Requires-Dist: transformers==4.46.2
|
7 |
+
Requires-Dist: tokenizers==0.20.3
|
8 |
+
Requires-Dist: sentencepiece==0.1.99
|
9 |
+
Requires-Dist: pyarrow==14.0.2
|
10 |
+
Requires-Dist: accelerate==1.1.0
|
11 |
+
Requires-Dist: pydantic==2.8.2
|
12 |
+
Requires-Dist: markdown2[all]
|
13 |
+
Requires-Dist: numpy==1.24.3
|
14 |
+
Requires-Dist: scikit-learn==1.2.2
|
15 |
+
Requires-Dist: requests
|
16 |
+
Requires-Dist: httpx
|
17 |
+
Requires-Dist: uvicorn
|
18 |
+
Requires-Dist: fastapi==0.112.4
|
19 |
+
Requires-Dist: einops==0.6.1
|
20 |
+
Requires-Dist: einops-exts==0.0.4
|
21 |
+
Requires-Dist: timm==0.6.13
|
22 |
+
Requires-Dist: tiktoken
|
23 |
+
Requires-Dist: transformers_stream_generator==0.0.4
|
24 |
+
Requires-Dist: scipy
|
25 |
+
Requires-Dist: pandas
|
26 |
+
Requires-Dist: torchaudio
|
27 |
+
Requires-Dist: xformers
|
28 |
+
Requires-Dist: pillow==10.3.0
|
29 |
+
Requires-Dist: deepspeed==0.15.4
|
30 |
+
Requires-Dist: gradio
|
Ovis/ovis.egg-info/SOURCES.txt
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
LICENSE
|
2 |
+
README.md
|
3 |
+
setup.py
|
4 |
+
ovis/__init__.py
|
5 |
+
ovis.egg-info/PKG-INFO
|
6 |
+
ovis.egg-info/SOURCES.txt
|
7 |
+
ovis.egg-info/dependency_links.txt
|
8 |
+
ovis.egg-info/requires.txt
|
9 |
+
ovis.egg-info/top_level.txt
|
10 |
+
ovis/model/__init__.py
|
11 |
+
ovis/model/configuration_ovis.py
|
12 |
+
ovis/model/conversation_formatter.py
|
13 |
+
ovis/model/modeling_ovis.py
|
14 |
+
ovis/train/__init__.py
|
15 |
+
ovis/train/arguments.py
|
16 |
+
ovis/train/callback.py
|
17 |
+
ovis/train/train.py
|
18 |
+
ovis/train/dataset/__init__.py
|
19 |
+
ovis/train/dataset/caption_dataset.py
|
20 |
+
ovis/train/dataset/conversation_dataset.py
|
21 |
+
ovis/train/dataset/multimodal_dataset.py
|
22 |
+
ovis/util/__init__.py
|
23 |
+
ovis/util/constants.py
|
24 |
+
ovis/util/utils.py
|
Ovis/ovis.egg-info/dependency_links.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
|
Ovis/ovis.egg-info/requires.txt
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
torch==2.4.0
|
2 |
+
transformers==4.46.2
|
3 |
+
tokenizers==0.20.3
|
4 |
+
sentencepiece==0.1.99
|
5 |
+
pyarrow==14.0.2
|
6 |
+
accelerate==1.1.0
|
7 |
+
pydantic==2.8.2
|
8 |
+
markdown2[all]
|
9 |
+
numpy==1.24.3
|
10 |
+
scikit-learn==1.2.2
|
11 |
+
requests
|
12 |
+
httpx
|
13 |
+
uvicorn
|
14 |
+
fastapi==0.112.4
|
15 |
+
einops==0.6.1
|
16 |
+
einops-exts==0.0.4
|
17 |
+
timm==0.6.13
|
18 |
+
tiktoken
|
19 |
+
transformers_stream_generator==0.0.4
|
20 |
+
scipy
|
21 |
+
pandas
|
22 |
+
torchaudio
|
23 |
+
xformers
|
24 |
+
pillow==10.3.0
|
25 |
+
deepspeed==0.15.4
|
26 |
+
gradio
|
Ovis/ovis.egg-info/top_level.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
ovis
|
Ovis/ovis/__init__.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
Ovis/ovis/model/__init__.py
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
from .visual_tokenizer.clip_visual_tokenizer import ClipVisualTokenizerConfig, ClipVisualTokenizer
|
2 |
+
from .visual_tokenizer.siglip_visual_tokenizer import SiglipVisualTokenizerConfig, SiglipVisualTokenizer
|
Ovis/ovis/model/__pycache__/conversation_formatter.cpython-311.pyc
ADDED
Binary file (12 kB). View file
|
|
Ovis/ovis/model/__pycache__/modeling_ovis.cpython-310.pyc
ADDED
Binary file (14 kB). View file
|
|
Ovis/ovis/model/modeling_ovis.py
ADDED
@@ -0,0 +1,434 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
|
4 |
+
from packaging import version
|
5 |
+
from datetime import datetime
|
6 |
+
from importlib import import_module
|
7 |
+
from typing import List, Union, Callable, Optional, Dict
|
8 |
+
|
9 |
+
import PIL.Image
|
10 |
+
import deepspeed
|
11 |
+
import torch
|
12 |
+
import transformers
|
13 |
+
from torch import Tensor
|
14 |
+
from torch.nn import init
|
15 |
+
from transformers import PreTrainedModel, AutoConfig, AutoModel, AutoTokenizer, AutoModelForCausalLM
|
16 |
+
from transformers.cache_utils import HybridCache
|
17 |
+
from transformers.generation.utils import GenerateOutput
|
18 |
+
from transformers.integrations.deepspeed import is_deepspeed_zero3_enabled, deepspeed_config
|
19 |
+
|
20 |
+
from ovis.model.configuration_ovis import OvisConfig
|
21 |
+
from ovis.model.conversation_formatter import ConversationFormatter
|
22 |
+
from ovis.util.constants import IGNORE_ID, BEGIN_LINE, END_LINE, IMAGE_ATOM_ID, IMAGE_INDICATOR_IDS, \
|
23 |
+
IMAGE_TOKEN_ID
|
24 |
+
from ovis.util.utils import rank0_print
|
25 |
+
|
26 |
+
|
27 |
+
class VisualEmbedding(torch.nn.Embedding):
|
28 |
+
def forward(self, visual_tokens: Tensor) -> Tensor:
|
29 |
+
if visual_tokens.dtype in [torch.int8, torch.int16, torch.int32, torch.int64, torch.long]:
|
30 |
+
return super().forward(visual_tokens)
|
31 |
+
return torch.matmul(visual_tokens, self.weight)
|
32 |
+
|
33 |
+
def reset_parameters(self, mean=0., std=1.) -> None:
|
34 |
+
init.normal_(self.weight, mean=mean, std=std)
|
35 |
+
self._fill_padding_idx_with_zero()
|
36 |
+
|
37 |
+
|
38 |
+
class OvisPreTrainedModel(PreTrainedModel):
|
39 |
+
config_class = OvisConfig
|
40 |
+
base_model_prefix = "ovis"
|
41 |
+
|
42 |
+
|
43 |
+
class Ovis(OvisPreTrainedModel):
|
44 |
+
|
45 |
+
def __init__(self, config: OvisConfig, *inputs, **kwargs):
|
46 |
+
super().__init__(config, *inputs, **kwargs)
|
47 |
+
if kwargs.get('train_from_scratch'):
|
48 |
+
self.llm = kwargs['llm']
|
49 |
+
self.generation_config = self.llm.generation_config
|
50 |
+
self.config.llm_config = self.llm.config
|
51 |
+
self.config.hidden_size = self.llm.config.hidden_size # for deepspeed auto configuration
|
52 |
+
self.text_tokenizer = kwargs['text_tokenizer']
|
53 |
+
self.visual_tokenizer = kwargs['visual_tokenizer']
|
54 |
+
self.config.visual_tokenizer_config = self.visual_tokenizer.config
|
55 |
+
else:
|
56 |
+
attn_kwargs = dict()
|
57 |
+
if self.config.llm_attn_implementation:
|
58 |
+
attn_kwargs['attn_implementation'] = self.config.llm_attn_implementation
|
59 |
+
self.llm = AutoModelForCausalLM.from_config(self.config.llm_config, **attn_kwargs)
|
60 |
+
assert self.config.hidden_size == self.llm.config.hidden_size, "hidden size mismatch"
|
61 |
+
self.text_tokenizer = AutoTokenizer.from_pretrained(self.config.name_or_path)
|
62 |
+
self.visual_tokenizer = AutoModel.from_config(self.config.visual_tokenizer_config,
|
63 |
+
image_processor_name_or_path=self.config.name_or_path)
|
64 |
+
|
65 |
+
# initialize vte
|
66 |
+
if is_deepspeed_zero3_enabled():
|
67 |
+
with deepspeed.zero.Init(config_dict_or_path=deepspeed_config()):
|
68 |
+
self.vte = VisualEmbedding(self.config.visual_tokenizer_config.vocab_size, self.config.hidden_size)
|
69 |
+
else:
|
70 |
+
self.vte = VisualEmbedding(self.config.visual_tokenizer_config.vocab_size, self.config.hidden_size,
|
71 |
+
device=self.visual_tokenizer.device, dtype=self.visual_tokenizer.dtype)
|
72 |
+
|
73 |
+
def _merge_modules(modules_list: tuple):
|
74 |
+
merged_modules = []
|
75 |
+
for modules in modules_list:
|
76 |
+
merged_modules.extend(modules if modules else [])
|
77 |
+
return merged_modules
|
78 |
+
|
79 |
+
self._no_split_modules = _merge_modules((self.llm._no_split_modules, self.visual_tokenizer._no_split_modules))
|
80 |
+
self._skip_keys_device_placement = self.llm._skip_keys_device_placement
|
81 |
+
self._keep_in_fp32_modules = _merge_modules(
|
82 |
+
(self.llm._keep_in_fp32_modules, self.visual_tokenizer._keep_in_fp32_modules))
|
83 |
+
self.is_parallelizable = all((self.llm.is_parallelizable, self.visual_tokenizer.is_parallelizable))
|
84 |
+
self.supports_gradient_checkpointing = all(
|
85 |
+
(self.llm.supports_gradient_checkpointing, self.visual_tokenizer.supports_gradient_checkpointing))
|
86 |
+
self._supports_flash_attn_2 = all(
|
87 |
+
(self.llm._supports_flash_attn_2, self.visual_tokenizer._supports_flash_attn_2))
|
88 |
+
self._supports_sdpa = all((self.llm._supports_sdpa, self.visual_tokenizer._supports_sdpa))
|
89 |
+
|
90 |
+
def get_text_tokenizer(self):
|
91 |
+
return self.text_tokenizer
|
92 |
+
|
93 |
+
def get_visual_tokenizer(self):
|
94 |
+
return self.visual_tokenizer
|
95 |
+
|
96 |
+
def tie_weights(self):
|
97 |
+
if not self.config.disable_tie_weight:
|
98 |
+
self.get_llm().tie_weights()
|
99 |
+
|
100 |
+
def re_init_vte(self, mean, std):
|
101 |
+
vte = self.get_vte()
|
102 |
+
rank0_print(BEGIN_LINE)
|
103 |
+
rank0_print(f'[{datetime.now()}] Before re-initialization of vte: ')
|
104 |
+
with deepspeed.zero.GatheredParameters([vte.weight]):
|
105 |
+
rank0_print(f'vte.weight: {vte.weight}')
|
106 |
+
with deepspeed.zero.GatheredParameters([vte.weight], modifier_rank=0):
|
107 |
+
if not is_deepspeed_zero3_enabled() or deepspeed.comm.get_rank() == 0:
|
108 |
+
vte.reset_parameters(mean, std)
|
109 |
+
rank0_print(f'[{datetime.now()}] After re-initialization of vte:')
|
110 |
+
with deepspeed.zero.GatheredParameters([vte.weight]):
|
111 |
+
rank0_print(f'vte.weight: {vte.weight}')
|
112 |
+
rank0_print(END_LINE)
|
113 |
+
|
114 |
+
def get_monitor_tensors(self):
|
115 |
+
monitor_tensors = dict(
|
116 |
+
wte=self.get_wte().weight,
|
117 |
+
lm_head=self.get_lm_head().weight,
|
118 |
+
vte=self.get_vte().weight
|
119 |
+
)
|
120 |
+
monitor_tensors.update(
|
121 |
+
{f'visual_tokenizer_{k}': v for k, v in self.get_visual_tokenizer().get_monitor_tensors().items()})
|
122 |
+
return monitor_tensors
|
123 |
+
|
124 |
+
def get_lm_head(self):
|
125 |
+
return self.get_llm().get_output_embeddings()
|
126 |
+
|
127 |
+
def get_llm(self):
|
128 |
+
return self.llm
|
129 |
+
|
130 |
+
def get_vte(self):
|
131 |
+
return self.vte
|
132 |
+
|
133 |
+
def get_wte(self):
|
134 |
+
return self.llm.get_input_embeddings()
|
135 |
+
|
136 |
+
def get_conversation_formatter(self) -> ConversationFormatter:
|
137 |
+
if getattr(self, 'conversation_formatter', None) is None:
|
138 |
+
self.conversation_formatter = getattr(import_module(".conversation_formatter", __package__),
|
139 |
+
self.config.conversation_formatter_class)(self.text_tokenizer)
|
140 |
+
return self.conversation_formatter
|
141 |
+
|
142 |
+
def forward(
|
143 |
+
self,
|
144 |
+
input_ids: torch.Tensor,
|
145 |
+
attention_mask: torch.Tensor,
|
146 |
+
labels: Optional[torch.Tensor],
|
147 |
+
pixel_values: List[Optional[torch.Tensor]],
|
148 |
+
**kwargs
|
149 |
+
):
|
150 |
+
assert self.training, "`forward` can only be used in training. For inference, use `generate`."
|
151 |
+
_, inputs_embeds, labels, attention_mask = self.merge_multimodal(
|
152 |
+
text_input_ids=input_ids,
|
153 |
+
text_attention_masks=attention_mask,
|
154 |
+
text_labels=labels,
|
155 |
+
pixel_values=pixel_values
|
156 |
+
)
|
157 |
+
return self.llm(inputs_embeds=inputs_embeds, labels=labels, attention_mask=attention_mask, **kwargs)
|
158 |
+
|
159 |
+
def merge_multimodal(
|
160 |
+
self,
|
161 |
+
text_input_ids: torch.Tensor,
|
162 |
+
text_attention_masks: torch.Tensor,
|
163 |
+
text_labels: Optional[torch.Tensor],
|
164 |
+
pixel_values: List[Optional[torch.Tensor]]
|
165 |
+
):
|
166 |
+
input_device = text_input_ids.device
|
167 |
+
visual_vocab_szie = self.get_visual_tokenizer().config.vocab_size
|
168 |
+
visual_indicator_embeds = self.get_vte()(
|
169 |
+
torch.tensor(
|
170 |
+
list(range(visual_vocab_szie - 5, visual_vocab_szie)),
|
171 |
+
dtype=torch.long,
|
172 |
+
device=self.get_visual_tokenizer().device
|
173 |
+
)
|
174 |
+
).to(device=input_device)
|
175 |
+
|
176 |
+
if self.training:
|
177 |
+
# When training, to be compatible with deepspeed zero, each sample has to include pixel_value tensor.
|
178 |
+
# For text-only sample, one can simply use a full zero tensor as pixel_value, which will be ignored
|
179 |
+
# (see below in this function); so, the gradient will not be affected.
|
180 |
+
num_images = [x.shape[0] for x in pixel_values]
|
181 |
+
visual_tokens = self.visual_tokenizer(torch.cat([x for x in pixel_values], dim=0))
|
182 |
+
visual_embeds = torch.split(self.get_vte()(visual_tokens).to(dtype=self.dtype, device=input_device),
|
183 |
+
split_size_or_sections=num_images, dim=0)
|
184 |
+
visual_input_ids = torch.split(torch.argmax(visual_tokens, dim=-1).to(device=input_device),
|
185 |
+
split_size_or_sections=num_images, dim=0)
|
186 |
+
visual_labels = [torch.full(x.shape, IGNORE_ID, dtype=torch.long, device=input_device) for x in
|
187 |
+
visual_input_ids]
|
188 |
+
else:
|
189 |
+
# When inference, sample can include only text with `None` pixel_value
|
190 |
+
num_images = [x.shape[0] if x is not None else 0 for x in pixel_values]
|
191 |
+
if sum(num_images) > 0:
|
192 |
+
visual_tokens = self.visual_tokenizer(torch.cat([x for x in pixel_values if x is not None], dim=0))
|
193 |
+
visual_embeds = torch.split(self.get_vte()(visual_tokens).to(dtype=self.dtype, device=input_device),
|
194 |
+
split_size_or_sections=num_images, dim=0)
|
195 |
+
visual_input_ids = torch.split(torch.argmax(visual_tokens, dim=-1).to(device=input_device),
|
196 |
+
split_size_or_sections=num_images, dim=0)
|
197 |
+
visual_labels = [torch.full(x.shape, IGNORE_ID, dtype=torch.long, device=input_device) for x in
|
198 |
+
visual_input_ids]
|
199 |
+
else:
|
200 |
+
# just placeholders
|
201 |
+
visual_embeds = [None] * len(num_images)
|
202 |
+
visual_input_ids = [None] * len(num_images)
|
203 |
+
visual_labels = [None] * len(num_images)
|
204 |
+
# just placeholders
|
205 |
+
text_labels = torch.full(text_input_ids.shape, IGNORE_ID, dtype=torch.long, device=input_device)
|
206 |
+
|
207 |
+
input_embeds = []
|
208 |
+
attention_masks = []
|
209 |
+
labels = []
|
210 |
+
for text_input_id, text_label, text_attention_mask, visual_embed, visual_input_id, visual_label in zip(
|
211 |
+
text_input_ids, text_labels, text_attention_masks, visual_embeds, visual_input_ids, visual_labels
|
212 |
+
):
|
213 |
+
placeholder_token_mask = torch.lt(text_input_id, 0)
|
214 |
+
text_embed = self.get_wte()(torch.masked_fill(text_input_id, placeholder_token_mask, 0))
|
215 |
+
for i, indicator_id in enumerate(IMAGE_INDICATOR_IDS):
|
216 |
+
text_embed[text_input_id == indicator_id] = visual_indicator_embeds[i]
|
217 |
+
image_atom_positions = torch.where(torch.eq(text_input_id, IMAGE_ATOM_ID))[0].tolist()
|
218 |
+
if len(image_atom_positions) > 0:
|
219 |
+
input_embed_parts = []
|
220 |
+
attention_mask_parts = []
|
221 |
+
label_parts = []
|
222 |
+
prev_image_atom_position = -1
|
223 |
+
for index, image_atom_position in enumerate(image_atom_positions):
|
224 |
+
input_embed_parts.append(
|
225 |
+
text_embed[prev_image_atom_position + 1:image_atom_position, :])
|
226 |
+
label_parts.append(
|
227 |
+
text_label[prev_image_atom_position + 1:image_atom_position])
|
228 |
+
attention_mask_parts.append(
|
229 |
+
text_attention_mask[prev_image_atom_position + 1:image_atom_position])
|
230 |
+
input_embed_parts.append(visual_embed[index])
|
231 |
+
attention_mask_parts.append(
|
232 |
+
torch.ones_like(visual_label[index], dtype=torch.bool))
|
233 |
+
label_parts.append(visual_label[index])
|
234 |
+
prev_image_atom_position = image_atom_position
|
235 |
+
if prev_image_atom_position + 1 < text_input_id.shape[0]:
|
236 |
+
input_embed_parts.append(
|
237 |
+
text_embed[prev_image_atom_position + 1:, :])
|
238 |
+
attention_mask_parts.append(
|
239 |
+
text_attention_mask[prev_image_atom_position + 1:])
|
240 |
+
label_parts.append(
|
241 |
+
text_label[prev_image_atom_position + 1:])
|
242 |
+
input_embed = torch.cat(input_embed_parts, dim=0)
|
243 |
+
attention_mask = torch.cat(attention_mask_parts, dim=0)
|
244 |
+
label = torch.cat(label_parts, dim=0)
|
245 |
+
else:
|
246 |
+
input_embed = text_embed
|
247 |
+
attention_mask = text_attention_mask
|
248 |
+
label = text_label
|
249 |
+
if self.training:
|
250 |
+
# Make visual_embed & visual_indicator_embeds involved in the backward graph,
|
251 |
+
# to be compatible with deepspeed zero and ddp.
|
252 |
+
input_embed += torch.sum(visual_embed * 0.0) + torch.sum(visual_indicator_embeds * 0.0)
|
253 |
+
input_embeds.append(input_embed)
|
254 |
+
attention_masks.append(attention_mask)
|
255 |
+
labels.append(label)
|
256 |
+
|
257 |
+
if self.training: # padding to self.config.multimodal_max_length for increased training speed
|
258 |
+
padding_size = max(0, self.config.multimodal_max_length - len(input_embeds[0]))
|
259 |
+
input_embeds[0] = torch.nn.ConstantPad2d((0, 0, 0, padding_size), 0.0)(input_embeds[0])
|
260 |
+
attention_masks[0] = torch.nn.ConstantPad1d((0, padding_size), False)(attention_masks[0])
|
261 |
+
labels[0] = torch.nn.ConstantPad1d((0, padding_size), IGNORE_ID)(labels[0])
|
262 |
+
batch_input_embeds = torch.nn.utils.rnn.pad_sequence(input_embeds, batch_first=True, padding_value=0.0)[:,
|
263 |
+
:self.config.multimodal_max_length, :]
|
264 |
+
batch_attention_mask = torch.nn.utils.rnn.pad_sequence(attention_masks, batch_first=True, padding_value=False)[
|
265 |
+
:,
|
266 |
+
:self.config.multimodal_max_length]
|
267 |
+
batch_labels = torch.nn.utils.rnn.pad_sequence(labels, batch_first=True, padding_value=IGNORE_ID)[:,
|
268 |
+
:self.config.multimodal_max_length]
|
269 |
+
|
270 |
+
return visual_input_ids, batch_input_embeds, batch_labels, batch_attention_mask
|
271 |
+
|
272 |
+
def preprocess_inputs(
|
273 |
+
self,
|
274 |
+
text_or_conversations: Union[List[Dict], str],
|
275 |
+
images: Optional[List[PIL.Image.Image]],
|
276 |
+
max_partition=9,
|
277 |
+
generation_preface='',
|
278 |
+
return_labels=False,
|
279 |
+
propagate_exception=True
|
280 |
+
):
|
281 |
+
# convert text to conversations
|
282 |
+
if isinstance(text_or_conversations, str):
|
283 |
+
conversations = [{
|
284 |
+
"from": "human",
|
285 |
+
"value": text_or_conversations
|
286 |
+
}]
|
287 |
+
elif isinstance(text_or_conversations, list):
|
288 |
+
conversations = text_or_conversations
|
289 |
+
else:
|
290 |
+
raise ValueError(f'Invalid type of `text_or_conversations`, expected `List[Dict]` or `str`,'
|
291 |
+
f' but got {type(text_or_conversations)}')
|
292 |
+
|
293 |
+
# format conversations
|
294 |
+
prompt, raw_input_ids, raw_labels = self.get_conversation_formatter().format(
|
295 |
+
conversations, generation_preface=generation_preface)
|
296 |
+
|
297 |
+
# place image placeholders
|
298 |
+
input_ids = []
|
299 |
+
labels = []
|
300 |
+
pixel_values = []
|
301 |
+
invalidate_label = False
|
302 |
+
image_token_indices = [i for i, v in enumerate(raw_input_ids) if v == IMAGE_TOKEN_ID]
|
303 |
+
last_image_token_index = -1
|
304 |
+
for i in range(len(image_token_indices)):
|
305 |
+
head = 0 if i == 0 else image_token_indices[i - 1] + 1
|
306 |
+
tail = image_token_indices[i]
|
307 |
+
last_image_token_index = tail
|
308 |
+
input_ids.extend(raw_input_ids[head:tail])
|
309 |
+
labels.extend(raw_labels[head:tail])
|
310 |
+
try:
|
311 |
+
image = images[i]
|
312 |
+
raw_pixel_values, image_placeholders = self.visual_tokenizer.preprocess_image(
|
313 |
+
image, max_partition=max_partition)
|
314 |
+
except Exception as e:
|
315 |
+
if propagate_exception:
|
316 |
+
raise e
|
317 |
+
logging.exception(e)
|
318 |
+
invalidate_label = True
|
319 |
+
raw_pixel_values, image_placeholders = self.visual_tokenizer.mock_input()
|
320 |
+
input_ids.extend(image_placeholders)
|
321 |
+
labels.extend([IGNORE_ID] * len(image_placeholders))
|
322 |
+
pixel_values.append(raw_pixel_values)
|
323 |
+
input_ids.extend(raw_input_ids[last_image_token_index + 1:])
|
324 |
+
labels.extend(raw_labels[last_image_token_index + 1:])
|
325 |
+
|
326 |
+
# return tensors
|
327 |
+
input_ids = torch.tensor(input_ids, dtype=torch.long)
|
328 |
+
labels = torch.tensor([IGNORE_ID] * len(labels) if invalidate_label else labels, dtype=torch.long)
|
329 |
+
pixel_values = torch.cat(pixel_values, dim=0) if len(pixel_values) > 0 else None
|
330 |
+
|
331 |
+
if return_labels:
|
332 |
+
return prompt, input_ids, pixel_values, labels
|
333 |
+
else:
|
334 |
+
return prompt, input_ids, pixel_values
|
335 |
+
|
336 |
+
def save_pretrained(
|
337 |
+
self,
|
338 |
+
save_directory: Union[str, os.PathLike],
|
339 |
+
is_main_process: bool = True,
|
340 |
+
state_dict: Optional[dict] = None,
|
341 |
+
save_function: Callable = torch.save,
|
342 |
+
push_to_hub: bool = False,
|
343 |
+
max_shard_size: Union[int, str] = "5GB",
|
344 |
+
safe_serialization: bool = True,
|
345 |
+
variant: Optional[str] = None,
|
346 |
+
token: Optional[Union[str, bool]] = None,
|
347 |
+
save_peft_format: bool = True,
|
348 |
+
**kwargs
|
349 |
+
):
|
350 |
+
super().save_pretrained(save_directory,
|
351 |
+
is_main_process=is_main_process,
|
352 |
+
state_dict=state_dict,
|
353 |
+
save_function=save_function,
|
354 |
+
safe_serialization=safe_serialization)
|
355 |
+
self.get_text_tokenizer().save_pretrained(save_directory)
|
356 |
+
self.get_visual_tokenizer().get_image_processor().save_pretrained(save_directory)
|
357 |
+
|
358 |
+
# uncomment the following will additionally save a separate visual tokenizer
|
359 |
+
# visual_tokenizer_directory = os.path.join(save_directory, 'visual_tokenizer')
|
360 |
+
# self.get_visual_tokenizer().save_pretrained(visual_tokenizer_directory,
|
361 |
+
# is_main_process=is_main_process,
|
362 |
+
# state_dict=None,
|
363 |
+
# save_function=save_function,
|
364 |
+
# safe_serialization=safe_serialization)
|
365 |
+
# self.get_visual_tokenizer().get_image_processor().save_pretrained(visual_tokenizer_directory)
|
366 |
+
|
367 |
+
def _get_hybrid_cache_for_llm(self, batch_size: int, max_cache_len: int):
|
368 |
+
cache_cls = HybridCache
|
369 |
+
llm = self.get_llm()
|
370 |
+
|
371 |
+
if version.parse(transformers.__version__) >= version.parse("4.46.0"):
|
372 |
+
need_new_cache = (
|
373 |
+
not hasattr(llm, "_cache")
|
374 |
+
or (not isinstance(llm._cache, cache_cls))
|
375 |
+
or llm._cache.batch_size != batch_size
|
376 |
+
or llm._cache.max_cache_len < max_cache_len
|
377 |
+
)
|
378 |
+
else:
|
379 |
+
need_new_cache = (
|
380 |
+
not hasattr(llm, "_cache")
|
381 |
+
or (not isinstance(llm._cache, cache_cls))
|
382 |
+
or llm._cache.max_batch_size != batch_size
|
383 |
+
or llm._cache.max_cache_len < max_cache_len
|
384 |
+
)
|
385 |
+
|
386 |
+
if need_new_cache:
|
387 |
+
if hasattr(llm.config, "_pre_quantization_dtype"):
|
388 |
+
cache_dtype = llm.config._pre_quantization_dtype
|
389 |
+
else:
|
390 |
+
cache_dtype = llm.dtype
|
391 |
+
if version.parse(transformers.__version__) >= version.parse("4.46.0"):
|
392 |
+
llm._cache = cache_cls(
|
393 |
+
config=llm.config,
|
394 |
+
batch_size=batch_size,
|
395 |
+
max_cache_len=max_cache_len,
|
396 |
+
device=llm.device,
|
397 |
+
dtype=cache_dtype,
|
398 |
+
)
|
399 |
+
else:
|
400 |
+
llm._cache = cache_cls(
|
401 |
+
config=llm.config,
|
402 |
+
max_batch_size=batch_size,
|
403 |
+
max_cache_len=max_cache_len,
|
404 |
+
device=llm.device,
|
405 |
+
dtype=cache_dtype,
|
406 |
+
)
|
407 |
+
else:
|
408 |
+
llm._cache.reset()
|
409 |
+
return llm._cache
|
410 |
+
|
411 |
+
# TODO: support batch generation
|
412 |
+
def generate(
|
413 |
+
self,
|
414 |
+
inputs: Optional[torch.Tensor] = None,
|
415 |
+
**kwargs
|
416 |
+
) -> Union[GenerateOutput, torch.LongTensor]:
|
417 |
+
assert inputs.shape[0] == 1, 'Currently, only support `batch_size=1`'
|
418 |
+
_, inputs_embeds, labels, attention_mask = self.merge_multimodal(
|
419 |
+
text_input_ids=inputs,
|
420 |
+
text_attention_masks=kwargs.pop('attention_mask'),
|
421 |
+
text_labels=None,
|
422 |
+
pixel_values=kwargs.pop('pixel_values')
|
423 |
+
)
|
424 |
+
if getattr(self.generation_config, 'cache_implementation') == 'hybrid': # mainly for Gemma2
|
425 |
+
kwargs['past_key_values'] = self._get_hybrid_cache_for_llm(
|
426 |
+
getattr(kwargs, "num_beams", 1), kwargs['max_new_tokens'] + inputs_embeds.shape[-2])
|
427 |
+
self.get_llm()._supports_cache_class = True
|
428 |
+
kwargs['cache_implementation'] = None
|
429 |
+
|
430 |
+
return self.llm.generate(inputs=None, inputs_embeds=inputs_embeds, attention_mask=attention_mask, **kwargs)
|
431 |
+
|
432 |
+
|
433 |
+
AutoConfig.register("ovis", OvisConfig)
|
434 |
+
AutoModelForCausalLM.register(OvisConfig, Ovis)
|
Ovis/ovis/model/visual_tokenizer/base_visual_tokenizer.py
ADDED
@@ -0,0 +1,264 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Union, Optional
|
2 |
+
|
3 |
+
import PIL.Image
|
4 |
+
import torch
|
5 |
+
from torch.nn.functional import softmax, gumbel_softmax, pad
|
6 |
+
from transformers import PretrainedConfig, PreTrainedModel, AutoImageProcessor, AutoModel, AutoConfig
|
7 |
+
from ovis.util.constants import IMAGE_INDICATOR_IDS, IMAGE_ATOM_ID
|
8 |
+
|
9 |
+
|
10 |
+
class BaseVisualTokenizerConfig(PretrainedConfig):
|
11 |
+
def __init__(
|
12 |
+
self,
|
13 |
+
vocab_size=16384,
|
14 |
+
tokenize_function="softmax",
|
15 |
+
tau=1.0,
|
16 |
+
depths=None,
|
17 |
+
drop_cls_token=False,
|
18 |
+
backbone_config: Optional[Union[PretrainedConfig, dict]] = None,
|
19 |
+
hidden_stride: int = 1,
|
20 |
+
**kwargs
|
21 |
+
):
|
22 |
+
super().__init__(**kwargs)
|
23 |
+
self.vocab_size = vocab_size
|
24 |
+
self.tokenize_function = tokenize_function
|
25 |
+
self.tau = tau
|
26 |
+
if isinstance(depths, str):
|
27 |
+
depths = [int(x) for x in depths.split('|')]
|
28 |
+
self.depths = depths
|
29 |
+
self.backbone_kwargs = {}
|
30 |
+
self.drop_cls_token = drop_cls_token
|
31 |
+
if backbone_config is not None:
|
32 |
+
assert isinstance(backbone_config, (PretrainedConfig, dict)), \
|
33 |
+
f"expect `backbone_config` to be instance of PretrainedConfig or dict, but got {type(backbone_config)} type"
|
34 |
+
if not isinstance(backbone_config, PretrainedConfig):
|
35 |
+
model_type = backbone_config['model_type']
|
36 |
+
backbone_config.pop('model_type')
|
37 |
+
backbone_config = AutoConfig.for_model(model_type, **backbone_config)
|
38 |
+
self.backbone_config = backbone_config
|
39 |
+
self.hidden_stride = hidden_stride
|
40 |
+
|
41 |
+
|
42 |
+
class BaseVisualTokenizer(PreTrainedModel):
|
43 |
+
base_model_prefix = "backbone"
|
44 |
+
main_input_name = None
|
45 |
+
_image_processor_class = None
|
46 |
+
_image_processor_kwargs = {}
|
47 |
+
_backbone_class = None
|
48 |
+
_backbone_name_or_path = None
|
49 |
+
|
50 |
+
def __init__(self, config: BaseVisualTokenizerConfig, *inputs, **kwargs):
|
51 |
+
super().__init__(config, *inputs, **kwargs)
|
52 |
+
if kwargs.get('train_from_scratch'):
|
53 |
+
self.image_processor = self._image_processor_class.from_pretrained(self._backbone_name_or_path,
|
54 |
+
**self._image_processor_kwargs)
|
55 |
+
self.backbone = self._backbone_class.from_pretrained(self._backbone_name_or_path,
|
56 |
+
**self.config.backbone_kwargs)
|
57 |
+
self.config.backbone_config = self.backbone.config
|
58 |
+
else:
|
59 |
+
self.image_processor = AutoImageProcessor.from_pretrained(kwargs['image_processor_name_or_path'])
|
60 |
+
self.backbone = AutoModel.from_config(self.config.backbone_config)
|
61 |
+
head_dim = self.config.vocab_size - len(IMAGE_INDICATOR_IDS) # reserved tokens for IMAGE_INDICATORS
|
62 |
+
self.head = torch.nn.Sequential(
|
63 |
+
torch.nn.Linear(
|
64 |
+
self.backbone.config.hidden_size * self.config.hidden_stride * self.config.hidden_stride, head_dim,
|
65 |
+
bias=False
|
66 |
+
),
|
67 |
+
torch.nn.LayerNorm(head_dim)
|
68 |
+
)
|
69 |
+
|
70 |
+
assert all((self.image_processor.do_resize,
|
71 |
+
not getattr(self.image_processor, 'do_center_crop', False),
|
72 |
+
self.image_processor.do_rescale,
|
73 |
+
self.image_processor.do_normalize
|
74 |
+
)), f"image_processor `{self.image_processor}` is not supported currently"
|
75 |
+
|
76 |
+
def get_backbone(self):
|
77 |
+
return self.backbone
|
78 |
+
|
79 |
+
def get_monitor_tensors(self):
|
80 |
+
raise NotImplementedError
|
81 |
+
|
82 |
+
def get_image_processor(self):
|
83 |
+
return self.image_processor
|
84 |
+
|
85 |
+
def mock_input(self):
|
86 |
+
height, width = self.get_image_size()
|
87 |
+
return torch.zeros(1, 3, height, width), self.construct_image_placeholders((1, 1))
|
88 |
+
|
89 |
+
def get_head(self):
|
90 |
+
return self.head
|
91 |
+
|
92 |
+
def get_image_size(self):
|
93 |
+
raise NotImplementedError
|
94 |
+
|
95 |
+
@staticmethod
|
96 |
+
def construct_image_placeholders(grid):
|
97 |
+
image_placeholders = [IMAGE_INDICATOR_IDS[0], IMAGE_ATOM_ID, IMAGE_INDICATOR_IDS[1]]
|
98 |
+
if grid[0] * grid[1] > 1:
|
99 |
+
for r in range(grid[0]):
|
100 |
+
for c in range(grid[1]):
|
101 |
+
image_placeholders.append(IMAGE_ATOM_ID)
|
102 |
+
if c < grid[1] - 1:
|
103 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[2])
|
104 |
+
if r < grid[0] - 1:
|
105 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[3])
|
106 |
+
image_placeholders.append(IMAGE_INDICATOR_IDS[4])
|
107 |
+
return image_placeholders
|
108 |
+
|
109 |
+
def preprocess_image(self, image: PIL.Image.Image, max_partition=9, covering_threshold=0.9, convert_to_rgb=True):
|
110 |
+
def _preprocess(img: PIL.Image.Image, side):
|
111 |
+
# first resize and preprocess
|
112 |
+
w, h = img.size
|
113 |
+
if w == h:
|
114 |
+
new_width = new_height = side
|
115 |
+
elif w > h:
|
116 |
+
new_width = side
|
117 |
+
new_height = int(h / w * new_width)
|
118 |
+
else:
|
119 |
+
new_height = side
|
120 |
+
new_width = int(w / h * new_height)
|
121 |
+
new_size = dict(height=new_height, width=new_width)
|
122 |
+
pixel_values = self.image_processor.preprocess(img, size=new_size, return_tensors='pt')['pixel_values']
|
123 |
+
|
124 |
+
# then pad to square
|
125 |
+
square_values = torch.zeros([1, 3, side, side], dtype=pixel_values.dtype, device=pixel_values.device)
|
126 |
+
new_height, new_width = pixel_values.shape[2:]
|
127 |
+
if new_height == new_width:
|
128 |
+
square_values[:, :, :, :] = pixel_values
|
129 |
+
elif new_height > new_width:
|
130 |
+
from_index = (side - new_width) // 2
|
131 |
+
square_values[:, :, :, from_index:from_index + new_width] = pixel_values
|
132 |
+
else:
|
133 |
+
from_index = (side - new_height) // 2
|
134 |
+
square_values[:, :, from_index:from_index + new_height, :] = pixel_values
|
135 |
+
|
136 |
+
return square_values
|
137 |
+
|
138 |
+
def _partition(img, grid):
|
139 |
+
w, h = img.size
|
140 |
+
row_height = h // grid[0]
|
141 |
+
col_width = w // grid[1]
|
142 |
+
|
143 |
+
partition = []
|
144 |
+
for row in range(grid[0]):
|
145 |
+
for col in range(grid[1]):
|
146 |
+
left = col * col_width
|
147 |
+
upper = row * row_height
|
148 |
+
right = w if col == grid[1] - 1 else (col + 1) * col_width
|
149 |
+
lower = h if row == grid[0] - 1 else (row + 1) * row_height
|
150 |
+
partition.append((left, upper, right, lower))
|
151 |
+
|
152 |
+
return partition
|
153 |
+
|
154 |
+
def _covering_area(left, upper, right, lower, side):
|
155 |
+
w = right - left
|
156 |
+
h = lower - upper
|
157 |
+
w, h = max(w, h), min(w, h)
|
158 |
+
if w > side:
|
159 |
+
h = h / w * side
|
160 |
+
w = side
|
161 |
+
return w * h
|
162 |
+
|
163 |
+
def _get_best_grid(img, side):
|
164 |
+
img_area = img.size[0] * img.size[1]
|
165 |
+
|
166 |
+
candidate_grids = []
|
167 |
+
for i in range(1, max_partition + 1):
|
168 |
+
for j in range(1, max_partition + 1):
|
169 |
+
if i * j <= max_partition:
|
170 |
+
candidate_grids.append((i, j))
|
171 |
+
|
172 |
+
all_grids = []
|
173 |
+
good_grids = []
|
174 |
+
for grid in candidate_grids:
|
175 |
+
partition = _partition(img, grid)
|
176 |
+
covering_ratio = sum([_covering_area(*p, side) for p in partition]) / img_area
|
177 |
+
assert covering_ratio <= 1.0
|
178 |
+
all_grids.append((grid, covering_ratio))
|
179 |
+
if covering_ratio > covering_threshold:
|
180 |
+
good_grids.append((grid, covering_ratio))
|
181 |
+
|
182 |
+
if len(good_grids) > 0:
|
183 |
+
# pick the good partition with minimum #sub_images and break the tie using covering_ratio
|
184 |
+
return sorted(good_grids, key=lambda x: (x[0][0] * x[0][1], -x[1]))[0][0]
|
185 |
+
else:
|
186 |
+
# pick the partition with maximum covering_ratio and break the tie using #sub_images
|
187 |
+
return sorted(all_grids, key=lambda x: (-x[1], x[0][0] * x[0][1]))[0][0]
|
188 |
+
|
189 |
+
if convert_to_rgb and image.mode != 'RGB':
|
190 |
+
image = image.convert('RGB')
|
191 |
+
|
192 |
+
sides = self.get_image_size()
|
193 |
+
if sides[0] != sides[1]:
|
194 |
+
raise ValueError('get_image_size() returns non-square size')
|
195 |
+
side = sides[0]
|
196 |
+
grid = _get_best_grid(image, side)
|
197 |
+
partition = _partition(image, grid)
|
198 |
+
crops = [image.crop(p) for p in partition]
|
199 |
+
if len(crops) > 1:
|
200 |
+
crops.insert(0, image)
|
201 |
+
pixel_values = torch.cat([_preprocess(crop, side) for crop in crops], dim=0)
|
202 |
+
image_placeholders = self.construct_image_placeholders(grid)
|
203 |
+
return pixel_values, image_placeholders
|
204 |
+
|
205 |
+
def get_backbone_layer(self, index):
|
206 |
+
return self.backbone.vision_model.encoder.layers[index]
|
207 |
+
|
208 |
+
def tokenize(self, logits):
|
209 |
+
def st_argmax(y_soft, dim): # straight-through softmax
|
210 |
+
index = y_soft.max(dim, keepdim=True)[1]
|
211 |
+
y_hard = torch.zeros_like(y_soft, memory_format=torch.legacy_contiguous_format).scatter_(dim, index, 1.0)
|
212 |
+
ret = y_hard - y_soft.detach() + y_soft
|
213 |
+
return ret
|
214 |
+
|
215 |
+
if self.config.tokenize_function == 'softmax':
|
216 |
+
tokens = softmax(logits, dim=-1)
|
217 |
+
elif self.config.tokenize_function == 'gumbel_argmax':
|
218 |
+
tokens = gumbel_softmax(logits, tau=self.config.tau, hard=True)
|
219 |
+
elif self.config.tokenize_function == 'st_argmax':
|
220 |
+
tokens = st_argmax(logits, dim=-1)
|
221 |
+
else:
|
222 |
+
raise ValueError(
|
223 |
+
f'Invalid `max_type`, expected softmax or gumbel_argmax or st_argmax, but got {self.config.tokenize_function}')
|
224 |
+
return tokens
|
225 |
+
|
226 |
+
def encode(self, pixel_values):
|
227 |
+
output = self.backbone(pixel_values, output_hidden_states=True, return_dict=True)
|
228 |
+
features = output.hidden_states[-1]
|
229 |
+
if self.config.drop_cls_token:
|
230 |
+
features = features[:, 1:, :]
|
231 |
+
|
232 |
+
# merge number of `hidden_stride * hidden_stride` hidden states together to reduce token sequence length
|
233 |
+
# e.g., for hidden_stride=3, this leads to a token length reduction: 729 -> 81 for siglip
|
234 |
+
if self.config.hidden_stride > 1:
|
235 |
+
n, l, d = features.shape # this `d` maybe different from the above `d
|
236 |
+
sqrt_l = int(l ** 0.5)
|
237 |
+
assert sqrt_l ** 2 == l, "The token sequence length should be a perfect square."
|
238 |
+
features = features.reshape(n, sqrt_l, sqrt_l, d)
|
239 |
+
pl = (self.config.hidden_stride - (sqrt_l % self.config.hidden_stride)) % self.config.hidden_stride
|
240 |
+
features = pad(features, (0, 0, 0, pl, 0, pl), "constant", 0)
|
241 |
+
sqrt_l += pl
|
242 |
+
features = features.reshape(n, sqrt_l // self.config.hidden_stride, self.config.hidden_stride,
|
243 |
+
sqrt_l // self.config.hidden_stride, self.config.hidden_stride, d)
|
244 |
+
features = features.permute(0, 1, 3, 2, 4, 5) # [n, sqrt_l/hs, sqrt_l/hs, hs, hs, d]
|
245 |
+
features = features.flatten(3) # [n, sqrt_l/hs, sqrt_l/hs, hs*hs*d]
|
246 |
+
features = features.reshape(
|
247 |
+
n, -1, self.config.hidden_stride * self.config.hidden_stride * d)
|
248 |
+
|
249 |
+
return features
|
250 |
+
|
251 |
+
def forward(self, pixel_values) -> torch.Tensor: # [BatchSize, ImageShape] -> [BatchSize, #Token, VocabSize]
|
252 |
+
features = self.encode(pixel_values)
|
253 |
+
logits = self.head(features)
|
254 |
+
tokens = self.tokenize(logits)
|
255 |
+
# tokens' shape is [BatchSize, #Token, VocabSize-5], so padding with [BatchSize, #Token, 5], after
|
256 |
+
# which, tokens' shape should become [BatchSize, #Token, VocabSize]
|
257 |
+
batch_size, token_len, _ = tokens.shape
|
258 |
+
padding_tensor = torch.zeros(size=(batch_size, token_len, len(IMAGE_INDICATOR_IDS)),
|
259 |
+
dtype=tokens.dtype,
|
260 |
+
device=tokens.device,
|
261 |
+
layout=tokens.layout,
|
262 |
+
requires_grad=False)
|
263 |
+
tokens = torch.cat((tokens, padding_tensor), dim=2)
|
264 |
+
return tokens
|
Ovis/ovis/model/visual_tokenizer/clip_visual_tokenizer.py
ADDED
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoConfig, AutoModel
|
2 |
+
from transformers import CLIPVisionModel, CLIPImageProcessor
|
3 |
+
from .base_visual_tokenizer import BaseVisualTokenizerConfig, BaseVisualTokenizer
|
4 |
+
|
5 |
+
MODEL_TYPE = "clip_visual_tokenizer"
|
6 |
+
|
7 |
+
|
8 |
+
class ClipVisualTokenizerConfig(BaseVisualTokenizerConfig):
|
9 |
+
model_type = MODEL_TYPE
|
10 |
+
|
11 |
+
def __init__(self, **kwargs):
|
12 |
+
super().__init__(**kwargs)
|
13 |
+
if self.depths:
|
14 |
+
assert len(self.depths) == 1
|
15 |
+
self.backbone_kwargs['num_hidden_layers'] = self.depths[0]
|
16 |
+
|
17 |
+
|
18 |
+
class ClipVisualTokenizer(BaseVisualTokenizer):
|
19 |
+
config_class = ClipVisualTokenizerConfig
|
20 |
+
supports_gradient_checkpointing = True
|
21 |
+
_no_split_modules = ["CLIPEncoderLayer"]
|
22 |
+
_image_processor_class = CLIPImageProcessor
|
23 |
+
_image_processor_kwargs = dict(do_center_crop=False)
|
24 |
+
_backbone_class = CLIPVisionModel
|
25 |
+
_backbone_name_or_path = "openai/clip-vit-large-patch14-336"
|
26 |
+
|
27 |
+
def get_monitor_tensors(self):
|
28 |
+
return dict(
|
29 |
+
backbone_bottom=self.backbone.vision_model.encoder.layers[0].self_attn.k_proj.weight,
|
30 |
+
backbone_top=self.backbone.vision_model.encoder.layers[-1].self_attn.out_proj.weight,
|
31 |
+
head=self.head[0].weight
|
32 |
+
)
|
33 |
+
|
34 |
+
def get_image_size(self):
|
35 |
+
height = self.image_processor.crop_size["height"]
|
36 |
+
width = self.image_processor.crop_size["width"]
|
37 |
+
return height, width
|
38 |
+
|
39 |
+
|
40 |
+
AutoConfig.register(MODEL_TYPE, ClipVisualTokenizerConfig)
|
41 |
+
AutoModel.register(ClipVisualTokenizerConfig, ClipVisualTokenizer)
|
Ovis/ovis/model/visual_tokenizer/siglip_visual_tokenizer.py
ADDED
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from transformers import AutoConfig, AutoModel
|
2 |
+
from transformers import SiglipVisionModel, SiglipImageProcessor
|
3 |
+
from .base_visual_tokenizer import BaseVisualTokenizerConfig, BaseVisualTokenizer
|
4 |
+
|
5 |
+
MODEL_TYPE = "siglip_visual_tokenizer"
|
6 |
+
|
7 |
+
|
8 |
+
class SiglipVisualTokenizerConfig(BaseVisualTokenizerConfig):
|
9 |
+
model_type = MODEL_TYPE
|
10 |
+
|
11 |
+
def __init__(self, **kwargs):
|
12 |
+
super().__init__(**kwargs)
|
13 |
+
if self.drop_cls_token:
|
14 |
+
self.drop_cls_token = False
|
15 |
+
if self.depths:
|
16 |
+
assert len(self.depths) == 1
|
17 |
+
self.backbone_kwargs['num_hidden_layers'] = self.depths[0]
|
18 |
+
|
19 |
+
|
20 |
+
class SiglipVisualTokenizer(BaseVisualTokenizer):
|
21 |
+
config_class = SiglipVisualTokenizerConfig
|
22 |
+
supports_gradient_checkpointing = True
|
23 |
+
_no_split_modules = ["SiglipVisionTransformer"]
|
24 |
+
_image_processor_class = SiglipImageProcessor
|
25 |
+
_image_processor_kwargs = {}
|
26 |
+
_backbone_class = SiglipVisionModel
|
27 |
+
_backbone_name_or_path = "google/siglip-so400m-patch14-384"
|
28 |
+
|
29 |
+
def get_monitor_tensors(self):
|
30 |
+
return dict(
|
31 |
+
backbone_bottom=self.backbone.vision_model.encoder.layers[0].self_attn.k_proj.weight,
|
32 |
+
backbone_top=self.backbone.vision_model.encoder.layers[-1].self_attn.out_proj.weight,
|
33 |
+
head=self.head[0].weight
|
34 |
+
)
|
35 |
+
|
36 |
+
def get_image_size(self):
|
37 |
+
height = self.image_processor.size["height"]
|
38 |
+
width = self.image_processor.size["width"]
|
39 |
+
return height, width
|
40 |
+
|
41 |
+
|
42 |
+
AutoConfig.register(MODEL_TYPE, SiglipVisualTokenizerConfig)
|
43 |
+
AutoModel.register(SiglipVisualTokenizerConfig, SiglipVisualTokenizer)
|
Ovis/ovis/serve/__pycache__/runner.cpython-310.pyc
ADDED
Binary file (3.45 kB). View file
|
|
Ovis/ovis/serve/__pycache__/runner.cpython-311.pyc
ADDED
Binary file (6.61 kB). View file
|
|
Ovis/ovis/train/dataset/__init__.py
ADDED
File without changes
|
Ovis/ovis/train/dataset/caption_dataset.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
from datetime import datetime
|
3 |
+
from typing import Dict
|
4 |
+
|
5 |
+
import pandas
|
6 |
+
import torch
|
7 |
+
|
8 |
+
from ovis.train.dataset.multimodal_dataset import MultimodalDataset
|
9 |
+
from ovis.util.constants import IMAGE_TOKEN, IGNORE_ID
|
10 |
+
from ovis.util.utils import rank0_print
|
11 |
+
|
12 |
+
|
13 |
+
class CaptionDataset(MultimodalDataset):
|
14 |
+
|
15 |
+
def load(self):
|
16 |
+
rank0_print(f"[{datetime.now()}] Loading dataset {self.name} from {self.meta_file} begin")
|
17 |
+
samples = pandas.read_parquet(self.meta_file, engine='pyarrow')
|
18 |
+
rank0_print(f"[{datetime.now()}] Loading dataset {self.name} end")
|
19 |
+
return samples
|
20 |
+
|
21 |
+
def __getitem__(self, i: int) -> Dict[str, torch.Tensor]:
|
22 |
+
sample = self.samples.iloc[i]
|
23 |
+
text = sample['caption']
|
24 |
+
image_path = sample['image_path']
|
25 |
+
|
26 |
+
# read and preprocess image
|
27 |
+
pixel_values, image_placeholders = self.visual_tokenizer.mock_input()
|
28 |
+
valid_image = False
|
29 |
+
image, e = self.read_image(image_path)
|
30 |
+
if image is None:
|
31 |
+
logging.warning(
|
32 |
+
f'reading image failed with index: {i}, image path: {image_path}, and exception: {e}')
|
33 |
+
else:
|
34 |
+
try:
|
35 |
+
pixel_values, image_placeholders = self.visual_tokenizer.preprocess_image(
|
36 |
+
image, max_partition=self.max_partitions[0])
|
37 |
+
valid_image = True
|
38 |
+
except Exception as e:
|
39 |
+
logging.warning(
|
40 |
+
f'preprocessing image failed with index: {i}, image path: {image_path}, and exception: {e}')
|
41 |
+
|
42 |
+
# preprocess text
|
43 |
+
if text is None:
|
44 |
+
logging.warning(f'text is `None`, index: {i}')
|
45 |
+
text = ""
|
46 |
+
if not valid_image:
|
47 |
+
logging.warning(f'image is not valid, so set text as empty, index: {i}, image path: {image_path}')
|
48 |
+
text = ""
|
49 |
+
text = text.replace(IMAGE_TOKEN, '').strip()
|
50 |
+
head, tail = self.caption_template.split(IMAGE_TOKEN)
|
51 |
+
head_ids = self.text_tokenizer(head, add_special_tokens=False).input_ids
|
52 |
+
tail_ids = self.text_tokenizer(tail, add_special_tokens=False).input_ids
|
53 |
+
text_ids = self.text_tokenizer(text, add_special_tokens=False).input_ids
|
54 |
+
input_ids = head_ids + image_placeholders + tail_ids + text_ids
|
55 |
+
labels = [IGNORE_ID] * (len(input_ids) - len(text_ids)) + text_ids
|
56 |
+
|
57 |
+
input_ids = input_ids[:self.text_max_length]
|
58 |
+
labels = labels[:self.text_max_length]
|
59 |
+
|
60 |
+
input_ids = torch.tensor(input_ids, dtype=torch.long)
|
61 |
+
labels = torch.tensor(labels, dtype=torch.long)
|
62 |
+
|
63 |
+
return dict(
|
64 |
+
pixel_values=pixel_values,
|
65 |
+
input_ids=input_ids,
|
66 |
+
labels=labels
|
67 |
+
)
|
Ovis/ovis/train/dataset/conversation_dataset.py
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import copy
|
2 |
+
import json
|
3 |
+
import logging
|
4 |
+
from datetime import datetime
|
5 |
+
from typing import Dict
|
6 |
+
|
7 |
+
import torch
|
8 |
+
|
9 |
+
from ovis.train.dataset.multimodal_dataset import MultimodalDataset
|
10 |
+
from ovis.util.utils import rank0_print
|
11 |
+
|
12 |
+
|
13 |
+
class ConversationDataset(MultimodalDataset):
|
14 |
+
def load(self):
|
15 |
+
rank0_print(f"[{datetime.now()}] Loading dataset {self.name} from {self.meta_file} begin")
|
16 |
+
with open(self.meta_file, 'r', encoding='utf-8') as f:
|
17 |
+
samples = json.load(f)
|
18 |
+
rank0_print(f'#samples: {len(samples)}')
|
19 |
+
rank0_print(f'sample: {samples[0]}')
|
20 |
+
rank0_print(f"[{datetime.now()}] Loading dataset {self.name} end")
|
21 |
+
return samples
|
22 |
+
|
23 |
+
def __getitem__(self, i: int) -> Dict[str, torch.Tensor]:
|
24 |
+
sample = self.samples[i]
|
25 |
+
conversations = copy.deepcopy(sample["conversations"])
|
26 |
+
|
27 |
+
images = None
|
28 |
+
max_partition = None
|
29 |
+
if 'image' in sample:
|
30 |
+
image_paths = sample['image']
|
31 |
+
if isinstance(image_paths, str):
|
32 |
+
image_paths = [image_paths]
|
33 |
+
images = []
|
34 |
+
for image_path in image_paths:
|
35 |
+
image, e = self.read_image(image_path)
|
36 |
+
if image is None:
|
37 |
+
logging.warning(
|
38 |
+
f'reading image failed with index: {i}, image path: {image_path}, and exception: {e}')
|
39 |
+
images = None
|
40 |
+
break
|
41 |
+
images.append(image)
|
42 |
+
elif 'video' in sample:
|
43 |
+
raise RuntimeError('video is to be supported')
|
44 |
+
|
45 |
+
if images:
|
46 |
+
max_partition = self.max_partitions[0] if len(images) == 1 else self.max_partitions[1]
|
47 |
+
|
48 |
+
prompt, input_ids, pixel_values, labels = self.model.preprocess_inputs(
|
49 |
+
conversations,
|
50 |
+
images,
|
51 |
+
max_partition=max_partition,
|
52 |
+
generation_preface=None,
|
53 |
+
return_labels=True,
|
54 |
+
propagate_exception=False
|
55 |
+
)
|
56 |
+
|
57 |
+
if pixel_values is None:
|
58 |
+
pixel_values, _ = self.visual_tokenizer.mock_input()
|
59 |
+
|
60 |
+
input_ids = input_ids[:self.text_max_length]
|
61 |
+
labels = labels[:self.text_max_length]
|
62 |
+
|
63 |
+
return dict(
|
64 |
+
pixel_values=pixel_values,
|
65 |
+
input_ids=input_ids,
|
66 |
+
labels=labels
|
67 |
+
)
|
Ovis/ovis/train/dataset/multimodal_dataset.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
import os
|
3 |
+
from typing import Dict, Sequence, Union, List
|
4 |
+
|
5 |
+
import torch
|
6 |
+
from PIL import Image
|
7 |
+
from torch.utils.data import Dataset
|
8 |
+
from transformers import PreTrainedTokenizer
|
9 |
+
|
10 |
+
from ovis.model.modeling_ovis import Ovis
|
11 |
+
from ovis.train.arguments import TrainingArguments
|
12 |
+
from ovis.util.constants import IGNORE_ID
|
13 |
+
|
14 |
+
|
15 |
+
class MultimodalDataset(Dataset):
|
16 |
+
def __init__(self, name: str, info: Dict, model: Ovis, training_args: TrainingArguments):
|
17 |
+
self.name = name
|
18 |
+
self.meta_file = info['meta_file']
|
19 |
+
self.image_dir = info['image_dir']
|
20 |
+
self.caption_template = info.get('caption_template', None)
|
21 |
+
self.text_tokenizer = model.get_text_tokenizer()
|
22 |
+
self.visual_tokenizer = model.get_visual_tokenizer()
|
23 |
+
self.image_height, self.image_width = self.visual_tokenizer.get_image_size()
|
24 |
+
self.model = model
|
25 |
+
self.text_max_length = training_args.text_max_length
|
26 |
+
self.max_partitions = [int(m.strip()) for m in training_args.max_partitions.split('|')]
|
27 |
+
self.samples = self.load()
|
28 |
+
|
29 |
+
def load(self):
|
30 |
+
raise NotImplementedError
|
31 |
+
|
32 |
+
def __getitem__(self, i: int) -> Dict[str, torch.Tensor]:
|
33 |
+
raise NotImplementedError
|
34 |
+
|
35 |
+
def __len__(self):
|
36 |
+
return len(self.samples)
|
37 |
+
|
38 |
+
def read_image(self, path):
|
39 |
+
try:
|
40 |
+
full_path = os.path.join(self.image_dir, path)
|
41 |
+
image = Image.open(full_path).convert('RGB')
|
42 |
+
return image, None
|
43 |
+
except Exception as e:
|
44 |
+
return None, e
|
45 |
+
|
46 |
+
|
47 |
+
class DataCollatorForMultimodalDataset:
|
48 |
+
def __init__(self, text_tokenizer: PreTrainedTokenizer):
|
49 |
+
self.text_tokenizer = text_tokenizer
|
50 |
+
|
51 |
+
def __call__(self, instances: Sequence[Dict]) -> Dict[str, Union[torch.Tensor, List[torch.Tensor]]]:
|
52 |
+
pixel_values, input_ids, labels = tuple([instance[key] for instance in instances]
|
53 |
+
for key in ("pixel_values", "input_ids", "labels"))
|
54 |
+
input_ids = torch.nn.utils.rnn.pad_sequence(
|
55 |
+
input_ids,
|
56 |
+
batch_first=True,
|
57 |
+
padding_value=self.text_tokenizer.pad_token_id)
|
58 |
+
attention_mask = torch.ne(input_ids, self.text_tokenizer.pad_token_id)
|
59 |
+
labels = torch.nn.utils.rnn.pad_sequence(
|
60 |
+
labels,
|
61 |
+
batch_first=True,
|
62 |
+
padding_value=IGNORE_ID)
|
63 |
+
num_valid_label = torch.not_equal(labels, IGNORE_ID).sum().item()
|
64 |
+
if num_valid_label == 0:
|
65 |
+
logging.warning(
|
66 |
+
f'[DataCollatorForMultimodalDataset] All labels in a batch are ignored, which may lead to training instability\n{input_ids=}\n{attention_mask=}\n{labels=}')
|
67 |
+
return dict(
|
68 |
+
input_ids=input_ids,
|
69 |
+
attention_mask=attention_mask,
|
70 |
+
labels=labels,
|
71 |
+
pixel_values=pixel_values
|
72 |
+
)
|
Ovis/ovis/util/__init__.py
ADDED
File without changes
|
Ovis/ovis/util/__pycache__/__init__.cpython-310.pyc
ADDED
Binary file (145 Bytes). View file
|
|
Ovis/ovis/util/__pycache__/__init__.cpython-311.pyc
ADDED
Binary file (161 Bytes). View file
|
|
Ovis/ovis/util/__pycache__/constants.cpython-310.pyc
ADDED
Binary file (460 Bytes). View file
|
|
Ovis/ovis/util/__pycache__/constants.cpython-311.pyc
ADDED
Binary file (503 Bytes). View file
|
|
Ovis/ovis/util/__pycache__/utils.cpython-311.pyc
ADDED
Binary file (1.28 kB). View file
|
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/.ipynb_checkpoints/Qwen2-VL-2B-Instruct_DSPAR_MINI_6345-checkpoint.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đứng trên đường phố. Anh ấy đang nhìn xuống phía dưới, có vẻ như đang tập trung vào một vật gì đó. Anh ấy đang mặc một chiếc áo khoác màu xanh, quần jeans và giày sneaker trắng. Anh ấy có vẻ như đang ở trong một khu vực có nhiều cây xanh."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6275.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đi bộ trên đường. Anh ấy đang đeo một chiếc khẩu trang y tế màu xanh lá cây và đang cầm một chiếc túi xách. Anh ấy có một chiếc đồng hồ đeo tay và một chiếc áo khoác ngắn. Anh ấy đang đi trên một con đường xanh."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6278.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đi bộ trên đường. Anh ấy có một chiếc túi xách trên vai và đang đeo một chiếc kính. Anh ấy có một chiếc quần jeans và đôi giày. Anh ấy có một vẻ mặt buồn bã và đang cố gắng giữ cho mình không rơi vào tầm tay của người khác."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6385.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đi bộ trên đường. Người này có một chiếc mũ đen và một chiếc khẩu trang màu đỏ. Người này đang đi bộ trên một con đường xanh."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6481.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức ảnh này mô tả một người đàn ông đang đi bộ trên đường. Anh ấy có mái tóc ngắn, mặc áo hoodie màu xanh nhạt và quần áo thể thao. Anh ấy đang đeo một chiếc túi xách trên vai."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6500.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đi bộ trên đường. Anh ấy có một chiếc áo khoác màu xanh, quần đen và giày trắng. Anh ấy có vẻ như đang đi bộ trên một con đường màu xám."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6513.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đi bộ trên đường phố. Người này đang mặc một bộ đồ màu xanh, có một chiếc túi xách trên vai."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6515.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người phụ nữ đang đứng trên đường phố. Cô ấy đang mặc một chiếc áo thun màu hồng và quần short. Cô ấy có hai chân và hai tay. Cô ấy đang nhìn về phía trước và có vẻ như đang ngắm nhìn hoặc nhìn vào một thứ gì đó. Cô ấy có hai đôi giày màu đen. Bức tranh được chụp ở một góc đường phố."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6517.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đứng trên một bức tường màu xanh. Anh ta đang nhìn về phía trước và có vẻ như đang tập trung vào một điều gì đó. Bức tranh có một màu sắc đơn giản và không có nhiều chi tiết."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6521.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người phụ nữ đang đi bộ trên đường. Cô ấy đang đeo một chiếc túi xách trên vai và mặc một bộ quần áo màu đen. Cô ấy đang đi trên một con đường có một con đường đi bộ."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6533.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đấm vào người khác trên đường. Người đàn ông đang đấm vào người khác bằng một cây cối."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6539.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đi bộ trên đường. Anh ta đang mặc một bộ đồ bảo hộ lao động màu xanh dương, có một chiếc áo khoác và quần áo dài. Anh ta đang đứng trên một con phố có gạch cống và đường đi xước."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6580.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người phụ nữ đang đi bộ trên đường. Cô ấy đang đeo một chiếc khẩu trang y tế màu xanh lá cây. Cô ấy đang mặc một chiếc áo khoác màu trắng, quần đen và giày sneaker màu trắng. Cô ấy cũng đang đeo một chiếc túi đeo chéo trên vai."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6589.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang cố gắng giữ lại tóc của mình. Anh ta đang cố gắng giữ lại tóc của mình bằng tay. Anh ta đang mặc một chiếc áo màu trắng và quần short màu xám. Anh ta cũng đang đeo một chiếc đồng hồ trên tay."
|
VLMEvalKit_old/outputs/Qwen2-VL-2B-Instruct/DSPAR_MINI/2024-12-24_08-17-11/Qwen2-VL-2B-Instruct/T2024-12-24_08-17-20_Gd57daa89/json/Qwen2-VL-2B-Instruct_DSPAR_MINI_6608.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
"Bức tranh này mô tả một người đàn ông đang đi bộ trên đường. Anh ấy đang mặc một chiếc áo thun màu xám và một chiếc áo vest màu đỏ. Anh ấy đang cầm một chiếc túi xách màu trắng. Anh ấy có một chiếc kính kính màu đen và một chiếc mũ nón màu đen. Anh ấy có một chiếc điện thoại di động trong túi xách. Anh ấy có một chiếc áo dài màu đỏ và một chiếc áo dài màu xanh. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo d��i màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc áo dài màu xanh và một chiếc áo dài màu đỏ. Anh ấy có một chiếc"
|