Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- llm-awq/scripts/DeepSeek_R1_Distill_example.sh +25 -0
- llm-awq/scripts/llava_example.sh +12 -0
- llm-awq/scripts/nvila_example.sh +12 -0
- llm-awq/scripts/vila15_example.sh +14 -0
- llm-awq/scripts/vila_example.sh +12 -0
- llm-awq/tinychat/README.md +526 -0
- llm-awq/tinychat/internvl_demo.py +270 -0
- llm-awq/tinychat/models/__init__.py +10 -0
- llm-awq/tinychat/models/falcon.py +304 -0
- llm-awq/tinychat/models/internvl/configuration_internvl.py +204 -0
- llm-awq/tinychat/models/internvl/conversation.py +391 -0
- llm-awq/tinychat/models/internvl/internvit.py +425 -0
- llm-awq/tinychat/models/internvl/media.py +113 -0
- llm-awq/tinychat/models/internvl3.py +383 -0
- llm-awq/tinychat/models/llama.py +413 -0
- llm-awq/tinychat/models/llava_base/llava_arch.py +412 -0
- llm-awq/tinychat/models/llava_base/multimodal_encoder/builder.py +21 -0
- llm-awq/tinychat/models/llava_base/multimodal_encoder/clip_encoder.py +97 -0
- llm-awq/tinychat/models/llava_base/multimodal_projector/builder.py +72 -0
- llm-awq/tinychat/models/llava_llama.py +282 -0
- llm-awq/tinychat/models/mpt.py +304 -0
- llm-awq/tinychat/models/nvila/builder.py +291 -0
- llm-awq/tinychat/models/nvila/configuration_llava.py +89 -0
- llm-awq/tinychat/models/nvila/llava_arch.py +909 -0
- llm-awq/tinychat/models/nvila_qwen2.py +157 -0
- llm-awq/tinychat/models/qwen2.py +511 -0
- llm-awq/tinychat/models/vila_llama.py +109 -0
- llm-awq/tinychat/modules/__init__.py +9 -0
- llm-awq/tinychat/modules/fused_attn.py +634 -0
- llm-awq/tinychat/modules/fused_internencoder.py +237 -0
- llm-awq/tinychat/modules/fused_mlp.py +101 -0
- llm-awq/tinychat/modules/fused_norm.py +46 -0
- llm-awq/tinychat/modules/fused_siglipdecoder.py +282 -0
- llm-awq/tinychat/modules/fused_vision_attn.py +272 -0
- llm-awq/tinychat/nvila_benchmark.py +163 -0
- llm-awq/tinychat/nvila_demo.py +272 -0
- llm-awq/tinychat/offline-weight-repacker.py +157 -0
- llm-awq/tinychat/scripts/internvl_demo.sh +18 -0
- llm-awq/tinychat/scripts/llama2_demo.sh +31 -0
- llm-awq/tinychat/scripts/nvila_demo.sh +22 -0
- llm-awq/tinychat/serve/README.md +27 -0
- llm-awq/tinychat/serve/controller.py +325 -0
- llm-awq/tinychat/serve/examples/CPR.jpg +0 -0
- llm-awq/tinychat/serve/examples/icl-logo/adobe.jpg +0 -0
- llm-awq/tinychat/serve/examples/icl-logo/apple.jpg +0 -0
- llm-awq/tinychat/serve/examples/icl-logo/google.webp +0 -0
- llm-awq/tinychat/serve/examples/icl-logo/nvidia.png +0 -0
- llm-awq/tinychat/serve/gradio_web_server.py +1200 -0
- llm-awq/tinychat/serve/llava_conv.py +454 -0
- llm-awq/tinychat/serve/model_worker.py +433 -0
llm-awq/scripts/DeepSeek_R1_Distill_example.sh
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL=DeepSeek-R1-Distill-Llama-8B
|
| 2 |
+
|
| 3 |
+
# run AWQ search (optional; we provided the pre-computed results)
|
| 4 |
+
python -m awq.entry --model_path /dataset/models/$MODEL \
|
| 5 |
+
--w_bit 4 --q_group_size 128 \
|
| 6 |
+
--run_awq --dump_awq awq_cache/$MODEL-w4-g128.pt
|
| 7 |
+
|
| 8 |
+
# evaluate the AWQ quantize model (simulated pseudo quantization)
|
| 9 |
+
python -m awq.entry --model_path /dataset/models/$MODEL \
|
| 10 |
+
--tasks wikitext \
|
| 11 |
+
--w_bit 4 --q_group_size 128 \
|
| 12 |
+
--load_awq awq_cache/$MODEL-w4-g128.pt \
|
| 13 |
+
--q_backend fake
|
| 14 |
+
|
| 15 |
+
# generate real quantized weights (w4)
|
| 16 |
+
python -m awq.entry --model_path /dataset/models/$MODEL \
|
| 17 |
+
--w_bit 4 --q_group_size 128 \
|
| 18 |
+
--load_awq awq_cache/$MODEL-w4-g128.pt \
|
| 19 |
+
--q_backend real --dump_quant quant_cache/$MODEL-w4-g128-awq.pt
|
| 20 |
+
|
| 21 |
+
# load and evaluate the real quantized model (smaller gpu memory usage)
|
| 22 |
+
python -m awq.entry --model_path /dataset/models/$MODEL \
|
| 23 |
+
--tasks wikitext \
|
| 24 |
+
--w_bit 4 --q_group_size 128 \
|
| 25 |
+
--load_quant quant_cache/$MODEL-w4-g128-awq.pt
|
llm-awq/scripts/llava_example.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL=llava-13b-v0
|
| 2 |
+
|
| 3 |
+
# run AWQ search (optional; we provided the pre-computed results)
|
| 4 |
+
python -m awq.entry --model_path /dataset/llava-hf/$MODEL \
|
| 5 |
+
--w_bit 4 --q_group_size 128 \
|
| 6 |
+
--run_awq --dump_awq awq_cache/$MODEL-w4-g128.pt
|
| 7 |
+
|
| 8 |
+
# generate real quantized weights (w4)
|
| 9 |
+
python -m awq.entry --model_path /dataset/llava-hf/$MODEL \
|
| 10 |
+
--w_bit 4 --q_group_size 128 \
|
| 11 |
+
--load_awq awq_cache/$MODEL-w4-g128.pt \
|
| 12 |
+
--q_backend real --dump_quant quant_cache/$MODEL-w4-g128-awq.pt
|
llm-awq/scripts/nvila_example.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# run AWQ search
|
| 2 |
+
python -m awq.entry --model_path PATH/TO/NVILA \
|
| 3 |
+
--smooth_scale --media_path https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-VL/space_woaudio.mp4 \
|
| 4 |
+
--act_scale_path awq_cache/NVILA-VT-smooth-scale.pt --vila-20 \
|
| 5 |
+
--w_bit 4 --q_group_size 128 \
|
| 6 |
+
--run_awq --dump_awq awq_cache/NVILA.pt
|
| 7 |
+
|
| 8 |
+
# generate real quantized weights (w4)
|
| 9 |
+
python -m awq.entry --model_path PATH/TO/NVILA/llm \
|
| 10 |
+
--w_bit 4 --q_group_size 128 \
|
| 11 |
+
--load_awq awq_cache/NVILA.pt \
|
| 12 |
+
--q_backend real --dump_quant quant_cache/NVILA-w4-g128-awq.pt --vila-20
|
llm-awq/scripts/vila15_example.sh
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL=VILA1.5-7b
|
| 2 |
+
|
| 3 |
+
# run AWQ search (optional; we provided the pre-computed results)
|
| 4 |
+
# Note: vila checkpoints are now stored in 3 parts.
|
| 5 |
+
# only llm folder will be quantized
|
| 6 |
+
python -m awq.entry --model_path /dataset/vila-hf/$MODEL/llm \
|
| 7 |
+
--w_bit 4 --q_group_size 128 --vila-15 \
|
| 8 |
+
--run_awq --dump_awq awq_cache/$MODEL-w4-g128.pt
|
| 9 |
+
|
| 10 |
+
# generate real quantized weights (w4)
|
| 11 |
+
python -m awq.entry --model_path /dataset/vila-hf/$MODEL/llm \
|
| 12 |
+
--w_bit 4 --q_group_size 128 --vila-15 \
|
| 13 |
+
--load_awq awq_cache/$MODEL-w4-g128.pt \
|
| 14 |
+
--q_backend real --dump_quant /dataset/vila-hf/$MODEL-awq/llm/$MODEL-w4-g128-awq.pt
|
llm-awq/scripts/vila_example.sh
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL=vila-7b
|
| 2 |
+
|
| 3 |
+
# run AWQ search (optional; we provided the pre-computed results)
|
| 4 |
+
python -m awq.entry --model_path /dataset/vila-hf/$MODEL \
|
| 5 |
+
--w_bit 4 --q_group_size 128 \
|
| 6 |
+
--run_awq --dump_awq awq_cache/$MODEL-w4-g128.pt
|
| 7 |
+
|
| 8 |
+
# generate real quantized weights (w4)
|
| 9 |
+
python -m awq.entry --model_path /dataset/vila-hf/$MODEL \
|
| 10 |
+
--w_bit 4 --q_group_size 128 \
|
| 11 |
+
--load_awq awq_cache/$MODEL-w4-g128.pt \
|
| 12 |
+
--q_backend real --dump_quant quant_cache/$MODEL-w4-g128-awq.pt
|
llm-awq/tinychat/README.md
ADDED
|
@@ -0,0 +1,526 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# TinyChat 2.0: Efficient and Lightweight Chatbot with AWQ
|
| 2 |
+
|
| 3 |
+
We introduce TinyChat, a cutting-edge chatbot interface designed for lightweight resource consumption and fast inference speed on GPU platforms. It allows for seamless deployment on consumer-level GPUs such as 3090/4090 and low-power edge devices like the NVIDIA Jetson Orin, empowering users with a responsive conversational experience like never before.
|
| 4 |
+
|
| 5 |
+
The current release supports:
|
| 6 |
+
|
| 7 |
+
- DeepSeek-R1-Distill-Qwen-1.5B/7B
|
| 8 |
+
|
| 9 |
+
- DeepSeek-R1-Distill-Llama-8B
|
| 10 |
+
|
| 11 |
+
- Llama-3-8B/70B-instruct;
|
| 12 |
+
|
| 13 |
+
- NVILA-3B/8B;
|
| 14 |
+
|
| 15 |
+
- VILA-1.5-3B/8B/13B/40B;
|
| 16 |
+
|
| 17 |
+
- VILA-7B/13B;
|
| 18 |
+
|
| 19 |
+
- LLaVA-7B/13B;
|
| 20 |
+
|
| 21 |
+
- Llama-2-7B/13B-chat;
|
| 22 |
+
|
| 23 |
+
- Vicuna;
|
| 24 |
+
|
| 25 |
+
## Contents
|
| 26 |
+
|
| 27 |
+
- [Examples](#examples)
|
| 28 |
+
|
| 29 |
+
- [Benchmarks](#benchmarks)
|
| 30 |
+
|
| 31 |
+
- [Usage](#usage)
|
| 32 |
+
|
| 33 |
+
- [Reference](#reference)
|
| 34 |
+
|
| 35 |
+
## Examples
|
| 36 |
+
|
| 37 |
+
**Thanks to AWQ, TinyChat can now deliver more prompt responses through 4-bit inference. The following examples showcase that TinyChat's W4A16 generation is up to 2.7x faster on RTX 4090 and 2.9x faster on Jetson Orin, compared to the FP16 baselines. (Tested with LLaMA-3-8b model.)**
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
* TinyChat with LLaMA-3-8b on RTX 4090 (2.7x faster than FP16):
|
| 41 |
+
|
| 42 |
+

|
| 43 |
+
|
| 44 |
+
* TinyChat with LLaMA-3-8b on Jetson Orin (2.9x faster than FP16):
|
| 45 |
+
|
| 46 |
+

|
| 47 |
+
|
| 48 |
+
**TinyChat also supports inference with visual language models (e.g., VILA, LLaVA, NVILA). In the following examples, W4A16 quantized models from VILA family are launched with TinyChat.**
|
| 49 |
+
|
| 50 |
+
* TinyChat with NVILA-8B on RTX 4090 (single-image inputs):
|
| 51 |
+
|
| 52 |
+

|
| 53 |
+
|
| 54 |
+
* TinyChat with NVILA-8B on RTX 4090 (multi-image inputs):
|
| 55 |
+
|
| 56 |
+

|
| 57 |
+
|
| 58 |
+
* TinyChat with video reasoning:
|
| 59 |
+
|
| 60 |
+
https://github.com/user-attachments/assets/b68a7a0d-5175-4030-985b-5ae0ae94f874
|
| 61 |
+
|
| 62 |
+
**Prompt:** What might be the next step according to the video?
|
| 63 |
+
|
| 64 |
+
**Answer:** The next step in the video could be to place the shaped dough onto a baking sheet and let it rise before baking.
|
| 65 |
+
|
| 66 |
+
**Online demo:** https://vila.hanlab.ai
|
| 67 |
+
|
| 68 |
+
## Speed Benchmarks
|
| 69 |
+
|
| 70 |
+
We benchmark TinyChat on NVIDIA RTX 4090 (desktop GPU), Orin (edge GPU), and A100 (server-class GPU).
|
| 71 |
+
|
| 72 |
+
We use the default implementation from Huggingface for the FP16 baseline. The INT4 implementation applies AWQ and utilizes our fast W4A16 GPU kernel. We also apply additional optimization techniques in the latest release. For example, we fuse all the operations in MHA/GQA/MQA into a single kernel, and fuse positional embedding kernels into the attention kernel. We also pre-allocate key-value caches to avoid the online memory allocation overhead from Huggingface. For W4A16 GEMM, we introduce FP16 accumulation when applicable for higher throughputs.
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
### Decoding Speed
|
| 76 |
+
|
| 77 |
+
We benchmarked the per-token generation latency for the decoding stage in the following tables.
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
#### RTX 4090 Results
|
| 81 |
+
|
| 82 |
+
| Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 83 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 84 |
+
| LLaMA-3-8B | 17.07 | 6.39 | 2.69x |
|
| 85 |
+
| LLaMA-2-7B | 15.50 | 5.28 | 2.94x |
|
| 86 |
+
| LLaMA-2-13B | OOM | 9.19 | -- |
|
| 87 |
+
| Vicuna-7B | 15.81 | 5.33 | 2.97x |
|
| 88 |
+
| VILA-7B | 17.09 | 5.95 | 2.87x |
|
| 89 |
+
| VILA-13B | OOM | 10.01 | -- |
|
| 90 |
+
| NVILA-2B | 5.26 | 4.27 | 1.23x |
|
| 91 |
+
| NVILA-8B | 16.12 | 5.97 | 2.70x |
|
| 92 |
+
|
| 93 |
+
*: For the decoding speed of language models, we follow the benchmarking setting from exLLaMA (i.e. only 4 context tokens) for the sake of simplicity and fairness. For multi-modal LMs (VILA and [NVILA](https://arxiv.org/abs/2412.04468)), we benchmark the decoding speed with single image inputs. Specifically, for NVILA, we activate the lite mode during the benchmarking, where each image is correspond to 128 input tokens.
|
| 94 |
+
|
| 95 |
+
<!-- | Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 96 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 97 |
+
| LLaMA-3-8B | 17.07 | 6.66 | 2.56x |
|
| 98 |
+
| LLaMA-2-7B | 16.17 | 6.02* | 2.68x |
|
| 99 |
+
| LLaMA-2-13B | OOM | 10.35 | -- |
|
| 100 |
+
| Vicuna-7B | 15.81 | 5.33 | 2.97x |
|
| 101 |
+
| Vicuna-13B | OOM | 9.17 | -- |
|
| 102 |
+
| MPT-7B | 17.09 | 6.18 | 2.77x |
|
| 103 |
+
| MPT-30B | OOM | 20.60 | -- |
|
| 104 |
+
| Falcon-7B | 29.91 | 8.02 | 3.73x |
|
| 105 |
+
| VILA-7B | 17.09 | 5.95 | 2.87x |
|
| 106 |
+
| VILA-13B | OOM | 10.01 | -- | -->
|
| 107 |
+
|
| 108 |
+
<!-- *: The reason why LLaMA-2-7B is slower than Vicuna-7B is because we need a longer prompt (with > 500 tokens) to prevent the model from talking with itself. If we use the benchmarking strategy from exLLaMA (i.e. only 4 context tokens), our speed is around 195 tokens / second. -->
|
| 109 |
+
|
| 110 |
+
<!-- ### A6000 Results
|
| 111 |
+
| Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 112 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 113 |
+
| LLaMA-3-8B | 24.95 | 10.68 | 2.34x |
|
| 114 |
+
| LLaMA-2-7B | 22.75 | 8.71 | 2.61x |
|
| 115 |
+
| LLaMA-2-13B | 41.72 | 14.64 | 2.85x |
|
| 116 |
+
| Vicuna-7B | 22.03 | 8.39 | 2.63x |
|
| 117 |
+
| Vicuna-13B | 38.97 | 13.46 | 2.90x |
|
| 118 |
+
| MPT-7B | 22.79 | 7.99 | 2.85x |
|
| 119 |
+
| MPT-30B | OOM | 28.15 | -- |
|
| 120 |
+
| Falcon-7B | 39.44 | 11.71 | 3.37x |
|
| 121 |
+
| VILA-7B | 23.60 | 8.14 | 2.90x |
|
| 122 |
+
| VILA-13B | 46.58 | 13.74 | 3.39x | -->
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
#### Jetson Orin Results
|
| 126 |
+
|
| 127 |
+
| Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 128 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 129 |
+
| LLaMA-3-8B | 96.00 | 32.53 | 2.95x |
|
| 130 |
+
| LLaMA-2-7B | 83.95 | 25.94 | 3.24x |
|
| 131 |
+
| LLaMA-2-13B | 162.33 | 47.67 | 3.41x |
|
| 132 |
+
| Vicuna-7B | 84.77 | 26.34 | 3.22x |
|
| 133 |
+
| VILA-7B | 86.95 | 28.09 | 3.10x |
|
| 134 |
+
| VILA-13B | OOM | 57.14 | -- |
|
| 135 |
+
| NVILA-2B | 24.22 | 22.25 | 1.09x |
|
| 136 |
+
| NVILA-8B | 86.24 | 30.48 | 2.83x |
|
| 137 |
+
|
| 138 |
+
<!-- | Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 139 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 140 |
+
| LLaMA-3-8B | 96.24 | 32.55 | 2.96x |
|
| 141 |
+
| LLaMA-2-7B | 86.80 | 32.14* | 2.70x |
|
| 142 |
+
| LLaMA-2-13B | OOM | 58.20 | -- |
|
| 143 |
+
| Vicuna-7B | 84.77 | 30.73 | 2.76x |
|
| 144 |
+
| Vicuna-13B | OOM | 54.98 | -- |
|
| 145 |
+
| MPT-7B | 89.85 | 31.22 | 2.88x |
|
| 146 |
+
| Falcon-7B | 147.84 | 45.10 | 3.28x |
|
| 147 |
+
| VILA-7B | 86.95 | 28.09 | 3.10x |
|
| 148 |
+
| VILA-13B | OOM | 57.14 | -- |
|
| 149 |
+
|
| 150 |
+
*: We can similarly achieve 33 tokens / second on Orin if we use the benchmarking strategy from exLLaMA. -->
|
| 151 |
+
|
| 152 |
+
#### A100 Results
|
| 153 |
+
|
| 154 |
+
| Model | FP16 latency (ms) | INT4 latency (ms) | Speedup |
|
| 155 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|
|
| 156 |
+
| LLaMA-3-8B | 12.37 | 6.29 | 1.96x |
|
| 157 |
+
| LLaMA-2-7B | 10.77 | 5.71 | 1.89x |
|
| 158 |
+
| LLaMA-2-13B | 19.08 | 7.90 | 2.41x |
|
| 159 |
+
| Vicuna-7B | 10.54 | 5.87 | 1.80x |
|
| 160 |
+
| VILA-7B | 13.35 | 5.92 | 2.26x |
|
| 161 |
+
| VILA-13B | 19.64 | 8.63 | 2.28x |
|
| 162 |
+
| NVILA-2B | 7.03 | 5.38 | 1.31x |
|
| 163 |
+
| NVILA-8B | 11.90 | 5.50 | 2.16x |
|
| 164 |
+
|
| 165 |
+
|
| 166 |
+
### Prefilling Speed
|
| 167 |
+
|
| 168 |
+
In TinyChat 2.0, we also introduce significant prefilling speed optimizations for Large Language Models (LLMs) and Visual Language Models (VLMs). Specifically, with the integration of latest flash attention and FP16 accumulation in GEMM kernels, TinyChat now achieves state-of-the-art prefilling speed on edge devices.
|
| 169 |
+
|
| 170 |
+
#### RTX 4090 Results
|
| 171 |
+
|
| 172 |
+
Time-To-First-Token (TTFT) of Llama-3-8B (Unit: Seconds):
|
| 173 |
+
|
| 174 |
+
| Seq Len | 256 | 512 | 1024 | 2048 | 3072 | 4096 |
|
| 175 |
+
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
|
| 176 |
+
| FP16 | 0.031 | 0.055 | 0.109 | 0.211 | 0.336 | 0.446 |
|
| 177 |
+
| TinyChat | 0.021 | 0.033 | 0.064 | 0.131 | 0.200 | 0.275 |
|
| 178 |
+
| Speedup | 1.52x | 1.68x | 1.69x | 1.61x | 1.68x | 1.62x |
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
Time-To-First-Token (TTFT) of Llama-2-7B (Unit: Seconds):
|
| 182 |
+
|
| 183 |
+
| Seq Len | 256 | 512 | 1024 | 2048 | 3072 | 4096 |
|
| 184 |
+
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
|
| 185 |
+
| FP16 | 0.029 | 0.058 | 0.100 | 0.211 | 0.329 | 0.441 |
|
| 186 |
+
| TinyChat | 0.018 | 0.031 | 0.060 | 0.124 | 0.193 | 0.265 |
|
| 187 |
+
| Speedup | 1.57x | 1.83x | 1.66x | 1.70x | 1.70x | 1.66x |
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
#### Jetson Orin Results
|
| 191 |
+
|
| 192 |
+
Time-To-First-Token (TTFT) of Llama-3-8B (Unit: Seconds):
|
| 193 |
+
|
| 194 |
+
| Seq Len | 256 | 512 | 1024 | 2048 | 3072 | 4096 |
|
| 195 |
+
| ----------- |:-------:|:-------:|:-------:|:-------:|:-------:|:-------:|
|
| 196 |
+
| FP16 | 0.206 | 0.399 | 0.566 | 1.519 | 2.308 | 3.114 |
|
| 197 |
+
| TinyChat | 0.166 | 0.315 | 0.623 | 1.248 | 1.907 | 2.573 |
|
| 198 |
+
| Speedup | 1.24x | 1.26x | 0.91x | 1.22x | 1.21x | 1.21x |
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
#### Comparison with Other Systems
|
| 202 |
+
|
| 203 |
+
Time-To-First-Token (TTFT) of 4-bit weight-only quantized Llama3-8B on RTX 4090 across various systems (Unit: Seconds):
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
| Seq Len | 256 | 512 | 1024 | 2048 | 4096 |
|
| 207 |
+
|:-------------------:|:-----:|:-----:|:-----:|:-----:|:------:|
|
| 208 |
+
| TensorRT-LLM | 0.027 | 0.051 | 0.100 | 0.204 | 0.421 |
|
| 209 |
+
| MLC | 0.028 | 0.042 | 0.081 | 0.166 | 0.350 |
|
| 210 |
+
| llama.cpp | 0.026 | 0.045 | 0.086 | 0.175 | 0.375 |
|
| 211 |
+
| ExLlama v2 | 0.040 | 0.051 | 0.077 | 0.139 | 0.294 |
|
| 212 |
+
| TinyChat (Legacy) | 0.031 | 0.051 | 0.101 | 0.219 | 0.461 |
|
| 213 |
+
| TinyChat 2.0 | 0.021 | 0.033 | 0.065 | 0.132 | 0.278 |
|
| 214 |
+
|
| 215 |
+
Our approach outperforms all existing projects, achieving state-of-the-art speed.
|
| 216 |
+
|
| 217 |
+
### Context Streaming: Efficient Multi-round Dialogues
|
| 218 |
+
|
| 219 |
+
In TinyChat 2.0, we introduce chunk-prefilling optimization for multi-round dialogues. For multi-turn inputs, TinyChat will reuse the KV Cache from previous conversations without recomputing them. This optimization eliminates redundant computations and significantly reduce the Time To First Token (TTFT) for subsequent interaction rounds.
|
| 220 |
+
|
| 221 |
+
#### RTX 4090 Results
|
| 222 |
+
|
| 223 |
+
To evaluate Context Streaming, we measure the TTFT in multi-round conversations with a fixed question length of 32 tokens, and varying history lengths from 16 to 1024 tokens. Specifically, in TinyChat 2.0, all history tokens are already prefilled to the existing KV Cache when processing the current query, while baseline systems recompute the history tokens for each query.
|
| 224 |
+
|
| 225 |
+
<!-- To demonstrate the effectiveness of Context Streaming, we measure the TTFT in multi-round conversation with a fixed question length of 32 and varying history lengths ranging from 16 to 1024 tokens. This setup means that a number of history tokens (based on the specified history length) are already input into the model. In this round, the question tokens (32 tokens) are also input, and the model takes TTFT to process these question tokens, prefill the KV cache, and generate the first token. All the tables below follows this setting. The speedup ratio in all the tables below refer to the acceleration achieved by the new method compared to FP16 inference. -->
|
| 226 |
+
|
| 227 |
+
Time-To-First-Token (TTFT) of Llama-3-8B (Unit: ms):
|
| 228 |
+
|
| 229 |
+
| History length | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
|
| 230 |
+
|---------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:------:|
|
| 231 |
+
| FP16 | 21.49 | 21.38 | 23.51 | 40.82 | 47.15 | 75.41 | 162.27 |
|
| 232 |
+
| TinyChat (Legacy) | 15.20 | 14.89 | 17.61 | 29.66 | 44.11 | 72.50 | 163.90 |
|
| 233 |
+
| TinyChat 2.0 | 14.30 | 14.05 | 14.05 | 14.43 | 14.38 | 14.35 | 14.49 |
|
| 234 |
+
| Speedup | 1.54x | 1.54x | 1.69x | 2.84x | 3.33x | 5.27x | 11.45x |
|
| 235 |
+
|
| 236 |
+
<!-- Time-To-First-Token (TTFT) of VILA-1.5-8B (Unit: ms):
|
| 237 |
+
|
| 238 |
+
| History length | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
|
| 239 |
+
|---------------------------|:-----:|:-----:|:-----:|:-----:|:-----:|:------:|:------:|
|
| 240 |
+
| FP16 TTFT (ms) | 22.20 | 22.00 | 24.17 | 41.85 | 62.97 | 101.84 | 217.57 |
|
| 241 |
+
| Legacy TinyChat TTFT (ms) | 16.14 | 15.98 | 18.28 | 30.72 | 59.67 | 98.52 | 219.19 |
|
| 242 |
+
| New TinyChat TTFT (ms) | 14.86 | 14.69 | 14.64 | 14.90 | 14.91 | 14.95 | 14.90 |
|
| 243 |
+
| New TinyChat Speedup | 1.49x | 1.50x | 1.65x | 2.81x | 4.22x | 6.81x | 14.60x |
|
| 244 |
+
|
| 245 |
+
NOTE: [TODO] @Yuming. The current setting is too complicated. Let's consider the case: each round, there is an image input. Baseline need to re-encode every image, while tinychat only need to encode 1 image.
|
| 246 |
+
|
| 247 |
+
*: For Visual Language Models, the speedup of Context Streaming is more significant, since the model only decodes images during the first round. In the experiment, We assume that approximately 75% of the history tokens represent images, leading to the number of images in the table being 0, 0, 0, 0, 1, 2, 4. This assumption is reasonable to some extent, considering that a single image is decoded into 196 tokens. -->
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
<!-- We have optimized the speed of the context stage and updated our code with several enhancements, including the adoption of FlashAttention and the elimination of redundant computations. The key optimizations include:
|
| 252 |
+
1. Adopting the FlashAttention kernel. (Currently we only support single-batch operations to achieve better results)
|
| 253 |
+
2. Computing only the last tokens in the final logits layer. (This method is used by default.)
|
| 254 |
+
3. Utilizing history KV caches in the context stage to speed up. (chunk prefilling) -->
|
| 255 |
+
|
| 256 |
+
<!-- These optimizations are orthogonal, enabling their combined application to achieve significant speedups. Under specific conditions, these enhancements can lead to up to an 14x speedup on 4090 GPUs and an 8x speedup on Orin GPUs in Time To First Token (TTFT) compared to the previous version of TinyChat and FP16. We conducted experiments using both Orin and 4090 GPUs, and detailed results are presented below. -->
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
<!-- ### Orin Results
|
| 260 |
+
We follow the setup above and the results are as below.
|
| 261 |
+
#### Llama-3-8B
|
| 262 |
+
| History length | 16 | 32 | 64 | 128 | 256 | 512 | 1024 |
|
| 263 |
+
|---------------------------|:------:|:------:|:------:|:------:|:------:|:------:|:-------:|
|
| 264 |
+
| FP16 TTFT (ms) | 107.10 | 108.81 | 114.07 | 224.78 | 343.95 | 582.54 | 1048.11 |
|
| 265 |
+
| Legacy TinyChat TTFT (ms) | 92.04 | 111.31 | 106.60 | 160.78 | 278.47 | 528.70 | 1145.35 |
|
| 266 |
+
| New TinyChat TTFT (ms) | 65.57 | 65.40 | 66.49 | 67.15 | 73.29 | 84.67 | 118.53 |
|
| 267 |
+
| New TinyChat Speedup | 1.52x | 1.65x | 1.70x | 3.30x | 4.51x | 6.75x | 8.65x | -->
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
## Accuracy Evaluation
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
AWQ also achieves decent performance on the Visual Language Models. We evaluate AWQ on VILA and the lastest NVILA models.
|
| 274 |
+
|
| 275 |
+
| NVILA-8B | AI2D | ChartQA | DocVQA | MMMU_val | SEED | TextVQA | VideoMME |
|
| 276 |
+
| ---------- |:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|
|
| 277 |
+
| FP16 | 91.0 | 84.8 | 91.7 | 50.7 | 76.3 | 78.1 | 63.9 |
|
| 278 |
+
| AWQ-INT4 | 90.9 | 83.3 | 89.2 | 49.3 | 76.2 | 78.2 | 62.1 |
|
| 279 |
+
|
| 280 |
+
<!--
|
| 281 |
+
| NVILA-8B | AI2D | ChartQA | DocVQA | MMMU_val | SEED | TextVQA | VideoMME-Short | VideoMME-Medium | VideoMME-Long | VideoMME-Overall |
|
| 282 |
+
| ---------- |:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|:----------:|
|
| 283 |
+
| FP16 | 91.0 | 84.8 | 91.7 | 50.7 | 76.3 | 78.1 | 74.9 | 62.1 | 54.7 | 63.9 |
|
| 284 |
+
| AWQ-INT4 | 90.9 | 83.3 | 89.2 | 49.3 | 76.2 | 78.2 | 73.2 | 61.3 | 51.6 | 62.1 | -->
|
| 285 |
+
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
| VILA-1.5-3B | VQA-v2 | GQA | VizWiz | ScienceQA | TextVQA | POPE | MME | MMBench | MMBench-CN | SEED |
|
| 289 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|:-----------------:|:-----------------:|:-------:|:-------:|:-----------------:|:-------------:|:-------:|
|
| 290 |
+
| FP16 | 80.4 | 61.5 | 53.5 | 69.0 | 60.4 | 85.9 | 1442.4 | 63.4 | 52.7 | 60.9 |
|
| 291 |
+
| AWQ-INT4 | 80.0 | 61.1 | 53.8 | 67.8 | 60.4 | 85.9 | 1437.3 | 63.3 | 51.4 | 59.8 |
|
| 292 |
+
|
| 293 |
+
| VILA-1.5-8B | VQA-v2 | GQA | VizWiz | ScienceQA | TextVQA | POPE | MME | MMBench | MMBench-CN | SEED |
|
| 294 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|:-----------------:|:-----------------:|:-------:|:-------:|:-----------------:|:-------------:|:-------:|
|
| 295 |
+
| FP16 | 80.9 | 61.9 | 58.7 | 79.9 | 66.3 | 84.4 | 1577.01 | 72.3 | 66.2 | 64.2 |
|
| 296 |
+
| AWQ-INT4 | 80.3 | 61.7 | 59.3 | 79.0 | 65.4 | 82.9 | 1593.65 | 71.0 | 64.9 | 64.0 |
|
| 297 |
+
|
| 298 |
+
| VILA-1.5-13B | VQA-v2 | GQA | VizWiz | ScienceQA | TextVQA | POPE | MME | MMBench | MMBench-CN | SEED |
|
| 299 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|:-----------------:|:-----------------:|:-------:|:-------:|:-----------------:|:-------------:|:-------:|
|
| 300 |
+
| FP16 | 82.8 | 64.3 | 62.6 | 80.1 | 65.0 | 86.3 | 1569.55 | 74.9 | 66.3 | 65.1 |
|
| 301 |
+
| AWQ-INT4 | 82.7 | 64.5 | 63.3 | 79.7 | 64.7 | 86.7 | 1531.35 | 74.7 | 66.7 | 65.1 |
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
| VILA-1.5-40B | VQA-v2 | GQA | VizWiz | ScienceQA | TextVQA | POPE | MME | MMBench | MMBench-CN | SEED |
|
| 305 |
+
| ----------- |:-----------------:|:-----------------:|:-------:|:-----------------:|:-----------------:|:-------:|:-------:|:-----------------:|:-------------:|:-------:|
|
| 306 |
+
| FP16 | 84.3 | 64.6 | 62.2 | 87.2 | 73.6 | 87.3 | 1726.82 | 82.4 | 80.2 | 69.1 |
|
| 307 |
+
| AWQ-INT4 | 84.1 | 64.4 | 61.3 | 86.7 | 73.2 | 88.2 | 1714.79 | 83.2 | 79.6 | 68.9 |
|
| 308 |
+
|
| 309 |
+
AWQ has also demonstrated impressive performance on inference benchmarks, maintaining strong accuracy across a range of reasoning tasks.
|
| 310 |
+
|
| 311 |
+
| DeepSeek-R1-Distill-Llama-8B | WikiText perplexity | AIME 2024 | Math-500 |
|
| 312 |
+
| ---------------------------- |:-------------------:|:-------------------:|:-------------------:|
|
| 313 |
+
| FP16 | 13.13 | 43.33% | 83.00% |
|
| 314 |
+
| AWQ-INT4 | 13.84 | 43.33% | 84.40% |
|
| 315 |
+
|
| 316 |
+
| DeepSeek-R1-Distill-Qwen-7B | WikiText perplexity | AIME 2024 | Math-500 |
|
| 317 |
+
| --------------------------- |:-------------------:|:-------------------:|:-------------------:|
|
| 318 |
+
| FP16 | 25.06 | 53.33% | 91.40% |
|
| 319 |
+
| AWQ-INT4 | 27.45 | 53.33% | 89.60% |
|
| 320 |
+
|
| 321 |
+
## Usage
|
| 322 |
+
|
| 323 |
+
1. Please follow the [AWQ installation guidance](https://github.com/mit-han-lab/llm-awq#readme) to install AWQ and its dependencies. If you want to use FlashAttention, start by installing it with: ```pip install flash-attn --no-build-isolation```. However, for some GPUs such as Jetson Orin, there is no pre-built version available. You will need to build it from source. Follow these commands:
|
| 324 |
+
```bash
|
| 325 |
+
git clone https://github.com/Dao-AILab/flash-attention.git
|
| 326 |
+
cd flash-attention
|
| 327 |
+
sed -i '168 a\ cc_flag.append("-gencode")\n\ cc_flag.append("arch=compute_87,code=sm_87")' setup.py
|
| 328 |
+
python setup.py install
|
| 329 |
+
```
|
| 330 |
+
This process may take some time as it involves compiling the code. Additionally, please note that these commands are just for Jetson Orin GPUs, whose CUDA compute capability is 87. For other GPUs, you may use ```nvidia-smi --query-gpu=compute_cap --format=csv``` to get the compute capability and merely change '87' to that.
|
| 331 |
+
|
| 332 |
+
2. Download the pretrained instruction-tuned LLMs:
|
| 333 |
+
|
| 334 |
+
- For LLaMA-2-chat, please refer to [this link](https://huggingface.co/meta-llama/Llama-2-7b-chat-hf);
|
| 335 |
+
|
| 336 |
+
- For Vicuna, please refer to [this link](https://huggingface.co/lmsys/);
|
| 337 |
+
|
| 338 |
+
- For MPT-chat, please refer to [this link](https://huggingface.co/mosaicml/mpt-7b-chat);
|
| 339 |
+
|
| 340 |
+
- For Falcon-instruct, please refer to [this link](https://huggingface.co/tiiuae/falcon-7b-instruct).
|
| 341 |
+
|
| 342 |
+
3. Quantize instruction-tuned LLMs with AWQ:
|
| 343 |
+
- We provide pre-computed AWQ search results for multiple model families, including LLaMA, OPT, Vicuna, VILA, and LLaVA. To get the pre-computed AWQ search results, run:
|
| 344 |
+
|
| 345 |
+
```bash
|
| 346 |
+
# git lfs install # install git lfs if not already
|
| 347 |
+
git clone https://huggingface.co/datasets/mit-han-lab/awq-model-zoo awq_cache
|
| 348 |
+
```
|
| 349 |
+
|
| 350 |
+
- You may run a one-line starter below:
|
| 351 |
+
|
| 352 |
+
```bash
|
| 353 |
+
./scripts/llama2_demo.sh
|
| 354 |
+
```
|
| 355 |
+
|
| 356 |
+
Alternatively, you may go through the process step by step. We will demonstrate the quantization process with LLaMA-2. For all other models except Falcon, one only needs to change the `model_path` and saving locations. For Falcon-7B, we also need to change `q_group_size` from 128 to 64.
|
| 357 |
+
|
| 358 |
+
- Perform AWQ search and save search results (we already did it for you):
|
| 359 |
+
|
| 360 |
+
```bash
|
| 361 |
+
mkdir awq_cache
|
| 362 |
+
python -m awq.entry --model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 363 |
+
--w_bit 4 --q_group_size 128 \
|
| 364 |
+
--run_awq --dump_awq awq_cache/llama-2-7b-chat-w4-g128.pt
|
| 365 |
+
```
|
| 366 |
+
|
| 367 |
+
- Generate real quantized weights (INT4):
|
| 368 |
+
|
| 369 |
+
```bash
|
| 370 |
+
mkdir quant_cache
|
| 371 |
+
python -m awq.entry --model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 372 |
+
--w_bit 4 --q_group_size 128 \
|
| 373 |
+
--load_awq awq_cache/llama-2-7b-chat-w4-g128.pt \
|
| 374 |
+
--q_backend real --dump_quant quant_cache/llama-2-7b-chat-w4-g128-awq.pt
|
| 375 |
+
```
|
| 376 |
+
|
| 377 |
+
4. Run the TinyChat demo:
|
| 378 |
+
|
| 379 |
+
```bash
|
| 380 |
+
cd tinychat
|
| 381 |
+
python demo.py --model_type llama \
|
| 382 |
+
--model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 383 |
+
--q_group_size 128 --load_quant quant_cache/llama-2-7b-chat-w4-g128-awq.pt \
|
| 384 |
+
--precision W4A16
|
| 385 |
+
```
|
| 386 |
+
|
| 387 |
+
Note: if you use Falcon-7B-instruct, please remember to also change `q_group_size` to 64. You may also run the following command to execute the chatbot in FP16 to compare the speed and quality of language generation:
|
| 388 |
+
|
| 389 |
+
```bash
|
| 390 |
+
python demo.py --model_type llama \
|
| 391 |
+
--model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 392 |
+
--precision W16A16
|
| 393 |
+
```
|
| 394 |
+
You can now try using FlashAttention along with chunk prefilling. Use the following two arguments when running demo: ```
|
| 395 |
+
--flash --chunk_prefilling```.
|
| 396 |
+
|
| 397 |
+
The above command works well for most cloud and desktop GPUs, since their CPU and GPU memory space are separated. However, for edge GPUs with shared host and device memory, in order to run larger models (e.g. LLaMA-2-70B on 64GB Orin), it is necessary to break down the pretrained checkpoints into small pieces:
|
| 398 |
+
|
| 399 |
+
```bash
|
| 400 |
+
python split_ckpt.py --input_path quant_cache/llama-2-7b-chat-w4-g128-awq.pt \
|
| 401 |
+
--output_path quant_cache/llama-2-7b-chat-w4-g128-awq
|
| 402 |
+
```
|
| 403 |
+
|
| 404 |
+
Then, to run the demo, one can use the following command. The only changes compared with the demo command above are:
|
| 405 |
+
|
| 406 |
+
- We modify the `load_quant` argument;
|
| 407 |
+
|
| 408 |
+
- We introduce another flag `mem_efficient_load`.
|
| 409 |
+
|
| 410 |
+
```bash
|
| 411 |
+
cd tinychat
|
| 412 |
+
python demo.py --model_type llama \
|
| 413 |
+
--model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 414 |
+
--q_group_size 128 --load_quant quant_cache/llama-2-7b-chat-w4-g128-awq \
|
| 415 |
+
--precision W4A16 --mem_efficient_load
|
| 416 |
+
```
|
| 417 |
+
|
| 418 |
+
5. (Optional) Run the benchmark script to get TTFT and decoding throughput:
|
| 419 |
+
|
| 420 |
+
```bash
|
| 421 |
+
cd tinychat
|
| 422 |
+
python benchmark.py --flash \
|
| 423 |
+
--context_length 16 32 64 128 256 512 1024 2048 \
|
| 424 |
+
--model_path /PATH/TO/LLAMA2/llama-2-7b-chat --precision W4A16
|
| 425 |
+
```
|
| 426 |
+
To benchmark chunk prefilling, use:
|
| 427 |
+
```bash
|
| 428 |
+
python benchmark.py --chunk_prefilling \
|
| 429 |
+
--model_path /PATH/TO/LLAMA2/llama-2-7b-chat \
|
| 430 |
+
--question_length 32 --context_length 16 32 64 128 256 512 1024 --precision W4A16
|
| 431 |
+
```
|
| 432 |
+
Note: The kv caches in the current implementation are pre-allocated. So if you run out of memory, it might be the case that the kv cache is too large. To solve the problem, you may pass in `--max_seq_len [a smaller number]`.
|
| 433 |
+
### Support Visual Language Models (VILA-1.5, VILA, LLaVA, NVILA)
|
| 434 |
+
|
| 435 |
+
Our TinyChat also supports visual language models. Follow the instructions below to run VLMs on your own devices!
|
| 436 |
+
|
| 437 |
+
Step 1-3 are same as the deployment for Language-only models.
|
| 438 |
+
|
| 439 |
+
1. Follow the [AWQ installation guidance](https://github.com/mit-han-lab/llm-awq#readme) to install AWQ and its dependencies.
|
| 440 |
+
|
| 441 |
+
2. Download the pretrained VLMs (VILA).
|
| 442 |
+
|
| 443 |
+
3. Quantize the VLMs with AWQ and get the quantized checkpoint in `quant_cache`. We also provide a [sample script](../scripts/vila_example.sh) for this step.
|
| 444 |
+
|
| 445 |
+
4. Run the TinyChat demo for VLMs (with vila15_demo.py for VILA-1.5, vila10_demo.py for VILA and LLaVA):
|
| 446 |
+
|
| 447 |
+
```bash
|
| 448 |
+
cd tinychat
|
| 449 |
+
python vila15_demo.py \
|
| 450 |
+
--model-path /PATH/TO/VILA/VILA-1.5-13B \
|
| 451 |
+
--quant-path quant_cache/vila-1.5-13b-w4-g128-awq.pt \
|
| 452 |
+
--precision W4A16 \
|
| 453 |
+
--image-file /PATH/TO/INPUT/IMAGE \
|
| 454 |
+
--vis-image #Optional
|
| 455 |
+
```
|
| 456 |
+
|
| 457 |
+
Alternatively, one may also skip the quantization process and directy download the quantized VILA-1.5 checkpoints from [here](https://huggingface.co/Efficient-Large-Model). Take VILA-1.5-13B as an example, after running:
|
| 458 |
+
|
| 459 |
+
```bash
|
| 460 |
+
cd tinychat
|
| 461 |
+
git clone https://huggingface.co/Efficient-Large-Model/VILA1.5-13b-AWQ
|
| 462 |
+
```
|
| 463 |
+
|
| 464 |
+
One may run:
|
| 465 |
+
```bash
|
| 466 |
+
python vila15_demo.py \
|
| 467 |
+
--model-path VILA1.5-13b-AWQ \
|
| 468 |
+
--quant-path VILA1.5-13b-AWQ/llm \
|
| 469 |
+
--precision W4A16 \
|
| 470 |
+
--image-file /PATH/TO/INPUT/IMAGE \
|
| 471 |
+
--vis-image #Optional
|
| 472 |
+
```
|
| 473 |
+
|
| 474 |
+
to run the terminal demo directly. You can also use``` --flash --chunk_prefilling``` to accelerate. We also support context stage benckmarking for VILA.
|
| 475 |
+
```bash
|
| 476 |
+
python benchmark_context.py --flash --chunk_prefilling \
|
| 477 |
+
--model_path PATH/TO/Llama-3-VILA1.5-8B \
|
| 478 |
+
--question_length 32 --context_length 16 32 64 128 256 512 1024 \
|
| 479 |
+
--model_type vila --quant
|
| 480 |
+
```
|
| 481 |
+
Note: if you enable `--vis-image` mode, TinyChat will print input images directly in your terminal. You may need to install [termvisage](https://github.com/AnonymouX47/termvisage) to enable this mode. A [terminal emulator](https://github.com/AnonymouX47/termvisage?tab=readme-ov-file#requirements) is also required.
|
| 482 |
+
|
| 483 |
+
Note: VILA model family supports multi-image inputs. You can input multiple images in `/PATH/TO/INPUT/IMAGE` above, each image should be seperated by `,`.
|
| 484 |
+
|
| 485 |
+
5. TinyChat support NVILA now! We adopt W8A8 SmoothQuant for VisionTower and W4A16 quantization for LLM, achieving 1.3x -3.3x speedup for prefiling satge and nearly 1.5x higher throughput. You can use the commands below to prepare the your model and try four basic tasks of NVILA-video model.
|
| 486 |
+
To prepared the needed act scale for awq and smoothquant, please run:
|
| 487 |
+
```bash
|
| 488 |
+
python -m awq.entry --model_path PATH/TO/NVILA \
|
| 489 |
+
--smooth_scale --media_path https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-VL/space_woaudio.mp4 \
|
| 490 |
+
--act_scale_path awq_cache/NVILA-VT-smooth-scale.pt --vila-20 \
|
| 491 |
+
--w_bit 4 --q_group_size 128 \
|
| 492 |
+
--run_awq --dump_awq awq_cache/NVILA.pt
|
| 493 |
+
```
|
| 494 |
+
Then, please generate real quantized LLM with:
|
| 495 |
+
```bash
|
| 496 |
+
python -m awq.entry --model_path PATH/TO/NVILA/llm \
|
| 497 |
+
--w_bit 4 --q_group_size 128 \
|
| 498 |
+
--load_awq awq_cache/NVILA.pt \
|
| 499 |
+
--q_backend real --dump_quant quant_cache/NVILA-w4-g128-awq.pt --vila-20
|
| 500 |
+
```
|
| 501 |
+
Next, try chatting with it using the command below to experience shorter Time To First Token (TTFT) and higher decoding throughput.
|
| 502 |
+
```bash
|
| 503 |
+
python nvila_demo.py --model-path EPATH/TO/NVILA \
|
| 504 |
+
--quant_path PATH/TO/NVILA-w4-g128-v2.pt \
|
| 505 |
+
--media PATH/TO/MEDIA \
|
| 506 |
+
--act_scale_path PATH/TO/NVILA-smooth-scale.pt \
|
| 507 |
+
--quant_llm --chunk --model_type nvila
|
| 508 |
+
```
|
| 509 |
+
|
| 510 |
+
|
| 511 |
+
## Team
|
| 512 |
+
|
| 513 |
+
TinyChat is developed by the following wonderful team:
|
| 514 |
+
|
| 515 |
+
- [Shang Yang](https://ys-2020.github.io/): Project Lead, TinyChat v1 and v2 Lead;
|
| 516 |
+
- [Haotian Tang](http://kentang.net): Project Lead, TinyChat v1 Lead, v2 Mentor;
|
| 517 |
+
- [Yuming Lou](<>): TinyChat v2 Lead;
|
| 518 |
+
- [Junxian Guo](<>): TinyChat v2 Contributor;
|
| 519 |
+
- [Song Han](https://hanlab.mit.edu/songhan): Project Advisor.
|
| 520 |
+
|
| 521 |
+
Credits also go to AWQ algorithm leads: [Ji Lin](https://www.linji.me/) and [Jiaming Tang](https://jiamingtang.me/).
|
| 522 |
+
|
| 523 |
+
## Reference
|
| 524 |
+
|
| 525 |
+
TinyChat is inspired by the following open-source projects: [FasterTransformer](https://github.com/NVIDIA/FasterTransformer), [FlashAttention](https://github.com/Dao-AILab/flash-attention), [vLLM](https://github.com/vllm-project/vllm), [FastChat](https://github.com/lm-sys/FastChat), [llama_cu_awq](https://github.com/ankan-ban/llama_cu_awq), [LLaVA](https://github.com/haotian-liu/LLaVA), [termvisage](https://github.com/AnonymouX47/termvisage).
|
| 526 |
+
|
llm-awq/tinychat/internvl_demo.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
|
| 3 |
+
from termcolor import colored
|
| 4 |
+
|
| 5 |
+
import llava
|
| 6 |
+
from llava.media import Image, Video
|
| 7 |
+
import torch
|
| 8 |
+
from awq.quantize import fake_quant
|
| 9 |
+
from transformers import AutoConfig, AutoTokenizer
|
| 10 |
+
from tinychat.utils.load_quant import load_awq_model
|
| 11 |
+
from tinychat.utils.llava_image_processing import (
|
| 12 |
+
load_images,
|
| 13 |
+
vis_images,
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def skip(*args, **kwargs):
|
| 18 |
+
pass
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
from tinychat.utils.tune import (
|
| 22 |
+
device_warmup,
|
| 23 |
+
tune_all_wqlinears,
|
| 24 |
+
tune_llava_patch_embedding,
|
| 25 |
+
)
|
| 26 |
+
from tinychat.utils.prompt_templates import (
|
| 27 |
+
get_prompter,
|
| 28 |
+
get_stop_token_ids,
|
| 29 |
+
get_image_token,
|
| 30 |
+
)
|
| 31 |
+
from llava.utils.media import extract_media
|
| 32 |
+
import tinychat.utils.constants
|
| 33 |
+
from tinychat.stream_generators.internvl_stream_gen import InternVLStreamGenerator
|
| 34 |
+
from tinychat.utils.conversation_utils import gen_params, stream_output, TimeStats
|
| 35 |
+
|
| 36 |
+
import os
|
| 37 |
+
|
| 38 |
+
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "expandable_segments:True"
|
| 39 |
+
|
| 40 |
+
def tune_intern_patch_embedding(vision_model, device):
|
| 41 |
+
patch_embedding = vision_model.embeddings.patch_embedding
|
| 42 |
+
patch_embedding = patch_embedding.to(device)
|
| 43 |
+
|
| 44 |
+
image = (
|
| 45 |
+
torch.randn((1, patch_embedding.in_channels, 336, 336))
|
| 46 |
+
.to(device)
|
| 47 |
+
.to(patch_embedding.weight.dtype)
|
| 48 |
+
)
|
| 49 |
+
for i in range(100):
|
| 50 |
+
patch_embedding(image)
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def main(args):
|
| 54 |
+
# Accelerate model initialization
|
| 55 |
+
setattr(torch.nn.Linear, "reset_parameters", lambda self: None)
|
| 56 |
+
setattr(torch.nn.LayerNorm, "reset_parameters", lambda self: None)
|
| 57 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 58 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 59 |
+
torch.nn.init.uniform_ = skip
|
| 60 |
+
torch.nn.init.normal_ = skip
|
| 61 |
+
tinychat.utils.constants.max_seq_len = args.max_seq_len
|
| 62 |
+
|
| 63 |
+
# Prepare model
|
| 64 |
+
from tinychat.models import InternVL3
|
| 65 |
+
from tinychat.models.internvl.internvit import InternVisionModel
|
| 66 |
+
from transformers import AutoConfig
|
| 67 |
+
from tinychat.models.qwen2 import Qwen2ForCausalLM
|
| 68 |
+
|
| 69 |
+
config = AutoConfig.from_pretrained(args.model_path, trust_remote_code=True)
|
| 70 |
+
config.resume_path = args.model_path
|
| 71 |
+
if args.quant_llm or args.all:
|
| 72 |
+
model = InternVL3.from_pretrained(args.model_path, config=config).half()
|
| 73 |
+
else:
|
| 74 |
+
llm = Qwen2ForCausalLM.from_pretrained(args.model_path)
|
| 75 |
+
llm = llm.cpu()
|
| 76 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 77 |
+
args.model_path, use_fast=False, trust_remote_code=True
|
| 78 |
+
)
|
| 79 |
+
llm.resize_token_embeddings(len(tokenizer))
|
| 80 |
+
model = InternVL3(config, language_model=llm).half()
|
| 81 |
+
|
| 82 |
+
if args.smooth_VT or args.all:
|
| 83 |
+
from awq.quantize import smooth_lm
|
| 84 |
+
|
| 85 |
+
act_scales = torch.load(args.act_scale_path)
|
| 86 |
+
smooth_lm(model.vision_tower, act_scales, 0.3)
|
| 87 |
+
if args.quant_llm or args.all:
|
| 88 |
+
from tinychat.modules import (
|
| 89 |
+
make_quant_norm,
|
| 90 |
+
make_quant_attn,
|
| 91 |
+
make_fused_mlp,
|
| 92 |
+
make_fused_vision_attn,
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
model = load_awq_model(model, args.quant_path, 4, 128, args.device)
|
| 96 |
+
make_quant_attn(model, args.device, True)
|
| 97 |
+
make_quant_norm(model)
|
| 98 |
+
model.cpu()
|
| 99 |
+
model.resize_token_embeddings(len(model.tokenizer))
|
| 100 |
+
pass
|
| 101 |
+
|
| 102 |
+
if args.quant_VT or args.all:
|
| 103 |
+
from tinychat.modules import QuantInternVisionEncoder
|
| 104 |
+
model.vision_model.encoder = QuantInternVisionEncoder(model.vision_model.encoder)
|
| 105 |
+
# model.vision_model.encoder = torch.compile(model.vision_model.encoder)
|
| 106 |
+
|
| 107 |
+
model = model.cuda().eval()
|
| 108 |
+
device_warmup(args.device)
|
| 109 |
+
# tune_intern_patch_embedding(model.vision_model, device=args.device)
|
| 110 |
+
|
| 111 |
+
# Pre-prepare media
|
| 112 |
+
prompt = []
|
| 113 |
+
media_files = []
|
| 114 |
+
if args.media is not None:
|
| 115 |
+
for media in args.media or []:
|
| 116 |
+
if any(media.endswith(ext) for ext in [".jpg", ".jpeg", ".png"]):
|
| 117 |
+
media = Image(media)
|
| 118 |
+
media_files.append(media)
|
| 119 |
+
media_prompt = "<image>"
|
| 120 |
+
elif any(media.endswith(ext) for ext in [".mp4", ".mkv", ".webm"]):
|
| 121 |
+
media = Video(media)
|
| 122 |
+
media_files.append(media)
|
| 123 |
+
media_prompt = "<vila/video>"
|
| 124 |
+
else:
|
| 125 |
+
raise ValueError(f"Unsupported media type: {media}")
|
| 126 |
+
prompt.append(media)
|
| 127 |
+
media_num = len(media_files)
|
| 128 |
+
if args.vis_image:
|
| 129 |
+
print("=" * 50)
|
| 130 |
+
print("Input Image:")
|
| 131 |
+
vis_images(args.media)
|
| 132 |
+
|
| 133 |
+
conversation = [{"from": "human", "value": prompt}]
|
| 134 |
+
media, media_cfg = model.prepare_media(conversation)
|
| 135 |
+
# Prepare streaming
|
| 136 |
+
stream_generator = InternVLStreamGenerator
|
| 137 |
+
# Prepare prompt
|
| 138 |
+
if args.max_seq_len <= 1024:
|
| 139 |
+
short_prompt = True
|
| 140 |
+
else:
|
| 141 |
+
short_prompt = False
|
| 142 |
+
model_prompter = get_prompter(
|
| 143 |
+
args.model_type, args.model_path, short_prompt, args.empty_prompt
|
| 144 |
+
)
|
| 145 |
+
stop_token_ids = get_stop_token_ids(args.model_type, args.model_path)
|
| 146 |
+
count = 0
|
| 147 |
+
|
| 148 |
+
if args.empty_prompt:
|
| 149 |
+
input_indicator = "Input: "
|
| 150 |
+
output_indicator = "Generated: "
|
| 151 |
+
else:
|
| 152 |
+
input_indicator = "USER: "
|
| 153 |
+
output_indicator = "ASSISTANT: "
|
| 154 |
+
|
| 155 |
+
count = 0
|
| 156 |
+
model.eval()
|
| 157 |
+
time_stats = TimeStats()
|
| 158 |
+
start_pos = 0
|
| 159 |
+
while True:
|
| 160 |
+
# Get input from the user
|
| 161 |
+
print("=" * 50)
|
| 162 |
+
input_prompt = input(input_indicator)
|
| 163 |
+
print("-" * 50)
|
| 164 |
+
if input_prompt == "":
|
| 165 |
+
print("EXIT...")
|
| 166 |
+
time_stats.show()
|
| 167 |
+
break
|
| 168 |
+
if count == 0: # Insert media here
|
| 169 |
+
if args.media is not None:
|
| 170 |
+
if media_prompt in input_prompt:
|
| 171 |
+
input_prompt = input_prompt
|
| 172 |
+
else:
|
| 173 |
+
if media_prompt == "<image>":
|
| 174 |
+
input_prompt = media_prompt * media_num + input_prompt
|
| 175 |
+
elif media_prompt == "<vila/video>":
|
| 176 |
+
video_prefix = ''.join([f'Frame{i+1}: <image>\n' for i in range(len(media_cfg))])
|
| 177 |
+
input_prompt = video_prefix + input_prompt
|
| 178 |
+
|
| 179 |
+
model_prompter.insert_prompt(input_prompt)
|
| 180 |
+
else:
|
| 181 |
+
model_prompter.insert_prompt(input_prompt)
|
| 182 |
+
if args.chunk_prefilling:
|
| 183 |
+
media = None
|
| 184 |
+
media_cfg = None
|
| 185 |
+
output_stream = stream_generator(
|
| 186 |
+
model,
|
| 187 |
+
gen_params,
|
| 188 |
+
model_prompter.model_input,
|
| 189 |
+
media,
|
| 190 |
+
media_cfg,
|
| 191 |
+
start_pos,
|
| 192 |
+
device=args.device,
|
| 193 |
+
stop_token_ids=stop_token_ids,
|
| 194 |
+
chunk_prefilling=args.chunk_prefilling,
|
| 195 |
+
quant_llm=args.quant_llm or args.all,
|
| 196 |
+
)
|
| 197 |
+
print(output_indicator, end="", flush=True)
|
| 198 |
+
if count == 0:
|
| 199 |
+
outputs, total_tokens = stream_output(output_stream, time_stats)
|
| 200 |
+
else:
|
| 201 |
+
outputs, total_tokens = stream_output(output_stream)
|
| 202 |
+
if args.chunk_prefilling:
|
| 203 |
+
start_pos += total_tokens
|
| 204 |
+
if (
|
| 205 |
+
args.single_round is not True and args.max_seq_len > 512
|
| 206 |
+
): # Only memorize previous conversations when kv_cache_size > 512
|
| 207 |
+
model_prompter.update_template(outputs, args.chunk_prefilling)
|
| 208 |
+
count += 1
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
if __name__ == "__main__":
|
| 212 |
+
parser = argparse.ArgumentParser()
|
| 213 |
+
parser.add_argument(
|
| 214 |
+
"--model_type", type=str, default="LLaMa", help="type of the model"
|
| 215 |
+
)
|
| 216 |
+
parser.add_argument(
|
| 217 |
+
"--model-path", type=str, default="/data/llm/checkpoints/llava/llava-v1.5-7b"
|
| 218 |
+
)
|
| 219 |
+
parser.add_argument(
|
| 220 |
+
"--quant_path",
|
| 221 |
+
type=str,
|
| 222 |
+
default="/data/llm/checkpoints/llava/llava-v1.5-7b-w4-g128-awq.pt",
|
| 223 |
+
)
|
| 224 |
+
parser.add_argument(
|
| 225 |
+
"--act_scale_path",
|
| 226 |
+
type=str,
|
| 227 |
+
default="/PATH/TO/SCALE",
|
| 228 |
+
)
|
| 229 |
+
parser.add_argument(
|
| 230 |
+
"--media", type=str, nargs="+", help="Multi-modal input (Video or image path)"
|
| 231 |
+
)
|
| 232 |
+
parser.add_argument("--device", type=str, default="cuda")
|
| 233 |
+
parser.add_argument("--max_seq_len", type=int, default=4098)
|
| 234 |
+
parser.add_argument(
|
| 235 |
+
"--single_round",
|
| 236 |
+
action="store_true",
|
| 237 |
+
help="whether to memorize previous conversations",
|
| 238 |
+
)
|
| 239 |
+
parser.add_argument(
|
| 240 |
+
"--vis-image",
|
| 241 |
+
action="store_true",
|
| 242 |
+
help="whether to visualize the image while chatting",
|
| 243 |
+
)
|
| 244 |
+
parser.add_argument(
|
| 245 |
+
"--empty-prompt",
|
| 246 |
+
action="store_true",
|
| 247 |
+
help="whether to use empty prompt template",
|
| 248 |
+
)
|
| 249 |
+
parser.add_argument(
|
| 250 |
+
"--flash_attn",
|
| 251 |
+
action="store_true",
|
| 252 |
+
help="whether to use flash attention",
|
| 253 |
+
)
|
| 254 |
+
parser.add_argument(
|
| 255 |
+
"--chunk_prefilling",
|
| 256 |
+
action="store_true",
|
| 257 |
+
help="If used, in context stage, the history tokens will not be recalculated, greatly speeding up the calculation",
|
| 258 |
+
)
|
| 259 |
+
# smooth and quantization options
|
| 260 |
+
parser.add_argument("--quant_llm", action="store_true")
|
| 261 |
+
parser.add_argument("--quant_VT", action="store_true")
|
| 262 |
+
parser.add_argument("--smooth_VT", action="store_true")
|
| 263 |
+
parser.add_argument("--all", action="store_true")
|
| 264 |
+
parser.add_argument(
|
| 265 |
+
"--fakequant_VT",
|
| 266 |
+
action="store_true",
|
| 267 |
+
help="Use fake quant or real quant for VisionTower",
|
| 268 |
+
)
|
| 269 |
+
args = parser.parse_args()
|
| 270 |
+
main(args)
|
llm-awq/tinychat/models/__init__.py
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .falcon import FalconForCausalLM
|
| 2 |
+
from .llama import LlamaForCausalLM
|
| 3 |
+
from .mpt import MPTForCausalLM
|
| 4 |
+
from .llava_llama import LlavaLlamaForCausalLM
|
| 5 |
+
from .qwen2 import Qwen2ForCausalLM
|
| 6 |
+
try:
|
| 7 |
+
from .internvl3 import InternVL3
|
| 8 |
+
except ImportError as e:
|
| 9 |
+
print("InternVL3 model import failure. To activate, please install VILA at https://github.com/NVlabs/VILA.")
|
| 10 |
+
|
llm-awq/tinychat/models/falcon.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
# This software may be used and distributed according to the terms of the GNU General Public License version 3.
|
| 3 |
+
|
| 4 |
+
from typing import Optional, Tuple
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from torch import nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import awq_inference_engine
|
| 12 |
+
|
| 13 |
+
import tinychat.utils.constants
|
| 14 |
+
|
| 15 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 16 |
+
max_seq_len = tinychat.utils.constants.max_seq_len
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
# rotary pos emb helpers (torch.jit.script does not seem to support staticmethod...)
|
| 20 |
+
def rotate_half(x):
|
| 21 |
+
x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2 :]
|
| 22 |
+
return torch.cat(
|
| 23 |
+
(-x2, x1), dim=x1.ndim - 1
|
| 24 |
+
) # dim=-1 triggers a bug in torch < 1.8.0
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
class RotaryEmbedding(nn.Module):
|
| 28 |
+
"""Implementation of RotaryEmbedding from GPT-NeoX.
|
| 29 |
+
This implementation is design to operate on queries and keys that are compatible with
|
| 30 |
+
[batch_size, n_heads_per_partition, seq_len, head_dim] (e.g. MinGPTAttention format).
|
| 31 |
+
"""
|
| 32 |
+
|
| 33 |
+
def __init__(
|
| 34 |
+
self,
|
| 35 |
+
head_dim: int,
|
| 36 |
+
base=10000,
|
| 37 |
+
):
|
| 38 |
+
super().__init__()
|
| 39 |
+
inv_freq = 1.0 / (base ** (torch.arange(0, head_dim, 2).float() / head_dim))
|
| 40 |
+
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 41 |
+
self.head_dim = head_dim
|
| 42 |
+
self.seq_len_cached = None
|
| 43 |
+
self.batch_size_cached = None
|
| 44 |
+
self.cos_cached: torch.Tensor | None = None
|
| 45 |
+
self.sin_cached: torch.Tensor | None = None
|
| 46 |
+
|
| 47 |
+
def cos_sin(
|
| 48 |
+
self,
|
| 49 |
+
seq_len: int,
|
| 50 |
+
device="cuda",
|
| 51 |
+
dtype=torch.bfloat16,
|
| 52 |
+
) -> torch.Tensor:
|
| 53 |
+
if seq_len != self.seq_len_cached:
|
| 54 |
+
self.seq_len_cached = seq_len
|
| 55 |
+
t = torch.arange(seq_len, device=device).type_as(self.inv_freq)
|
| 56 |
+
freqs = torch.einsum("i,j->ij", t, self.inv_freq)
|
| 57 |
+
emb = torch.cat((freqs, freqs), dim=-1).to(device)
|
| 58 |
+
|
| 59 |
+
if dtype in [torch.float16, torch.bfloat16]:
|
| 60 |
+
emb = emb.float()
|
| 61 |
+
|
| 62 |
+
self.cos_cached = emb.cos()[None, :, :]
|
| 63 |
+
self.sin_cached = emb.sin()[None, :, :]
|
| 64 |
+
|
| 65 |
+
self.cos_cached = self.cos_cached.type(dtype)
|
| 66 |
+
self.sin_cached = self.sin_cached.type(dtype)
|
| 67 |
+
|
| 68 |
+
return self.cos_cached, self.sin_cached
|
| 69 |
+
|
| 70 |
+
def forward(self, _q, _k):
|
| 71 |
+
batch, seq_len, num_heads, head_dim = _q.shape
|
| 72 |
+
q = _q.permute(0, 2, 1, 3).contiguous().reshape(-1, seq_len, head_dim)
|
| 73 |
+
k = _k.permute(0, 2, 1, 3).contiguous().reshape(-1, seq_len, head_dim)
|
| 74 |
+
cos, sin = self.cos_sin(seq_len, q.device, q.dtype)
|
| 75 |
+
return (q * cos) + (rotate_half(q) * sin), (k * cos) + (rotate_half(k) * sin)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
class FalconAttentionFused(nn.Module):
|
| 79 |
+
def __init__(self, args):
|
| 80 |
+
super().__init__()
|
| 81 |
+
self.args = args
|
| 82 |
+
self.n_local_heads = args.n_head
|
| 83 |
+
self.head_dim = args.hidden_size // args.n_head
|
| 84 |
+
|
| 85 |
+
self.query_key_value = nn.Linear(
|
| 86 |
+
args.hidden_size,
|
| 87 |
+
args.n_head * self.head_dim + 2 * self.head_dim,
|
| 88 |
+
bias=False,
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
self.dense = nn.Linear(
|
| 92 |
+
args.n_head * self.head_dim,
|
| 93 |
+
args.hidden_size,
|
| 94 |
+
bias=False,
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
# following fastertransformer definition
|
| 98 |
+
|
| 99 |
+
self.cache_v = (
|
| 100 |
+
torch.zeros(
|
| 101 |
+
(
|
| 102 |
+
max_batch_size,
|
| 103 |
+
1,
|
| 104 |
+
max_seq_len,
|
| 105 |
+
self.head_dim,
|
| 106 |
+
)
|
| 107 |
+
)
|
| 108 |
+
.cuda()
|
| 109 |
+
.half()
|
| 110 |
+
) # added to half
|
| 111 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 112 |
+
self.cache_k = (
|
| 113 |
+
torch.zeros(
|
| 114 |
+
(
|
| 115 |
+
max_batch_size,
|
| 116 |
+
1,
|
| 117 |
+
self.head_dim // 8,
|
| 118 |
+
max_seq_len,
|
| 119 |
+
8,
|
| 120 |
+
)
|
| 121 |
+
)
|
| 122 |
+
.cuda()
|
| 123 |
+
.half()
|
| 124 |
+
) # added to half
|
| 125 |
+
|
| 126 |
+
self.rotary_emb = RotaryEmbedding(self.head_dim)
|
| 127 |
+
self.rope_theta = args.rope_theta
|
| 128 |
+
self.rope_scaling = args.rope_scaling
|
| 129 |
+
if self.rope_scaling is None:
|
| 130 |
+
self.rope_scaling = 1.0
|
| 131 |
+
else:
|
| 132 |
+
self.rope_scaling = 1.0 / self.rope_scaling["factor"]
|
| 133 |
+
|
| 134 |
+
def forward(
|
| 135 |
+
self,
|
| 136 |
+
x: torch.Tensor,
|
| 137 |
+
start_pos: int,
|
| 138 |
+
mask: Optional[torch.Tensor],
|
| 139 |
+
):
|
| 140 |
+
bsz, seqlen, _ = x.shape
|
| 141 |
+
|
| 142 |
+
xqkv = self.query_key_value(x)
|
| 143 |
+
xqkv = xqkv.view(bsz, seqlen, self.n_local_heads + 2, self.head_dim)
|
| 144 |
+
xq = xqkv[:, :, :-2]
|
| 145 |
+
xk = xqkv[:, :, [-2]]
|
| 146 |
+
xv = xqkv[:, :, [-1]]
|
| 147 |
+
|
| 148 |
+
if seqlen > 1:
|
| 149 |
+
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 150 |
+
xk = xk.view(bsz, seqlen, 1, self.head_dim)
|
| 151 |
+
xv = xv.view(bsz, seqlen, 1, self.head_dim)
|
| 152 |
+
|
| 153 |
+
xq, xk = self.rotary_emb(xq, xk)
|
| 154 |
+
xq = (
|
| 155 |
+
xq.reshape(bsz, self.n_local_heads, seqlen, self.head_dim)
|
| 156 |
+
.permute(0, 2, 1, 3)
|
| 157 |
+
.contiguous()
|
| 158 |
+
)
|
| 159 |
+
xk = (
|
| 160 |
+
xk.reshape(bsz, 1, seqlen, self.head_dim)
|
| 161 |
+
.permute(0, 2, 1, 3)
|
| 162 |
+
.contiguous()
|
| 163 |
+
)
|
| 164 |
+
|
| 165 |
+
self.cache_k = self.cache_k.to(xq)
|
| 166 |
+
self.cache_v = self.cache_v.to(xq)
|
| 167 |
+
|
| 168 |
+
values_store = xv.transpose(2, 1)
|
| 169 |
+
keys_store = (
|
| 170 |
+
xk.reshape(bsz, seqlen, 1, self.head_dim // 8, 8)
|
| 171 |
+
.permute(0, 2, 3, 1, 4)
|
| 172 |
+
.contiguous()
|
| 173 |
+
)
|
| 174 |
+
|
| 175 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 176 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 177 |
+
|
| 178 |
+
keys = xk
|
| 179 |
+
values = xv
|
| 180 |
+
|
| 181 |
+
xq = xq.transpose(1, 2)
|
| 182 |
+
keys = keys.transpose(1, 2)
|
| 183 |
+
values = values.transpose(1, 2)
|
| 184 |
+
scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
|
| 185 |
+
if mask is not None:
|
| 186 |
+
scores = scores + mask # (bs, n_local_heads, slen, cache_len + slen)
|
| 187 |
+
scores = F.softmax(scores.float(), dim=-1).type_as(xq)
|
| 188 |
+
output = torch.matmul(scores, values) # (bs, n_local_heads, slen, head_dim)
|
| 189 |
+
output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)
|
| 190 |
+
else:
|
| 191 |
+
# xq = xq[:, 0, :, :]
|
| 192 |
+
# xk = xk[:, 0, :, :]
|
| 193 |
+
# xv = xv[:, 0, :, :]
|
| 194 |
+
xq = xq.view(bsz, self.n_local_heads, self.head_dim)
|
| 195 |
+
xk = xk.view(bsz, 1, self.head_dim)
|
| 196 |
+
xv = xv.view(bsz, 1, self.head_dim)
|
| 197 |
+
|
| 198 |
+
output = awq_inference_engine.single_query_attention(
|
| 199 |
+
xq,
|
| 200 |
+
xk,
|
| 201 |
+
xv,
|
| 202 |
+
self.cache_k,
|
| 203 |
+
self.cache_v,
|
| 204 |
+
None,
|
| 205 |
+
# alibi position encodings
|
| 206 |
+
None,
|
| 207 |
+
start_pos,
|
| 208 |
+
self.head_dim,
|
| 209 |
+
self.rope_theta,
|
| 210 |
+
self.rope_scaling,
|
| 211 |
+
True,
|
| 212 |
+
)
|
| 213 |
+
output = output.reshape(bsz, 1, -1)
|
| 214 |
+
|
| 215 |
+
return self.dense(output)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
class FalconMLP(nn.Module):
|
| 219 |
+
def __init__(
|
| 220 |
+
self,
|
| 221 |
+
dim: int,
|
| 222 |
+
):
|
| 223 |
+
super().__init__()
|
| 224 |
+
self.dense_h_to_4h = nn.Linear(dim, 4 * dim, bias=False)
|
| 225 |
+
self.act = nn.GELU()
|
| 226 |
+
self.dense_4h_to_h = nn.Linear(4 * dim, dim, bias=False)
|
| 227 |
+
|
| 228 |
+
def forward(self, x):
|
| 229 |
+
x = self.act(self.dense_h_to_4h(x))
|
| 230 |
+
x = self.dense_4h_to_h(x)
|
| 231 |
+
return x
|
| 232 |
+
|
| 233 |
+
|
| 234 |
+
class TransformerBlock(nn.Module):
|
| 235 |
+
def __init__(self, layer_id: int, args):
|
| 236 |
+
super().__init__()
|
| 237 |
+
self.n_heads = args.n_head
|
| 238 |
+
self.dim = args.hidden_size
|
| 239 |
+
self.head_dim = args.hidden_size // args.n_head
|
| 240 |
+
self.self_attention = FalconAttentionFused(args)
|
| 241 |
+
self.mlp = FalconMLP(dim=args.hidden_size)
|
| 242 |
+
self.layer_id = layer_id
|
| 243 |
+
self.input_layernorm = nn.LayerNorm(
|
| 244 |
+
args.hidden_size, eps=args.layer_norm_epsilon
|
| 245 |
+
)
|
| 246 |
+
# self.post_attention_layernorm = nn.LayerNorm(args.dim, eps=args.norm_eps)
|
| 247 |
+
|
| 248 |
+
def forward(
|
| 249 |
+
self,
|
| 250 |
+
x: torch.Tensor,
|
| 251 |
+
start_pos: int,
|
| 252 |
+
mask: Optional[torch.Tensor],
|
| 253 |
+
):
|
| 254 |
+
layernorm_output = self.input_layernorm(x)
|
| 255 |
+
h_attn = x + self.self_attention.forward(layernorm_output, start_pos, mask)
|
| 256 |
+
h_mlp = self.mlp(layernorm_output)
|
| 257 |
+
out = h_attn + h_mlp
|
| 258 |
+
return out
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
class Transformer(nn.Module):
|
| 262 |
+
def __init__(self, params):
|
| 263 |
+
super().__init__()
|
| 264 |
+
self.params = params
|
| 265 |
+
self.vocab_size = params.vocab_size
|
| 266 |
+
self.n_layers = params.n_layer
|
| 267 |
+
|
| 268 |
+
self.word_embeddings = nn.Embedding(params.vocab_size, params.hidden_size)
|
| 269 |
+
|
| 270 |
+
self.h = torch.nn.ModuleList()
|
| 271 |
+
for layer_id in range(params.n_layer):
|
| 272 |
+
self.h.append(TransformerBlock(layer_id, params))
|
| 273 |
+
|
| 274 |
+
self.ln_f = nn.LayerNorm(params.hidden_size, eps=params.layer_norm_epsilon)
|
| 275 |
+
|
| 276 |
+
@torch.inference_mode()
|
| 277 |
+
def forward(self, tokens: torch.Tensor, start_pos: int):
|
| 278 |
+
_bsz, seqlen = tokens.shape
|
| 279 |
+
h = self.word_embeddings(tokens)
|
| 280 |
+
|
| 281 |
+
mask = None
|
| 282 |
+
if seqlen > 1:
|
| 283 |
+
mask = torch.full(
|
| 284 |
+
(1, 1, seqlen, seqlen), float("-inf"), device=tokens.device
|
| 285 |
+
)
|
| 286 |
+
mask = torch.triu(mask, diagonal=start_pos + 1).type_as(h)
|
| 287 |
+
for layer in self.h:
|
| 288 |
+
h = layer(h, start_pos, mask)
|
| 289 |
+
h = self.ln_f(h)
|
| 290 |
+
return h
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
class FalconForCausalLM(nn.Module):
|
| 294 |
+
def __init__(self, params):
|
| 295 |
+
super().__init__()
|
| 296 |
+
self.config = params
|
| 297 |
+
self.transformer = Transformer(params)
|
| 298 |
+
self.lm_head = nn.Linear(params.hidden_size, params.vocab_size, bias=False)
|
| 299 |
+
|
| 300 |
+
@torch.inference_mode()
|
| 301 |
+
def forward(self, tokens: torch.Tensor, start_pos: int):
|
| 302 |
+
h = self.transformer(tokens, start_pos)
|
| 303 |
+
output = self.lm_head(h) # only compute last logits
|
| 304 |
+
return output.float()
|
llm-awq/tinychat/models/internvl/configuration_internvl.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# --------------------------------------------------------
|
| 2 |
+
# InternVL
|
| 3 |
+
# Copyright (c) 2024 OpenGVLab
|
| 4 |
+
# Licensed under The MIT License [see LICENSE for details]
|
| 5 |
+
# --------------------------------------------------------
|
| 6 |
+
|
| 7 |
+
import os
|
| 8 |
+
from typing import Union
|
| 9 |
+
import copy
|
| 10 |
+
|
| 11 |
+
from transformers.configuration_utils import PretrainedConfig
|
| 12 |
+
from transformers.utils import logging
|
| 13 |
+
from transformers import AutoConfig, LlamaConfig, Qwen2Config
|
| 14 |
+
|
| 15 |
+
logger = logging.get_logger(__name__)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
class InternVisionConfig(PretrainedConfig):
|
| 19 |
+
r"""
|
| 20 |
+
This is the configuration class to store the configuration of a [`InternVisionModel`]. It is used to
|
| 21 |
+
instantiate a vision encoder according to the specified arguments, defining the model architecture.
|
| 22 |
+
|
| 23 |
+
Configuration objects inherit from [`PretrainedConfig`] and can be used to control the model outputs. Read the
|
| 24 |
+
documentation from [`PretrainedConfig`] for more information.
|
| 25 |
+
|
| 26 |
+
Args:
|
| 27 |
+
num_channels (`int`, *optional*, defaults to 3):
|
| 28 |
+
Number of color channels in the input images (e.g., 3 for RGB).
|
| 29 |
+
patch_size (`int`, *optional*, defaults to 14):
|
| 30 |
+
The size (resolution) of each patch.
|
| 31 |
+
image_size (`int`, *optional*, defaults to 224):
|
| 32 |
+
The size (resolution) of each image.
|
| 33 |
+
qkv_bias (`bool`, *optional*, defaults to `False`):
|
| 34 |
+
Whether to add a bias to the queries and values in the self-attention layers.
|
| 35 |
+
hidden_size (`int`, *optional*, defaults to 3200):
|
| 36 |
+
Dimensionality of the encoder layers and the pooler layer.
|
| 37 |
+
num_attention_heads (`int`, *optional*, defaults to 25):
|
| 38 |
+
Number of attention heads for each attention layer in the Transformer encoder.
|
| 39 |
+
intermediate_size (`int`, *optional*, defaults to 12800):
|
| 40 |
+
Dimensionality of the "intermediate" (i.e., feed-forward) layer in the Transformer encoder.
|
| 41 |
+
qk_normalization (`bool`, *optional*, defaults to `True`):
|
| 42 |
+
Whether to normalize the queries and keys in the self-attention layers.
|
| 43 |
+
num_hidden_layers (`int`, *optional*, defaults to 48):
|
| 44 |
+
Number of hidden layers in the Transformer encoder.
|
| 45 |
+
use_flash_attn (`bool`, *optional*, defaults to `True`):
|
| 46 |
+
Whether to use flash attention mechanism.
|
| 47 |
+
hidden_act (`str` or `function`, *optional*, defaults to `"gelu"`):
|
| 48 |
+
The non-linear activation function (function or string) in the encoder and pooler. If string, `"gelu"`,
|
| 49 |
+
`"relu"`, `"selu"` and `"gelu_new"` ``"gelu"` are supported.
|
| 50 |
+
layer_norm_eps (`float`, *optional*, defaults to 1e-6):
|
| 51 |
+
The epsilon used by the layer normalization layers.
|
| 52 |
+
dropout (`float`, *optional*, defaults to 0.0):
|
| 53 |
+
The dropout probability for all fully connected layers in the embeddings, encoder, and pooler.
|
| 54 |
+
drop_path_rate (`float`, *optional*, defaults to 0.0):
|
| 55 |
+
Dropout rate for stochastic depth.
|
| 56 |
+
attention_dropout (`float`, *optional*, defaults to 0.0):
|
| 57 |
+
The dropout ratio for the attention probabilities.
|
| 58 |
+
initializer_range (`float`, *optional*, defaults to 0.02):
|
| 59 |
+
The standard deviation of the truncated_normal_initializer for initializing all weight matrices.
|
| 60 |
+
initializer_factor (`float`, *optional*, defaults to 0.1):
|
| 61 |
+
A factor for layer scale.
|
| 62 |
+
"""
|
| 63 |
+
|
| 64 |
+
model_type = 'intern_vit_6b'
|
| 65 |
+
|
| 66 |
+
def __init__(
|
| 67 |
+
self,
|
| 68 |
+
num_channels=3,
|
| 69 |
+
patch_size=14,
|
| 70 |
+
image_size=224,
|
| 71 |
+
qkv_bias=False,
|
| 72 |
+
hidden_size=3200,
|
| 73 |
+
num_attention_heads=25,
|
| 74 |
+
intermediate_size=12800,
|
| 75 |
+
qk_normalization=True,
|
| 76 |
+
num_hidden_layers=48,
|
| 77 |
+
use_flash_attn=True,
|
| 78 |
+
hidden_act='gelu',
|
| 79 |
+
norm_type='rms_norm',
|
| 80 |
+
layer_norm_eps=1e-6,
|
| 81 |
+
dropout=0.0,
|
| 82 |
+
drop_path_rate=0.0,
|
| 83 |
+
attention_dropout=0.0,
|
| 84 |
+
initializer_range=0.02,
|
| 85 |
+
initializer_factor=0.1,
|
| 86 |
+
**kwargs,
|
| 87 |
+
):
|
| 88 |
+
super().__init__(**kwargs)
|
| 89 |
+
|
| 90 |
+
self.hidden_size = hidden_size
|
| 91 |
+
self.intermediate_size = intermediate_size
|
| 92 |
+
self.dropout = dropout
|
| 93 |
+
self.drop_path_rate = drop_path_rate
|
| 94 |
+
self.num_hidden_layers = num_hidden_layers
|
| 95 |
+
self.num_attention_heads = num_attention_heads
|
| 96 |
+
self.num_channels = num_channels
|
| 97 |
+
self.patch_size = patch_size
|
| 98 |
+
self.image_size = image_size
|
| 99 |
+
self.initializer_range = initializer_range
|
| 100 |
+
self.initializer_factor = initializer_factor
|
| 101 |
+
self.attention_dropout = attention_dropout
|
| 102 |
+
self.layer_norm_eps = layer_norm_eps
|
| 103 |
+
self.hidden_act = hidden_act
|
| 104 |
+
self.norm_type = norm_type
|
| 105 |
+
self.qkv_bias = qkv_bias
|
| 106 |
+
self.qk_normalization = qk_normalization
|
| 107 |
+
self.use_flash_attn = use_flash_attn
|
| 108 |
+
|
| 109 |
+
@classmethod
|
| 110 |
+
def from_pretrained(cls, pretrained_model_name_or_path: Union[str, os.PathLike], **kwargs) -> 'PretrainedConfig':
|
| 111 |
+
config_dict, kwargs = cls.get_config_dict(pretrained_model_name_or_path, **kwargs)
|
| 112 |
+
|
| 113 |
+
if 'vision_config' in config_dict:
|
| 114 |
+
config_dict = config_dict['vision_config']
|
| 115 |
+
|
| 116 |
+
if 'model_type' in config_dict and hasattr(cls, 'model_type') and config_dict['model_type'] != cls.model_type:
|
| 117 |
+
logger.warning(
|
| 118 |
+
f"You are using a model of type {config_dict['model_type']} to instantiate a model of type "
|
| 119 |
+
f'{cls.model_type}. This is not supported for all configurations of models and can yield errors.'
|
| 120 |
+
)
|
| 121 |
+
|
| 122 |
+
return cls.from_dict(config_dict, **kwargs)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class InternVLChatConfig(PretrainedConfig):
|
| 126 |
+
model_type = 'internvl_chat'
|
| 127 |
+
is_composition = True
|
| 128 |
+
|
| 129 |
+
def __init__(
|
| 130 |
+
self,
|
| 131 |
+
vision_config=None,
|
| 132 |
+
llm_config=None,
|
| 133 |
+
use_backbone_lora=0,
|
| 134 |
+
use_llm_lora=0,
|
| 135 |
+
select_layer=-1,
|
| 136 |
+
force_image_size=None,
|
| 137 |
+
downsample_ratio=0.5,
|
| 138 |
+
template=None,
|
| 139 |
+
dynamic_image_size=False,
|
| 140 |
+
use_thumbnail=False,
|
| 141 |
+
ps_version='v1',
|
| 142 |
+
min_dynamic_patch=1,
|
| 143 |
+
max_dynamic_patch=6,
|
| 144 |
+
**kwargs):
|
| 145 |
+
super().__init__(**kwargs)
|
| 146 |
+
|
| 147 |
+
if vision_config is None:
|
| 148 |
+
vision_config = {'architectures': ['InternVisionModel']}
|
| 149 |
+
logger.info('vision_config is None. Initializing the InternVisionConfig with default values.')
|
| 150 |
+
|
| 151 |
+
if llm_config is None:
|
| 152 |
+
llm_config = {'architectures': ['Qwen2ForCausalLM']}
|
| 153 |
+
logger.info('llm_config is None. Initializing the LlamaConfig config with default values (`LlamaConfig`).')
|
| 154 |
+
|
| 155 |
+
self.vision_config = InternVisionConfig(**vision_config)
|
| 156 |
+
if llm_config.get('architectures')[0] == 'LlamaForCausalLM':
|
| 157 |
+
self.llm_config = LlamaConfig(**llm_config)
|
| 158 |
+
elif llm_config.get('architectures')[0] == 'Qwen2ForCausalLM':
|
| 159 |
+
self.llm_config = Qwen2Config(**llm_config)
|
| 160 |
+
else:
|
| 161 |
+
raise ValueError('Unsupported architecture: {}'.format(llm_config.get('architectures')[0]))
|
| 162 |
+
self.use_backbone_lora = use_backbone_lora
|
| 163 |
+
self.use_llm_lora = use_llm_lora
|
| 164 |
+
self.select_layer = select_layer
|
| 165 |
+
self.force_image_size = force_image_size
|
| 166 |
+
self.downsample_ratio = downsample_ratio
|
| 167 |
+
self.template = template
|
| 168 |
+
self.dynamic_image_size = dynamic_image_size
|
| 169 |
+
self.use_thumbnail = use_thumbnail
|
| 170 |
+
self.ps_version = ps_version # pixel shuffle version
|
| 171 |
+
self.min_dynamic_patch = min_dynamic_patch
|
| 172 |
+
self.max_dynamic_patch = max_dynamic_patch
|
| 173 |
+
# By default, we use tie_word_embeddings=False for models of all sizes.
|
| 174 |
+
self.tie_word_embeddings = self.llm_config.tie_word_embeddings
|
| 175 |
+
|
| 176 |
+
logger.info(f'vision_select_layer: {self.select_layer}')
|
| 177 |
+
logger.info(f'ps_version: {self.ps_version}')
|
| 178 |
+
logger.info(f'min_dynamic_patch: {self.min_dynamic_patch}')
|
| 179 |
+
logger.info(f'max_dynamic_patch: {self.max_dynamic_patch}')
|
| 180 |
+
|
| 181 |
+
def to_dict(self):
|
| 182 |
+
"""
|
| 183 |
+
Serializes this instance to a Python dictionary. Override the default [`~PretrainedConfig.to_dict`].
|
| 184 |
+
|
| 185 |
+
Returns:
|
| 186 |
+
`Dict[str, any]`: Dictionary of all the attributes that make up this configuration instance,
|
| 187 |
+
"""
|
| 188 |
+
output = copy.deepcopy(self.__dict__)
|
| 189 |
+
output['vision_config'] = self.vision_config.to_dict()
|
| 190 |
+
output['llm_config'] = self.llm_config.to_dict()
|
| 191 |
+
output['model_type'] = self.__class__.model_type
|
| 192 |
+
output['use_backbone_lora'] = self.use_backbone_lora
|
| 193 |
+
output['use_llm_lora'] = self.use_llm_lora
|
| 194 |
+
output['select_layer'] = self.select_layer
|
| 195 |
+
output['force_image_size'] = self.force_image_size
|
| 196 |
+
output['downsample_ratio'] = self.downsample_ratio
|
| 197 |
+
output['template'] = self.template
|
| 198 |
+
output['dynamic_image_size'] = self.dynamic_image_size
|
| 199 |
+
output['use_thumbnail'] = self.use_thumbnail
|
| 200 |
+
output['ps_version'] = self.ps_version
|
| 201 |
+
output['min_dynamic_patch'] = self.min_dynamic_patch
|
| 202 |
+
output['max_dynamic_patch'] = self.max_dynamic_patch
|
| 203 |
+
|
| 204 |
+
return output
|
llm-awq/tinychat/models/internvl/conversation.py
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Conversation prompt templates.
|
| 3 |
+
|
| 4 |
+
We kindly request that you import fastchat instead of copying this file if you wish to use it.
|
| 5 |
+
If you have changes in mind, please contribute back so the community can benefit collectively and continue to maintain these valuable templates.
|
| 6 |
+
|
| 7 |
+
Modified from https://github.com/lm-sys/FastChat/blob/main/fastchat/conversation.py
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import dataclasses
|
| 11 |
+
from enum import IntEnum, auto
|
| 12 |
+
from typing import Dict, List, Tuple, Union
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class SeparatorStyle(IntEnum):
|
| 16 |
+
"""Separator styles."""
|
| 17 |
+
|
| 18 |
+
ADD_COLON_SINGLE = auto()
|
| 19 |
+
ADD_COLON_TWO = auto()
|
| 20 |
+
ADD_COLON_SPACE_SINGLE = auto()
|
| 21 |
+
NO_COLON_SINGLE = auto()
|
| 22 |
+
NO_COLON_TWO = auto()
|
| 23 |
+
ADD_NEW_LINE_SINGLE = auto()
|
| 24 |
+
LLAMA2 = auto()
|
| 25 |
+
CHATGLM = auto()
|
| 26 |
+
CHATML = auto()
|
| 27 |
+
CHATINTERN = auto()
|
| 28 |
+
DOLLY = auto()
|
| 29 |
+
RWKV = auto()
|
| 30 |
+
PHOENIX = auto()
|
| 31 |
+
ROBIN = auto()
|
| 32 |
+
FALCON_CHAT = auto()
|
| 33 |
+
CHATGLM3 = auto()
|
| 34 |
+
INTERNVL_ZH = auto()
|
| 35 |
+
MPT = auto()
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@dataclasses.dataclass
|
| 39 |
+
class Conversation:
|
| 40 |
+
"""A class that manages prompt templates and keeps all conversation history."""
|
| 41 |
+
|
| 42 |
+
# The name of this template
|
| 43 |
+
name: str
|
| 44 |
+
# The template of the system prompt
|
| 45 |
+
system_template: str = '{system_message}'
|
| 46 |
+
# The system message
|
| 47 |
+
system_message: str = ''
|
| 48 |
+
# The names of two roles
|
| 49 |
+
roles: Tuple[str] = ('USER', 'ASSISTANT')
|
| 50 |
+
# All messages. Each item is (role, message).
|
| 51 |
+
messages: List[List[str]] = ()
|
| 52 |
+
# The number of few shot examples
|
| 53 |
+
offset: int = 0
|
| 54 |
+
# The separator style and configurations
|
| 55 |
+
sep_style: SeparatorStyle = SeparatorStyle.ADD_COLON_SINGLE
|
| 56 |
+
sep: str = '\n'
|
| 57 |
+
sep2: str = None
|
| 58 |
+
# Stop criteria (the default one is EOS token)
|
| 59 |
+
stop_str: Union[str, List[str]] = None
|
| 60 |
+
# Stops generation if meeting any token in this list
|
| 61 |
+
stop_token_ids: List[int] = None
|
| 62 |
+
|
| 63 |
+
def get_prompt(self) -> str:
|
| 64 |
+
"""Get the prompt for generation."""
|
| 65 |
+
system_prompt = self.system_template.format(system_message=self.system_message)
|
| 66 |
+
if self.sep_style == SeparatorStyle.ADD_COLON_SINGLE:
|
| 67 |
+
ret = system_prompt + self.sep
|
| 68 |
+
for role, message in self.messages:
|
| 69 |
+
if message:
|
| 70 |
+
ret += role + ': ' + message + self.sep
|
| 71 |
+
else:
|
| 72 |
+
ret += role + ':'
|
| 73 |
+
return ret
|
| 74 |
+
elif self.sep_style == SeparatorStyle.ADD_COLON_TWO:
|
| 75 |
+
seps = [self.sep, self.sep2]
|
| 76 |
+
ret = system_prompt + seps[0]
|
| 77 |
+
for i, (role, message) in enumerate(self.messages):
|
| 78 |
+
if message:
|
| 79 |
+
ret += role + ': ' + message + seps[i % 2]
|
| 80 |
+
else:
|
| 81 |
+
ret += role + ':'
|
| 82 |
+
return ret
|
| 83 |
+
elif self.sep_style == SeparatorStyle.ADD_COLON_SPACE_SINGLE:
|
| 84 |
+
ret = system_prompt + self.sep
|
| 85 |
+
for role, message in self.messages:
|
| 86 |
+
if message:
|
| 87 |
+
ret += role + ': ' + message + self.sep
|
| 88 |
+
else:
|
| 89 |
+
ret += role + ': ' # must be end with a space
|
| 90 |
+
return ret
|
| 91 |
+
elif self.sep_style == SeparatorStyle.ADD_NEW_LINE_SINGLE:
|
| 92 |
+
ret = '' if system_prompt == '' else system_prompt + self.sep
|
| 93 |
+
for role, message in self.messages:
|
| 94 |
+
if message:
|
| 95 |
+
ret += role + '\n' + message + self.sep
|
| 96 |
+
else:
|
| 97 |
+
ret += role + '\n'
|
| 98 |
+
return ret
|
| 99 |
+
elif self.sep_style == SeparatorStyle.NO_COLON_SINGLE:
|
| 100 |
+
ret = system_prompt
|
| 101 |
+
for role, message in self.messages:
|
| 102 |
+
if message:
|
| 103 |
+
ret += role + message + self.sep
|
| 104 |
+
else:
|
| 105 |
+
ret += role
|
| 106 |
+
return ret
|
| 107 |
+
elif self.sep_style == SeparatorStyle.NO_COLON_TWO:
|
| 108 |
+
seps = [self.sep, self.sep2]
|
| 109 |
+
ret = system_prompt
|
| 110 |
+
for i, (role, message) in enumerate(self.messages):
|
| 111 |
+
if message:
|
| 112 |
+
ret += role + message + seps[i % 2]
|
| 113 |
+
else:
|
| 114 |
+
ret += role
|
| 115 |
+
return ret
|
| 116 |
+
elif self.sep_style == SeparatorStyle.RWKV:
|
| 117 |
+
ret = system_prompt
|
| 118 |
+
for i, (role, message) in enumerate(self.messages):
|
| 119 |
+
if message:
|
| 120 |
+
ret += (
|
| 121 |
+
role
|
| 122 |
+
+ ': '
|
| 123 |
+
+ message.replace('\r\n', '\n').replace('\n\n', '\n')
|
| 124 |
+
)
|
| 125 |
+
ret += '\n\n'
|
| 126 |
+
else:
|
| 127 |
+
ret += role + ':'
|
| 128 |
+
return ret
|
| 129 |
+
elif self.sep_style == SeparatorStyle.LLAMA2:
|
| 130 |
+
seps = [self.sep, self.sep2]
|
| 131 |
+
if self.system_message:
|
| 132 |
+
ret = system_prompt
|
| 133 |
+
else:
|
| 134 |
+
ret = '[INST] '
|
| 135 |
+
for i, (role, message) in enumerate(self.messages):
|
| 136 |
+
tag = self.roles[i % 2]
|
| 137 |
+
if message:
|
| 138 |
+
if i == 0:
|
| 139 |
+
ret += message + ' '
|
| 140 |
+
else:
|
| 141 |
+
ret += tag + ' ' + message + seps[i % 2]
|
| 142 |
+
else:
|
| 143 |
+
ret += tag
|
| 144 |
+
return ret
|
| 145 |
+
elif self.sep_style == SeparatorStyle.CHATGLM:
|
| 146 |
+
# source: https://huggingface.co/THUDM/chatglm-6b/blob/1d240ba371910e9282298d4592532d7f0f3e9f3e/modeling_chatglm.py#L1302-L1308
|
| 147 |
+
# source2: https://huggingface.co/THUDM/chatglm2-6b/blob/e186c891cf64310ac66ef10a87e6635fa6c2a579/modeling_chatglm.py#L926
|
| 148 |
+
round_add_n = 1 if self.name == 'chatglm2' else 0
|
| 149 |
+
if system_prompt:
|
| 150 |
+
ret = system_prompt + self.sep
|
| 151 |
+
else:
|
| 152 |
+
ret = ''
|
| 153 |
+
|
| 154 |
+
for i, (role, message) in enumerate(self.messages):
|
| 155 |
+
if i % 2 == 0:
|
| 156 |
+
ret += f'[Round {i//2 + round_add_n}]{self.sep}'
|
| 157 |
+
|
| 158 |
+
if message:
|
| 159 |
+
ret += f'{role}:{message}{self.sep}'
|
| 160 |
+
else:
|
| 161 |
+
ret += f'{role}:'
|
| 162 |
+
return ret
|
| 163 |
+
elif self.sep_style == SeparatorStyle.CHATML:
|
| 164 |
+
ret = '' if system_prompt == '' else system_prompt + self.sep + '\n'
|
| 165 |
+
for role, message in self.messages:
|
| 166 |
+
if message:
|
| 167 |
+
ret += role + '\n' + message + self.sep + '\n'
|
| 168 |
+
else:
|
| 169 |
+
ret += role + '\n'
|
| 170 |
+
return ret
|
| 171 |
+
elif self.sep_style == SeparatorStyle.CHATGLM3:
|
| 172 |
+
ret = ''
|
| 173 |
+
if self.system_message:
|
| 174 |
+
ret += system_prompt
|
| 175 |
+
for role, message in self.messages:
|
| 176 |
+
if message:
|
| 177 |
+
ret += role + '\n' + ' ' + message
|
| 178 |
+
else:
|
| 179 |
+
ret += role
|
| 180 |
+
return ret
|
| 181 |
+
elif self.sep_style == SeparatorStyle.CHATINTERN:
|
| 182 |
+
# source: https://huggingface.co/internlm/internlm-chat-7b-8k/blob/bd546fa984b4b0b86958f56bf37f94aa75ab8831/modeling_internlm.py#L771
|
| 183 |
+
seps = [self.sep, self.sep2]
|
| 184 |
+
ret = system_prompt
|
| 185 |
+
for i, (role, message) in enumerate(self.messages):
|
| 186 |
+
# if i % 2 == 0:
|
| 187 |
+
# ret += "<s>"
|
| 188 |
+
if message:
|
| 189 |
+
ret += role + ':' + message + seps[i % 2] + '\n'
|
| 190 |
+
else:
|
| 191 |
+
ret += role + ':'
|
| 192 |
+
return ret
|
| 193 |
+
elif self.sep_style == SeparatorStyle.DOLLY:
|
| 194 |
+
seps = [self.sep, self.sep2]
|
| 195 |
+
ret = system_prompt
|
| 196 |
+
for i, (role, message) in enumerate(self.messages):
|
| 197 |
+
if message:
|
| 198 |
+
ret += role + ':\n' + message + seps[i % 2]
|
| 199 |
+
if i % 2 == 1:
|
| 200 |
+
ret += '\n\n'
|
| 201 |
+
else:
|
| 202 |
+
ret += role + ':\n'
|
| 203 |
+
return ret
|
| 204 |
+
elif self.sep_style == SeparatorStyle.PHOENIX:
|
| 205 |
+
ret = system_prompt
|
| 206 |
+
for role, message in self.messages:
|
| 207 |
+
if message:
|
| 208 |
+
ret += role + ': ' + '<s>' + message + '</s>'
|
| 209 |
+
else:
|
| 210 |
+
ret += role + ': ' + '<s>'
|
| 211 |
+
return ret
|
| 212 |
+
elif self.sep_style == SeparatorStyle.ROBIN:
|
| 213 |
+
ret = system_prompt + self.sep
|
| 214 |
+
for role, message in self.messages:
|
| 215 |
+
if message:
|
| 216 |
+
ret += role + ':\n' + message + self.sep
|
| 217 |
+
else:
|
| 218 |
+
ret += role + ':\n'
|
| 219 |
+
return ret
|
| 220 |
+
elif self.sep_style == SeparatorStyle.FALCON_CHAT:
|
| 221 |
+
ret = ''
|
| 222 |
+
if self.system_message:
|
| 223 |
+
ret += system_prompt + self.sep
|
| 224 |
+
for role, message in self.messages:
|
| 225 |
+
if message:
|
| 226 |
+
ret += role + ': ' + message + self.sep
|
| 227 |
+
else:
|
| 228 |
+
ret += role + ':'
|
| 229 |
+
|
| 230 |
+
return ret
|
| 231 |
+
elif self.sep_style == SeparatorStyle.INTERNVL_ZH:
|
| 232 |
+
seps = [self.sep, self.sep2]
|
| 233 |
+
ret = self.system_message + seps[0]
|
| 234 |
+
for i, (role, message) in enumerate(self.messages):
|
| 235 |
+
if message:
|
| 236 |
+
ret += role + ': ' + message + seps[i % 2]
|
| 237 |
+
else:
|
| 238 |
+
ret += role + ':'
|
| 239 |
+
return ret
|
| 240 |
+
elif self.sep_style == SeparatorStyle.MPT:
|
| 241 |
+
ret = system_prompt + self.sep
|
| 242 |
+
for role, message in self.messages:
|
| 243 |
+
if message:
|
| 244 |
+
if type(message) is tuple:
|
| 245 |
+
message, _, _ = message
|
| 246 |
+
ret += role + message + self.sep
|
| 247 |
+
else:
|
| 248 |
+
ret += role
|
| 249 |
+
return ret
|
| 250 |
+
else:
|
| 251 |
+
raise ValueError(f'Invalid style: {self.sep_style}')
|
| 252 |
+
|
| 253 |
+
def set_system_message(self, system_message: str):
|
| 254 |
+
"""Set the system message."""
|
| 255 |
+
self.system_message = system_message
|
| 256 |
+
|
| 257 |
+
def append_message(self, role: str, message: str):
|
| 258 |
+
"""Append a new message."""
|
| 259 |
+
self.messages.append([role, message])
|
| 260 |
+
|
| 261 |
+
def update_last_message(self, message: str):
|
| 262 |
+
"""Update the last output.
|
| 263 |
+
|
| 264 |
+
The last message is typically set to be None when constructing the prompt,
|
| 265 |
+
so we need to update it in-place after getting the response from a model.
|
| 266 |
+
"""
|
| 267 |
+
self.messages[-1][1] = message
|
| 268 |
+
|
| 269 |
+
def to_gradio_chatbot(self):
|
| 270 |
+
"""Convert the conversation to gradio chatbot format."""
|
| 271 |
+
ret = []
|
| 272 |
+
for i, (role, msg) in enumerate(self.messages[self.offset :]):
|
| 273 |
+
if i % 2 == 0:
|
| 274 |
+
ret.append([msg, None])
|
| 275 |
+
else:
|
| 276 |
+
ret[-1][-1] = msg
|
| 277 |
+
return ret
|
| 278 |
+
|
| 279 |
+
def to_openai_api_messages(self):
|
| 280 |
+
"""Convert the conversation to OpenAI chat completion format."""
|
| 281 |
+
ret = [{'role': 'system', 'content': self.system_message}]
|
| 282 |
+
|
| 283 |
+
for i, (_, msg) in enumerate(self.messages[self.offset :]):
|
| 284 |
+
if i % 2 == 0:
|
| 285 |
+
ret.append({'role': 'user', 'content': msg})
|
| 286 |
+
else:
|
| 287 |
+
if msg is not None:
|
| 288 |
+
ret.append({'role': 'assistant', 'content': msg})
|
| 289 |
+
return ret
|
| 290 |
+
|
| 291 |
+
def copy(self):
|
| 292 |
+
return Conversation(
|
| 293 |
+
name=self.name,
|
| 294 |
+
system_template=self.system_template,
|
| 295 |
+
system_message=self.system_message,
|
| 296 |
+
roles=self.roles,
|
| 297 |
+
messages=[[x, y] for x, y in self.messages],
|
| 298 |
+
offset=self.offset,
|
| 299 |
+
sep_style=self.sep_style,
|
| 300 |
+
sep=self.sep,
|
| 301 |
+
sep2=self.sep2,
|
| 302 |
+
stop_str=self.stop_str,
|
| 303 |
+
stop_token_ids=self.stop_token_ids,
|
| 304 |
+
)
|
| 305 |
+
|
| 306 |
+
def dict(self):
|
| 307 |
+
return {
|
| 308 |
+
'template_name': self.name,
|
| 309 |
+
'system_message': self.system_message,
|
| 310 |
+
'roles': self.roles,
|
| 311 |
+
'messages': self.messages,
|
| 312 |
+
'offset': self.offset,
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
# A global registry for all conversation templates
|
| 317 |
+
conv_templates: Dict[str, Conversation] = {}
|
| 318 |
+
|
| 319 |
+
|
| 320 |
+
def register_conv_template(template: Conversation, override: bool = False):
|
| 321 |
+
"""Register a new conversation template."""
|
| 322 |
+
if not override:
|
| 323 |
+
assert (
|
| 324 |
+
template.name not in conv_templates
|
| 325 |
+
), f'{template.name} has been registered.'
|
| 326 |
+
|
| 327 |
+
conv_templates[template.name] = template
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def get_conv_template(name: str) -> Conversation:
|
| 331 |
+
"""Get a conversation template."""
|
| 332 |
+
return conv_templates[name].copy()
|
| 333 |
+
|
| 334 |
+
|
| 335 |
+
# Both Hermes-2 and internlm2-chat are chatml-format conversation templates. The difference
|
| 336 |
+
# is that during training, the preprocessing function for the Hermes-2 template doesn't add
|
| 337 |
+
# <s> at the beginning of the tokenized sequence, while the internlm2-chat template does.
|
| 338 |
+
# Therefore, they are completely equivalent during inference.
|
| 339 |
+
register_conv_template(
|
| 340 |
+
Conversation(
|
| 341 |
+
name='Hermes-2',
|
| 342 |
+
system_template='<|im_start|>system\n{system_message}',
|
| 343 |
+
# note: The new system prompt was not used here to avoid changes in benchmark performance.
|
| 344 |
+
# system_message='我是书生·万象,英文名是InternVL,是由上海人工智能实验室、清华大学及多家合作单位联合开发的多模态大语言模型。',
|
| 345 |
+
system_message='你是由上海人工智能实验室联合商汤科技开发的书生多模态大模型,英文名叫InternVL, 是一个有用无害的人工智能助手。',
|
| 346 |
+
roles=('<|im_start|>user\n', '<|im_start|>assistant\n'),
|
| 347 |
+
sep_style=SeparatorStyle.MPT,
|
| 348 |
+
sep='<|im_end|>',
|
| 349 |
+
stop_str='<|endoftext|>',
|
| 350 |
+
)
|
| 351 |
+
)
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
register_conv_template(
|
| 355 |
+
Conversation(
|
| 356 |
+
name='internlm2-chat',
|
| 357 |
+
system_template='<|im_start|>system\n{system_message}',
|
| 358 |
+
# note: The new system prompt was not used here to avoid changes in benchmark performance.
|
| 359 |
+
# system_message='我是书生·万象,英文名是InternVL,是由上海人工智能实验室、清华大学及多家合作单位联合开发的多模态大语言模型。',
|
| 360 |
+
system_message='你是由上海人工智能实验室联合商汤科技开发的书生多模态大模型,英文名叫InternVL, 是一个有用无害的人工智能助手。',
|
| 361 |
+
roles=('<|im_start|>user\n', '<|im_start|>assistant\n'),
|
| 362 |
+
sep_style=SeparatorStyle.MPT,
|
| 363 |
+
sep='<|im_end|>',
|
| 364 |
+
)
|
| 365 |
+
)
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
register_conv_template(
|
| 369 |
+
Conversation(
|
| 370 |
+
name='phi3-chat',
|
| 371 |
+
system_template='<|system|>\n{system_message}',
|
| 372 |
+
# note: The new system prompt was not used here to avoid changes in benchmark performance.
|
| 373 |
+
# system_message='我是书生·万象,英文名是InternVL,是由上海人工智能实验室、清华大学及多家合作单位联合开发的多模态大语言模型。',
|
| 374 |
+
system_message='你是由上海人工智能实验室联合商汤科技开发的书生多模态大模型,英文名叫InternVL, 是一个有用无害的人工智能助手。',
|
| 375 |
+
roles=('<|user|>\n', '<|assistant|>\n'),
|
| 376 |
+
sep_style=SeparatorStyle.MPT,
|
| 377 |
+
sep='<|end|>',
|
| 378 |
+
)
|
| 379 |
+
)
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
register_conv_template(
|
| 383 |
+
Conversation(
|
| 384 |
+
name='internvl2_5',
|
| 385 |
+
system_template='<|im_start|>system\n{system_message}',
|
| 386 |
+
system_message='你是书生·万象,英文名是InternVL,是由上海人工智能实验室、清华大学及多家合作单位联合开发的多模态大语言模型。',
|
| 387 |
+
roles=('<|im_start|>user\n', '<|im_start|>assistant\n'),
|
| 388 |
+
sep_style=SeparatorStyle.MPT,
|
| 389 |
+
sep='<|im_end|>\n',
|
| 390 |
+
)
|
| 391 |
+
)
|
llm-awq/tinychat/models/internvl/internvit.py
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple, Union
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
import torch.utils.checkpoint
|
| 6 |
+
from einops import rearrange
|
| 7 |
+
from timm.layers import DropPath
|
| 8 |
+
from torch import nn
|
| 9 |
+
from transformers.activations import ACT2FN
|
| 10 |
+
from transformers.modeling_outputs import (BaseModelOutput,
|
| 11 |
+
BaseModelOutputWithPooling)
|
| 12 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 13 |
+
from transformers.utils import logging
|
| 14 |
+
|
| 15 |
+
from .configuration_internvl import InternVisionConfig
|
| 16 |
+
|
| 17 |
+
try:
|
| 18 |
+
from flash_attn.bert_padding import pad_input, unpad_input
|
| 19 |
+
from flash_attn.flash_attn_interface import \
|
| 20 |
+
flash_attn_varlen_qkvpacked_func
|
| 21 |
+
has_flash_attn = True
|
| 22 |
+
except:
|
| 23 |
+
print('FlashAttention2 is not installed.')
|
| 24 |
+
has_flash_attn = False
|
| 25 |
+
|
| 26 |
+
logger = logging.get_logger(__name__)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class FlashAttention(nn.Module):
|
| 30 |
+
"""Implement the scaled dot product attention with softmax.
|
| 31 |
+
Arguments
|
| 32 |
+
---------
|
| 33 |
+
softmax_scale: The temperature to use for the softmax attention.
|
| 34 |
+
(default: 1/sqrt(d_keys) where d_keys is computed at
|
| 35 |
+
runtime)
|
| 36 |
+
attention_dropout: The dropout rate to apply to the attention
|
| 37 |
+
(default: 0.0)
|
| 38 |
+
"""
|
| 39 |
+
|
| 40 |
+
def __init__(self, softmax_scale=None, attention_dropout=0.0, device=None, dtype=None):
|
| 41 |
+
super().__init__()
|
| 42 |
+
self.softmax_scale = softmax_scale
|
| 43 |
+
self.dropout_p = attention_dropout
|
| 44 |
+
|
| 45 |
+
def forward(self, qkv, key_padding_mask=None, causal=False, cu_seqlens=None,
|
| 46 |
+
max_s=None, need_weights=False):
|
| 47 |
+
"""Implements the multihead softmax attention.
|
| 48 |
+
Arguments
|
| 49 |
+
---------
|
| 50 |
+
qkv: The tensor containing the query, key, and value. (B, S, 3, H, D) if key_padding_mask is None
|
| 51 |
+
if unpadded: (nnz, 3, h, d)
|
| 52 |
+
key_padding_mask: a bool tensor of shape (B, S)
|
| 53 |
+
"""
|
| 54 |
+
assert not need_weights
|
| 55 |
+
assert qkv.dtype in [torch.float16, torch.bfloat16]
|
| 56 |
+
assert qkv.is_cuda
|
| 57 |
+
|
| 58 |
+
if cu_seqlens is None:
|
| 59 |
+
batch_size = qkv.shape[0]
|
| 60 |
+
seqlen = qkv.shape[1]
|
| 61 |
+
if key_padding_mask is None:
|
| 62 |
+
qkv = rearrange(qkv, 'b s ... -> (b s) ...')
|
| 63 |
+
max_s = seqlen
|
| 64 |
+
cu_seqlens = torch.arange(0, (batch_size + 1) * seqlen, step=seqlen, dtype=torch.int32,
|
| 65 |
+
device=qkv.device)
|
| 66 |
+
output = flash_attn_varlen_qkvpacked_func(
|
| 67 |
+
qkv, cu_seqlens, max_s, self.dropout_p if self.training else 0.0,
|
| 68 |
+
softmax_scale=self.softmax_scale, causal=causal
|
| 69 |
+
)
|
| 70 |
+
output = rearrange(output, '(b s) ... -> b s ...', b=batch_size)
|
| 71 |
+
else:
|
| 72 |
+
nheads = qkv.shape[-2]
|
| 73 |
+
x = rearrange(qkv, 'b s three h d -> b s (three h d)')
|
| 74 |
+
x_unpad, indices, cu_seqlens, max_s = unpad_input(x, key_padding_mask)
|
| 75 |
+
x_unpad = rearrange(x_unpad, 'nnz (three h d) -> nnz three h d', three=3, h=nheads)
|
| 76 |
+
output_unpad = flash_attn_varlen_qkvpacked_func(
|
| 77 |
+
x_unpad, cu_seqlens, max_s, self.dropout_p if self.training else 0.0,
|
| 78 |
+
softmax_scale=self.softmax_scale, causal=causal
|
| 79 |
+
)
|
| 80 |
+
output = rearrange(pad_input(rearrange(output_unpad, 'nnz h d -> nnz (h d)'),
|
| 81 |
+
indices, batch_size, seqlen),
|
| 82 |
+
'b s (h d) -> b s h d', h=nheads)
|
| 83 |
+
else:
|
| 84 |
+
assert max_s is not None
|
| 85 |
+
output = flash_attn_varlen_qkvpacked_func(
|
| 86 |
+
qkv, cu_seqlens, max_s, self.dropout_p if self.training else 0.0,
|
| 87 |
+
softmax_scale=self.softmax_scale, causal=causal
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
return output, None
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class InternRMSNorm(nn.Module):
|
| 94 |
+
def __init__(self, hidden_size, eps=1e-6):
|
| 95 |
+
super().__init__()
|
| 96 |
+
self.weight = nn.Parameter(torch.ones(hidden_size))
|
| 97 |
+
self.variance_epsilon = eps
|
| 98 |
+
|
| 99 |
+
def forward(self, hidden_states):
|
| 100 |
+
input_dtype = hidden_states.dtype
|
| 101 |
+
hidden_states = hidden_states.to(torch.float32)
|
| 102 |
+
variance = hidden_states.pow(2).mean(-1, keepdim=True)
|
| 103 |
+
hidden_states = hidden_states * torch.rsqrt(variance + self.variance_epsilon)
|
| 104 |
+
return self.weight * hidden_states.to(input_dtype)
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
try:
|
| 108 |
+
from apex.normalization import FusedRMSNorm
|
| 109 |
+
|
| 110 |
+
InternRMSNorm = FusedRMSNorm # noqa
|
| 111 |
+
|
| 112 |
+
logger.info('Discovered apex.normalization.FusedRMSNorm - will use it instead of InternRMSNorm')
|
| 113 |
+
except ImportError:
|
| 114 |
+
# using the normal InternRMSNorm
|
| 115 |
+
pass
|
| 116 |
+
except Exception:
|
| 117 |
+
logger.warning('discovered apex but it failed to load, falling back to InternRMSNorm')
|
| 118 |
+
pass
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
NORM2FN = {
|
| 122 |
+
'rms_norm': InternRMSNorm,
|
| 123 |
+
'layer_norm': nn.LayerNorm,
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
|
| 127 |
+
class InternVisionEmbeddings(nn.Module):
|
| 128 |
+
def __init__(self, config: InternVisionConfig):
|
| 129 |
+
super().__init__()
|
| 130 |
+
self.config = config
|
| 131 |
+
self.embed_dim = config.hidden_size
|
| 132 |
+
self.image_size = config.image_size
|
| 133 |
+
self.patch_size = config.patch_size
|
| 134 |
+
|
| 135 |
+
self.class_embedding = nn.Parameter(
|
| 136 |
+
torch.randn(1, 1, self.embed_dim),
|
| 137 |
+
)
|
| 138 |
+
|
| 139 |
+
self.patch_embedding = nn.Conv2d(
|
| 140 |
+
in_channels=3, out_channels=self.embed_dim, kernel_size=self.patch_size, stride=self.patch_size
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
self.num_patches = (self.image_size // self.patch_size) ** 2
|
| 144 |
+
self.num_positions = self.num_patches + 1
|
| 145 |
+
|
| 146 |
+
self.position_embedding = nn.Parameter(torch.randn(1, self.num_positions, self.embed_dim))
|
| 147 |
+
|
| 148 |
+
def _get_pos_embed(self, pos_embed, H, W):
|
| 149 |
+
target_dtype = pos_embed.dtype
|
| 150 |
+
pos_embed = pos_embed.float().reshape(
|
| 151 |
+
1, self.image_size // self.patch_size, self.image_size // self.patch_size, -1).permute(0, 3, 1, 2)
|
| 152 |
+
pos_embed = F.interpolate(pos_embed, size=(H, W), mode='bicubic', align_corners=False). \
|
| 153 |
+
reshape(1, -1, H * W).permute(0, 2, 1).to(target_dtype)
|
| 154 |
+
return pos_embed
|
| 155 |
+
|
| 156 |
+
def forward(self, pixel_values: torch.FloatTensor) -> torch.Tensor:
|
| 157 |
+
target_dtype = self.patch_embedding.weight.dtype
|
| 158 |
+
patch_embeds = self.patch_embedding(pixel_values) # shape = [*, channel, width, height]
|
| 159 |
+
batch_size, _, height, width = patch_embeds.shape
|
| 160 |
+
patch_embeds = patch_embeds.flatten(2).transpose(1, 2)
|
| 161 |
+
class_embeds = self.class_embedding.expand(batch_size, 1, -1).to(target_dtype)
|
| 162 |
+
embeddings = torch.cat([class_embeds, patch_embeds], dim=1)
|
| 163 |
+
position_embedding = torch.cat([
|
| 164 |
+
self.position_embedding[:, :1, :],
|
| 165 |
+
self._get_pos_embed(self.position_embedding[:, 1:, :], height, width)
|
| 166 |
+
], dim=1)
|
| 167 |
+
embeddings = embeddings + position_embedding.to(target_dtype)
|
| 168 |
+
return embeddings
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class InternAttention(nn.Module):
|
| 172 |
+
"""Multi-headed attention from 'Attention Is All You Need' paper"""
|
| 173 |
+
|
| 174 |
+
def __init__(self, config: InternVisionConfig):
|
| 175 |
+
super().__init__()
|
| 176 |
+
self.config = config
|
| 177 |
+
self.embed_dim = config.hidden_size
|
| 178 |
+
self.num_heads = config.num_attention_heads
|
| 179 |
+
self.use_flash_attn = config.use_flash_attn and has_flash_attn
|
| 180 |
+
if config.use_flash_attn and not has_flash_attn:
|
| 181 |
+
print('Warning: Flash Attention is not available, use_flash_attn is set to False.')
|
| 182 |
+
self.head_dim = self.embed_dim // self.num_heads
|
| 183 |
+
if self.head_dim * self.num_heads != self.embed_dim:
|
| 184 |
+
raise ValueError(
|
| 185 |
+
f'embed_dim must be divisible by num_heads (got `embed_dim`: {self.embed_dim} and `num_heads`:'
|
| 186 |
+
f' {self.num_heads}).'
|
| 187 |
+
)
|
| 188 |
+
|
| 189 |
+
self.scale = self.head_dim ** -0.5
|
| 190 |
+
self.qkv = nn.Linear(self.embed_dim, 3 * self.embed_dim, bias=config.qkv_bias)
|
| 191 |
+
self.attn_drop = nn.Dropout(config.attention_dropout)
|
| 192 |
+
self.proj_drop = nn.Dropout(config.dropout)
|
| 193 |
+
|
| 194 |
+
self.qk_normalization = config.qk_normalization
|
| 195 |
+
|
| 196 |
+
if self.qk_normalization:
|
| 197 |
+
self.q_norm = InternRMSNorm(self.embed_dim, eps=config.layer_norm_eps)
|
| 198 |
+
self.k_norm = InternRMSNorm(self.embed_dim, eps=config.layer_norm_eps)
|
| 199 |
+
|
| 200 |
+
if self.use_flash_attn:
|
| 201 |
+
self.inner_attn = FlashAttention(attention_dropout=config.attention_dropout)
|
| 202 |
+
self.proj = nn.Linear(self.embed_dim, self.embed_dim)
|
| 203 |
+
|
| 204 |
+
def _naive_attn(self, x):
|
| 205 |
+
B, N, C = x.shape
|
| 206 |
+
qkv = self.qkv(x).reshape(B, N, 3, self.num_heads, C // self.num_heads).permute(2, 0, 3, 1, 4)
|
| 207 |
+
q, k, v = qkv.unbind(0) # make torchscript happy (cannot use tensor as tuple)
|
| 208 |
+
|
| 209 |
+
if self.qk_normalization:
|
| 210 |
+
B_, H_, N_, D_ = q.shape
|
| 211 |
+
q = self.q_norm(q.transpose(1, 2).flatten(-2, -1)).view(B_, N_, H_, D_).transpose(1, 2)
|
| 212 |
+
k = self.k_norm(k.transpose(1, 2).flatten(-2, -1)).view(B_, N_, H_, D_).transpose(1, 2)
|
| 213 |
+
|
| 214 |
+
attn = ((q * self.scale) @ k.transpose(-2, -1))
|
| 215 |
+
attn = attn.softmax(dim=-1)
|
| 216 |
+
attn = self.attn_drop(attn)
|
| 217 |
+
|
| 218 |
+
x = (attn @ v).transpose(1, 2).reshape(B, N, C)
|
| 219 |
+
x = self.proj(x)
|
| 220 |
+
x = self.proj_drop(x)
|
| 221 |
+
return x
|
| 222 |
+
|
| 223 |
+
def _flash_attn(self, x, key_padding_mask=None, need_weights=False):
|
| 224 |
+
qkv = self.qkv(x)
|
| 225 |
+
qkv = rearrange(qkv, 'b s (three h d) -> b s three h d', three=3, h=self.num_heads)
|
| 226 |
+
|
| 227 |
+
if self.qk_normalization:
|
| 228 |
+
q, k, v = qkv.unbind(2)
|
| 229 |
+
q = self.q_norm(q.flatten(-2, -1)).view(q.shape)
|
| 230 |
+
k = self.k_norm(k.flatten(-2, -1)).view(k.shape)
|
| 231 |
+
qkv = torch.stack([q, k, v], dim=2)
|
| 232 |
+
|
| 233 |
+
context, _ = self.inner_attn(
|
| 234 |
+
qkv, key_padding_mask=key_padding_mask, need_weights=need_weights, causal=False
|
| 235 |
+
)
|
| 236 |
+
outs = self.proj(rearrange(context, 'b s h d -> b s (h d)'))
|
| 237 |
+
outs = self.proj_drop(outs)
|
| 238 |
+
return outs
|
| 239 |
+
|
| 240 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 241 |
+
x = self._naive_attn(hidden_states) if not self.use_flash_attn else self._flash_attn(hidden_states)
|
| 242 |
+
return x
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
class InternMLP(nn.Module):
|
| 246 |
+
def __init__(self, config: InternVisionConfig):
|
| 247 |
+
super().__init__()
|
| 248 |
+
self.config = config
|
| 249 |
+
self.act = ACT2FN[config.hidden_act]
|
| 250 |
+
self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
|
| 251 |
+
self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
|
| 252 |
+
|
| 253 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 254 |
+
hidden_states = self.fc1(hidden_states)
|
| 255 |
+
hidden_states = self.act(hidden_states)
|
| 256 |
+
hidden_states = self.fc2(hidden_states)
|
| 257 |
+
return hidden_states
|
| 258 |
+
|
| 259 |
+
|
| 260 |
+
class InternVisionEncoderLayer(nn.Module):
|
| 261 |
+
def __init__(self, config: InternVisionConfig, drop_path_rate: float):
|
| 262 |
+
super().__init__()
|
| 263 |
+
self.embed_dim = config.hidden_size
|
| 264 |
+
self.intermediate_size = config.intermediate_size
|
| 265 |
+
self.norm_type = config.norm_type
|
| 266 |
+
|
| 267 |
+
self.attn = InternAttention(config)
|
| 268 |
+
self.mlp = InternMLP(config)
|
| 269 |
+
self.norm1 = NORM2FN[self.norm_type](self.embed_dim, eps=config.layer_norm_eps)
|
| 270 |
+
self.norm2 = NORM2FN[self.norm_type](self.embed_dim, eps=config.layer_norm_eps)
|
| 271 |
+
|
| 272 |
+
self.ls1 = nn.Parameter(config.initializer_factor * torch.ones(self.embed_dim))
|
| 273 |
+
self.ls2 = nn.Parameter(config.initializer_factor * torch.ones(self.embed_dim))
|
| 274 |
+
self.drop_path1 = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 275 |
+
self.drop_path2 = DropPath(drop_path_rate) if drop_path_rate > 0. else nn.Identity()
|
| 276 |
+
|
| 277 |
+
def forward(
|
| 278 |
+
self,
|
| 279 |
+
hidden_states: torch.Tensor,
|
| 280 |
+
) -> Tuple[torch.FloatTensor, Optional[torch.FloatTensor], Optional[Tuple[torch.FloatTensor]]]:
|
| 281 |
+
"""
|
| 282 |
+
Args:
|
| 283 |
+
hidden_states (`Tuple[torch.FloatTensor, Optional[torch.FloatTensor]]`): input to the layer of shape `(batch, seq_len, embed_dim)`
|
| 284 |
+
"""
|
| 285 |
+
hidden_states = hidden_states + self.drop_path1(self.attn(self.norm1(hidden_states).to(hidden_states.dtype)) * self.ls1)
|
| 286 |
+
|
| 287 |
+
hidden_states = hidden_states + self.drop_path2(self.mlp(self.norm2(hidden_states).to(hidden_states.dtype)) * self.ls2)
|
| 288 |
+
|
| 289 |
+
return hidden_states
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
class InternVisionEncoder(nn.Module):
|
| 293 |
+
"""
|
| 294 |
+
Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
|
| 295 |
+
[`InternEncoderLayer`].
|
| 296 |
+
|
| 297 |
+
Args:
|
| 298 |
+
config (`InternConfig`):
|
| 299 |
+
The corresponding vision configuration for the `InternEncoder`.
|
| 300 |
+
"""
|
| 301 |
+
|
| 302 |
+
def __init__(self, config: InternVisionConfig):
|
| 303 |
+
super().__init__()
|
| 304 |
+
self.config = config
|
| 305 |
+
# stochastic depth decay rule
|
| 306 |
+
dpr = [x.item() for x in torch.linspace(0, config.drop_path_rate, config.num_hidden_layers)]
|
| 307 |
+
self.layers = nn.ModuleList([
|
| 308 |
+
InternVisionEncoderLayer(config, dpr[idx]) for idx in range(config.num_hidden_layers)])
|
| 309 |
+
self.gradient_checkpointing = True
|
| 310 |
+
|
| 311 |
+
def forward(
|
| 312 |
+
self,
|
| 313 |
+
inputs_embeds,
|
| 314 |
+
output_hidden_states: Optional[bool] = None,
|
| 315 |
+
return_dict: Optional[bool] = None,
|
| 316 |
+
) -> Union[Tuple, BaseModelOutput]:
|
| 317 |
+
r"""
|
| 318 |
+
Args:
|
| 319 |
+
inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 320 |
+
Embedded representation of the inputs. Should be float, not int tokens.
|
| 321 |
+
output_hidden_states (`bool`, *optional*):
|
| 322 |
+
Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors
|
| 323 |
+
for more detail.
|
| 324 |
+
return_dict (`bool`, *optional*):
|
| 325 |
+
Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
|
| 326 |
+
"""
|
| 327 |
+
output_hidden_states = (
|
| 328 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 329 |
+
)
|
| 330 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 331 |
+
|
| 332 |
+
encoder_states = () if output_hidden_states else None
|
| 333 |
+
hidden_states = inputs_embeds
|
| 334 |
+
|
| 335 |
+
for idx, encoder_layer in enumerate(self.layers):
|
| 336 |
+
if output_hidden_states:
|
| 337 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 338 |
+
if self.gradient_checkpointing and self.training:
|
| 339 |
+
layer_outputs = torch.utils.checkpoint.checkpoint(
|
| 340 |
+
encoder_layer,
|
| 341 |
+
hidden_states)
|
| 342 |
+
else:
|
| 343 |
+
layer_outputs = encoder_layer(
|
| 344 |
+
hidden_states,
|
| 345 |
+
)
|
| 346 |
+
hidden_states = layer_outputs
|
| 347 |
+
|
| 348 |
+
if output_hidden_states:
|
| 349 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 350 |
+
|
| 351 |
+
if not return_dict:
|
| 352 |
+
return tuple(v for v in [hidden_states, encoder_states] if v is not None)
|
| 353 |
+
return BaseModelOutput(
|
| 354 |
+
last_hidden_state=hidden_states, hidden_states=encoder_states
|
| 355 |
+
)
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
class InternVisionModel(PreTrainedModel):
|
| 359 |
+
main_input_name = 'pixel_values'
|
| 360 |
+
_supports_flash_attn_2 = True
|
| 361 |
+
supports_gradient_checkpointing = True
|
| 362 |
+
config_class = InternVisionConfig
|
| 363 |
+
_no_split_modules = ['InternVisionEncoderLayer']
|
| 364 |
+
|
| 365 |
+
def __init__(self, config: InternVisionConfig):
|
| 366 |
+
super().__init__(config)
|
| 367 |
+
self.config = config
|
| 368 |
+
|
| 369 |
+
self.embeddings = InternVisionEmbeddings(config)
|
| 370 |
+
self.encoder = InternVisionEncoder(config)
|
| 371 |
+
|
| 372 |
+
def resize_pos_embeddings(self, old_size, new_size, patch_size):
|
| 373 |
+
pos_emb = self.embeddings.position_embedding
|
| 374 |
+
_, num_positions, embed_dim = pos_emb.shape
|
| 375 |
+
cls_emb = pos_emb[:, :1, :]
|
| 376 |
+
pos_emb = pos_emb[:, 1:, :].reshape(1, old_size // patch_size, old_size // patch_size, -1).permute(0, 3, 1, 2)
|
| 377 |
+
pos_emb = F.interpolate(pos_emb.float(), size=new_size // patch_size, mode='bicubic', align_corners=False)
|
| 378 |
+
pos_emb = pos_emb.to(cls_emb.dtype).reshape(1, embed_dim, -1).permute(0, 2, 1)
|
| 379 |
+
pos_emb = torch.cat([cls_emb, pos_emb], dim=1)
|
| 380 |
+
self.embeddings.position_embedding = nn.Parameter(pos_emb)
|
| 381 |
+
self.embeddings.image_size = new_size
|
| 382 |
+
logger.info('Resized position embeddings from {} to {}'.format(old_size, new_size))
|
| 383 |
+
|
| 384 |
+
def get_input_embeddings(self):
|
| 385 |
+
return self.embeddings
|
| 386 |
+
|
| 387 |
+
def forward(
|
| 388 |
+
self,
|
| 389 |
+
pixel_values: Optional[torch.FloatTensor] = None,
|
| 390 |
+
output_hidden_states: Optional[bool] = None,
|
| 391 |
+
return_dict: Optional[bool] = None,
|
| 392 |
+
pixel_embeds: Optional[torch.FloatTensor] = None,
|
| 393 |
+
) -> Union[Tuple, BaseModelOutputWithPooling]:
|
| 394 |
+
output_hidden_states = (
|
| 395 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 396 |
+
)
|
| 397 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 398 |
+
|
| 399 |
+
if pixel_values is None and pixel_embeds is None:
|
| 400 |
+
raise ValueError('You have to specify pixel_values or pixel_embeds')
|
| 401 |
+
|
| 402 |
+
if pixel_embeds is not None:
|
| 403 |
+
hidden_states = pixel_embeds
|
| 404 |
+
else:
|
| 405 |
+
if len(pixel_values.shape) == 4:
|
| 406 |
+
hidden_states = self.embeddings(pixel_values)
|
| 407 |
+
else:
|
| 408 |
+
raise ValueError(f'wrong pixel_values size: {pixel_values.shape}')
|
| 409 |
+
encoder_outputs = self.encoder(
|
| 410 |
+
inputs_embeds=hidden_states,
|
| 411 |
+
output_hidden_states=output_hidden_states,
|
| 412 |
+
return_dict=return_dict,
|
| 413 |
+
)
|
| 414 |
+
last_hidden_state = encoder_outputs.last_hidden_state
|
| 415 |
+
pooled_output = last_hidden_state[:, 0, :]
|
| 416 |
+
|
| 417 |
+
if not return_dict:
|
| 418 |
+
return (last_hidden_state, pooled_output) + encoder_outputs[1:]
|
| 419 |
+
|
| 420 |
+
return BaseModelOutputWithPooling(
|
| 421 |
+
last_hidden_state=last_hidden_state,
|
| 422 |
+
pooler_output=pooled_output,
|
| 423 |
+
hidden_states=encoder_outputs.hidden_states,
|
| 424 |
+
attentions=encoder_outputs.attentions,
|
| 425 |
+
)
|
llm-awq/tinychat/models/internvl/media.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import numpy as np
|
| 3 |
+
import torch
|
| 4 |
+
import torchvision.transforms as T
|
| 5 |
+
from decord import VideoReader, cpu
|
| 6 |
+
from PIL import Image
|
| 7 |
+
from torchvision.transforms.functional import InterpolationMode
|
| 8 |
+
|
| 9 |
+
IMAGENET_MEAN = (0.485, 0.456, 0.406)
|
| 10 |
+
IMAGENET_STD = (0.229, 0.224, 0.225)
|
| 11 |
+
|
| 12 |
+
def build_transform(input_size):
|
| 13 |
+
MEAN, STD = IMAGENET_MEAN, IMAGENET_STD
|
| 14 |
+
transform = T.Compose([
|
| 15 |
+
T.Lambda(lambda img: img.convert('RGB') if img.mode != 'RGB' else img),
|
| 16 |
+
T.Resize((input_size, input_size), interpolation=InterpolationMode.BICUBIC),
|
| 17 |
+
T.ToTensor(),
|
| 18 |
+
T.Normalize(mean=MEAN, std=STD)
|
| 19 |
+
])
|
| 20 |
+
return transform
|
| 21 |
+
|
| 22 |
+
def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size):
|
| 23 |
+
best_ratio_diff = float('inf')
|
| 24 |
+
best_ratio = (1, 1)
|
| 25 |
+
area = width * height
|
| 26 |
+
for ratio in target_ratios:
|
| 27 |
+
target_aspect_ratio = ratio[0] / ratio[1]
|
| 28 |
+
ratio_diff = abs(aspect_ratio - target_aspect_ratio)
|
| 29 |
+
if ratio_diff < best_ratio_diff:
|
| 30 |
+
best_ratio_diff = ratio_diff
|
| 31 |
+
best_ratio = ratio
|
| 32 |
+
elif ratio_diff == best_ratio_diff:
|
| 33 |
+
if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:
|
| 34 |
+
best_ratio = ratio
|
| 35 |
+
return best_ratio
|
| 36 |
+
|
| 37 |
+
def dynamic_preprocess(image, min_num=1, max_num=12, image_size=448, use_thumbnail=False):
|
| 38 |
+
orig_width, orig_height = image.size
|
| 39 |
+
aspect_ratio = orig_width / orig_height
|
| 40 |
+
|
| 41 |
+
# calculate the existing image aspect ratio
|
| 42 |
+
target_ratios = set(
|
| 43 |
+
(i, j) for n in range(min_num, max_num + 1) for i in range(1, n + 1) for j in range(1, n + 1) if
|
| 44 |
+
i * j <= max_num and i * j >= min_num)
|
| 45 |
+
target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])
|
| 46 |
+
|
| 47 |
+
# find the closest aspect ratio to the target
|
| 48 |
+
target_aspect_ratio = find_closest_aspect_ratio(
|
| 49 |
+
aspect_ratio, target_ratios, orig_width, orig_height, image_size)
|
| 50 |
+
|
| 51 |
+
# calculate the target width and height
|
| 52 |
+
target_width = image_size * target_aspect_ratio[0]
|
| 53 |
+
target_height = image_size * target_aspect_ratio[1]
|
| 54 |
+
blocks = target_aspect_ratio[0] * target_aspect_ratio[1]
|
| 55 |
+
|
| 56 |
+
# resize the image
|
| 57 |
+
resized_img = image.resize((target_width, target_height))
|
| 58 |
+
processed_images = []
|
| 59 |
+
for i in range(blocks):
|
| 60 |
+
box = (
|
| 61 |
+
(i % (target_width // image_size)) * image_size,
|
| 62 |
+
(i // (target_width // image_size)) * image_size,
|
| 63 |
+
((i % (target_width // image_size)) + 1) * image_size,
|
| 64 |
+
((i // (target_width // image_size)) + 1) * image_size
|
| 65 |
+
)
|
| 66 |
+
# split the image
|
| 67 |
+
split_img = resized_img.crop(box)
|
| 68 |
+
processed_images.append(split_img)
|
| 69 |
+
assert len(processed_images) == blocks
|
| 70 |
+
if use_thumbnail and len(processed_images) != 1:
|
| 71 |
+
thumbnail_img = image.resize((image_size, image_size))
|
| 72 |
+
processed_images.append(thumbnail_img)
|
| 73 |
+
return processed_images
|
| 74 |
+
|
| 75 |
+
def load_image(image_file, input_size=448, max_num=12):
|
| 76 |
+
image = Image.open(image_file).convert('RGB')
|
| 77 |
+
transform = build_transform(input_size=input_size)
|
| 78 |
+
images = dynamic_preprocess(image, image_size=input_size, use_thumbnail=True, max_num=max_num)
|
| 79 |
+
pixel_values = [transform(image) for image in images]
|
| 80 |
+
pixel_values = torch.stack(pixel_values)
|
| 81 |
+
return pixel_values
|
| 82 |
+
|
| 83 |
+
def get_index(bound, fps, max_frame, first_idx=0, num_segments=32):
|
| 84 |
+
if bound:
|
| 85 |
+
start, end = bound[0], bound[1]
|
| 86 |
+
else:
|
| 87 |
+
start, end = -100000, 100000
|
| 88 |
+
start_idx = max(first_idx, round(start * fps))
|
| 89 |
+
end_idx = min(round(end * fps), max_frame)
|
| 90 |
+
seg_size = float(end_idx - start_idx) / num_segments
|
| 91 |
+
frame_indices = np.array([
|
| 92 |
+
int(start_idx + (seg_size / 2) + np.round(seg_size * idx))
|
| 93 |
+
for idx in range(num_segments)
|
| 94 |
+
])
|
| 95 |
+
return frame_indices
|
| 96 |
+
|
| 97 |
+
def load_video(video_path, bound=None, input_size=448, max_num=1, num_segments=32):
|
| 98 |
+
vr = VideoReader(video_path, ctx=cpu(0), num_threads=1)
|
| 99 |
+
max_frame = len(vr) - 1
|
| 100 |
+
fps = float(vr.get_avg_fps())
|
| 101 |
+
|
| 102 |
+
pixel_values_list, num_patches_list = [], []
|
| 103 |
+
transform = build_transform(input_size=input_size)
|
| 104 |
+
frame_indices = get_index(bound, fps, max_frame, first_idx=0, num_segments=num_segments)
|
| 105 |
+
for frame_index in frame_indices:
|
| 106 |
+
img = Image.fromarray(vr[frame_index].asnumpy()).convert('RGB')
|
| 107 |
+
img = dynamic_preprocess(img, image_size=input_size, use_thumbnail=True, max_num=max_num)
|
| 108 |
+
pixel_values = [transform(tile) for tile in img]
|
| 109 |
+
pixel_values = torch.stack(pixel_values)
|
| 110 |
+
num_patches_list.append(pixel_values.shape[0])
|
| 111 |
+
pixel_values_list.append(pixel_values)
|
| 112 |
+
|
| 113 |
+
return pixel_values_list, num_patches_list
|
llm-awq/tinychat/models/internvl3.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from collections import defaultdict, deque
|
| 3 |
+
from typing import Dict, List, Optional, Tuple, Union, Any
|
| 4 |
+
import warnings
|
| 5 |
+
from time import time
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.utils.checkpoint
|
| 9 |
+
from torch import nn
|
| 10 |
+
from torch.nn import CrossEntropyLoss
|
| 11 |
+
|
| 12 |
+
import transformers
|
| 13 |
+
from transformers import (AutoConfig,
|
| 14 |
+
AutoModel,
|
| 15 |
+
AutoTokenizer,
|
| 16 |
+
GenerationConfig,
|
| 17 |
+
PretrainedConfig,
|
| 18 |
+
PreTrainedModel)
|
| 19 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 20 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 21 |
+
from transformers.utils import ModelOutput, logging
|
| 22 |
+
from transformers import modeling_utils
|
| 23 |
+
|
| 24 |
+
from .internvl.configuration_internvl import InternVisionConfig, InternVLChatConfig
|
| 25 |
+
from .internvl.internvit import InternVisionModel
|
| 26 |
+
from .internvl.conversation import get_conv_template
|
| 27 |
+
from .internvl.media import load_image, load_video
|
| 28 |
+
|
| 29 |
+
from llava.media import Image, Video
|
| 30 |
+
|
| 31 |
+
from .qwen2 import Qwen2ForCausalLM
|
| 32 |
+
from .llama import LlamaForCausalLM
|
| 33 |
+
|
| 34 |
+
try:
|
| 35 |
+
import flash_attn
|
| 36 |
+
has_flash_attn = True
|
| 37 |
+
except ImportError:
|
| 38 |
+
print('FlashAttention2 is not installed.')
|
| 39 |
+
has_flash_attn = False
|
| 40 |
+
|
| 41 |
+
def skip(*args, **kwargs):
|
| 42 |
+
pass
|
| 43 |
+
|
| 44 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 45 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 46 |
+
torch.nn.init.uniform_ = skip
|
| 47 |
+
torch.nn.init.normal_ = skip
|
| 48 |
+
|
| 49 |
+
modeling_utils._init_weights = False
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
logger = logging.get_logger(__name__)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
class InternVL3(PreTrainedModel):
|
| 56 |
+
config_class = InternVLChatConfig
|
| 57 |
+
main_input_name = 'pixel_values'
|
| 58 |
+
base_model_prefix = 'language_model'
|
| 59 |
+
_supports_flash_attn_2 = True
|
| 60 |
+
supports_gradient_checkpointing = True
|
| 61 |
+
_no_split_modules = ['InternVisionModel', 'LlamaDecoderLayer', 'Qwen2DecoderLayer']
|
| 62 |
+
|
| 63 |
+
def __init__(self, config: InternVLChatConfig, vision_model=None, language_model=None, use_flash_attn=True):
|
| 64 |
+
super().__init__(config)
|
| 65 |
+
|
| 66 |
+
self.tokenizer = AutoTokenizer.from_pretrained(config.name_or_path, trust_remote_code=True, use_fast=False)
|
| 67 |
+
|
| 68 |
+
image_size = config.force_image_size or config.vision_config.image_size
|
| 69 |
+
patch_size = config.vision_config.patch_size
|
| 70 |
+
self.patch_size = patch_size
|
| 71 |
+
self.select_layer = config.select_layer
|
| 72 |
+
self.template = config.template
|
| 73 |
+
self.num_image_token = int((image_size // patch_size) ** 2 * (config.downsample_ratio ** 2))
|
| 74 |
+
self.downsample_ratio = config.downsample_ratio
|
| 75 |
+
self.ps_version = config.ps_version
|
| 76 |
+
use_flash_attn = use_flash_attn if has_flash_attn else False
|
| 77 |
+
config.vision_config.use_flash_attn = True if use_flash_attn else False
|
| 78 |
+
config.llm_config._attn_implementation = 'flash_attention_2' if use_flash_attn else 'eager'
|
| 79 |
+
|
| 80 |
+
logger.info(f'num_image_token: {self.num_image_token}')
|
| 81 |
+
logger.info(f'ps_version: {self.ps_version}')
|
| 82 |
+
if vision_model is not None:
|
| 83 |
+
self.vision_model = vision_model
|
| 84 |
+
else:
|
| 85 |
+
self.vision_model = InternVisionModel(config.vision_config)
|
| 86 |
+
if language_model is not None:
|
| 87 |
+
self.language_model = language_model
|
| 88 |
+
else:
|
| 89 |
+
if config.llm_config.architectures[0] == 'LlamaForCausalLM':
|
| 90 |
+
self.language_model = LlamaForCausalLM(config.llm_config)
|
| 91 |
+
elif config.llm_config.architectures[0] == 'Qwen2ForCausalLM':
|
| 92 |
+
self.language_model = Qwen2ForCausalLM(config.llm_config)
|
| 93 |
+
else:
|
| 94 |
+
raise NotImplementedError(f'{config.llm_config.architectures[0]} is not implemented.')
|
| 95 |
+
|
| 96 |
+
vit_hidden_size = config.vision_config.hidden_size
|
| 97 |
+
llm_hidden_size = config.llm_config.hidden_size
|
| 98 |
+
|
| 99 |
+
self.mlp1 = nn.Sequential(
|
| 100 |
+
nn.LayerNorm(vit_hidden_size * int(1 / self.downsample_ratio) ** 2),
|
| 101 |
+
nn.Linear(vit_hidden_size * int(1 / self.downsample_ratio) ** 2, llm_hidden_size),
|
| 102 |
+
nn.GELU(),
|
| 103 |
+
nn.Linear(llm_hidden_size, llm_hidden_size)
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
self.img_context_token_id = None
|
| 107 |
+
self.conv_template = get_conv_template(self.template)
|
| 108 |
+
self.system_message = self.conv_template.system_message
|
| 109 |
+
|
| 110 |
+
def freezed_module_patch(self):
|
| 111 |
+
self.vision_model.eval()
|
| 112 |
+
self.language_model.eval()
|
| 113 |
+
self.mlp1.eval()
|
| 114 |
+
|
| 115 |
+
def pixel_shuffle(self, x, scale_factor=0.5):
|
| 116 |
+
n, w, h, c = x.size()
|
| 117 |
+
# N, W, H, C --> N, W, H * scale, C // scale
|
| 118 |
+
x = x.view(n, w, int(h * scale_factor), int(c / scale_factor))
|
| 119 |
+
# N, W, H * scale, C // scale --> N, H * scale, W, C // scale
|
| 120 |
+
x = x.permute(0, 2, 1, 3).contiguous()
|
| 121 |
+
# N, H * scale, W, C // scale --> N, H * scale, W * scale, C // (scale ** 2)
|
| 122 |
+
x = x.view(n, int(h * scale_factor), int(w * scale_factor),
|
| 123 |
+
int(c / (scale_factor * scale_factor)))
|
| 124 |
+
if self.ps_version == 'v1':
|
| 125 |
+
warnings.warn("In ps_version 'v1', the height and width have not been swapped back, "
|
| 126 |
+
'which results in a transposed image.')
|
| 127 |
+
else:
|
| 128 |
+
x = x.permute(0, 2, 1, 3).contiguous()
|
| 129 |
+
return x
|
| 130 |
+
|
| 131 |
+
@torch.inference_mode()
|
| 132 |
+
def prepare_media(self, conversation):
|
| 133 |
+
prompt = conversation[0]["value"]
|
| 134 |
+
media = {"image": [], "video": []}
|
| 135 |
+
for item in prompt:
|
| 136 |
+
if isinstance(item, Image):
|
| 137 |
+
media["image"].append(load_image(item.path))
|
| 138 |
+
if isinstance(item, Video):
|
| 139 |
+
pixel_values, num_patches_list = load_video(item.path)
|
| 140 |
+
media["video"].extend(pixel_values)
|
| 141 |
+
|
| 142 |
+
return media, num_patches_list if media["video"] else None
|
| 143 |
+
|
| 144 |
+
@torch.inference_mode()
|
| 145 |
+
def extract_features(self, pixel_values):
|
| 146 |
+
if self.select_layer == -1:
|
| 147 |
+
vit_embeds = self.vision_model(
|
| 148 |
+
pixel_values=pixel_values,
|
| 149 |
+
output_hidden_states=False,
|
| 150 |
+
return_dict=True).last_hidden_state
|
| 151 |
+
else:
|
| 152 |
+
vit_embeds = self.vision_model(
|
| 153 |
+
pixel_values=pixel_values,
|
| 154 |
+
output_hidden_states=True,
|
| 155 |
+
return_dict=True).hidden_states[self.select_layer]
|
| 156 |
+
vit_embeds = vit_embeds[:, 1:, :]
|
| 157 |
+
|
| 158 |
+
h = w = int(vit_embeds.shape[1] ** 0.5)
|
| 159 |
+
vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], h, w, -1)
|
| 160 |
+
vit_embeds = self.pixel_shuffle(vit_embeds, scale_factor=self.downsample_ratio)
|
| 161 |
+
vit_embeds = vit_embeds.reshape(vit_embeds.shape[0], -1, vit_embeds.shape[-1])
|
| 162 |
+
vit_embeds = self.mlp1(vit_embeds)
|
| 163 |
+
return vit_embeds
|
| 164 |
+
|
| 165 |
+
@torch.inference_mode()
|
| 166 |
+
def _embed(
|
| 167 |
+
self,
|
| 168 |
+
input_ids: torch.Tensor,
|
| 169 |
+
media: Dict[str, List[torch.Tensor]],
|
| 170 |
+
media_config: Dict[str, Dict[str, Any]],
|
| 171 |
+
labels: Optional[torch.Tensor],
|
| 172 |
+
attention_mask: Optional[torch.Tensor],
|
| 173 |
+
):
|
| 174 |
+
attention_mask = (
|
| 175 |
+
attention_mask
|
| 176 |
+
if attention_mask is not None
|
| 177 |
+
else torch.ones_like(input_ids, dtype=torch.bool)
|
| 178 |
+
)
|
| 179 |
+
|
| 180 |
+
if media["image"]:
|
| 181 |
+
pixel_values = torch.cat(media["image"], dim=0).half().cuda()
|
| 182 |
+
elif media["video"]:
|
| 183 |
+
pixel_values = torch.cat(media["video"], dim=0).half().cuda()
|
| 184 |
+
|
| 185 |
+
vit_embeds = self.extract_features(pixel_values)
|
| 186 |
+
|
| 187 |
+
input_embeds = self.language_model.get_input_embeddings()(input_ids)
|
| 188 |
+
B, N, C = input_embeds.shape
|
| 189 |
+
input_embeds = input_embeds.reshape(B * N, C)
|
| 190 |
+
|
| 191 |
+
input_ids = input_ids.reshape(B * N)
|
| 192 |
+
selected = (input_ids == self.img_context_token_id)
|
| 193 |
+
|
| 194 |
+
input_embeds[selected] = input_embeds[selected] * 0.0 + vit_embeds.reshape(-1, C)
|
| 195 |
+
|
| 196 |
+
input_embeds = input_embeds.reshape(B, N, C)
|
| 197 |
+
|
| 198 |
+
return input_embeds, None, attention_mask
|
| 199 |
+
|
| 200 |
+
@torch.inference_mode()
|
| 201 |
+
def benchmark(self, prompt: Union[str, List], quant_llm) -> None:
|
| 202 |
+
media = {"image": [], "video": []}
|
| 203 |
+
question = ""
|
| 204 |
+
for item in prompt:
|
| 205 |
+
if isinstance(item, str):
|
| 206 |
+
question += item
|
| 207 |
+
if isinstance(item, Image):
|
| 208 |
+
media["image"].append(load_image(item.path))
|
| 209 |
+
if isinstance(item, Video):
|
| 210 |
+
pixel_values, num_patches_list = load_video(item.path)
|
| 211 |
+
media["video"].extend(pixel_values)
|
| 212 |
+
|
| 213 |
+
if media["image"]:
|
| 214 |
+
num_patches_list = [image.size(0) for image in media["image"]]
|
| 215 |
+
|
| 216 |
+
if media["image"] and '<image>' not in question:
|
| 217 |
+
question = '<image>\n' + question
|
| 218 |
+
|
| 219 |
+
if media["video"] and '<image>' not in question:
|
| 220 |
+
video_prefix = ''.join([f'Frame{i+1}: <image>\n' for i in range(len(num_patches_list))])
|
| 221 |
+
question = video_prefix + question
|
| 222 |
+
|
| 223 |
+
template = get_conv_template(self.template)
|
| 224 |
+
template.system_message = self.system_message
|
| 225 |
+
eos_token_id = self.tokenizer.convert_tokens_to_ids(template.sep.strip())
|
| 226 |
+
|
| 227 |
+
template.append_message(template.roles[0], question)
|
| 228 |
+
template.append_message(template.roles[1], None)
|
| 229 |
+
query = template.get_prompt()
|
| 230 |
+
|
| 231 |
+
IMG_START_TOKEN = '<img>'
|
| 232 |
+
IMG_END_TOKEN = '</img>'
|
| 233 |
+
IMG_CONTEXT_TOKEN = '<IMG_CONTEXT>'
|
| 234 |
+
|
| 235 |
+
img_context_token_id = self.tokenizer.convert_tokens_to_ids(IMG_CONTEXT_TOKEN)
|
| 236 |
+
self.img_context_token_id = img_context_token_id
|
| 237 |
+
|
| 238 |
+
for num_patches in num_patches_list:
|
| 239 |
+
image_tokens = IMG_START_TOKEN + IMG_CONTEXT_TOKEN * self.num_image_token * num_patches + IMG_END_TOKEN
|
| 240 |
+
query = query.replace('<image>', image_tokens, 1)
|
| 241 |
+
|
| 242 |
+
model_inputs = self.tokenizer(query, return_tensors='pt')
|
| 243 |
+
input_ids = model_inputs['input_ids'].to(self.device)
|
| 244 |
+
attention_mask = model_inputs['attention_mask'].to(self.device)
|
| 245 |
+
|
| 246 |
+
for i in range(10):
|
| 247 |
+
torch.cuda.synchronize()
|
| 248 |
+
t_st = time()
|
| 249 |
+
inputs_embeds, _, attention_mask = self._embed(
|
| 250 |
+
input_ids=input_ids,
|
| 251 |
+
media=media,
|
| 252 |
+
media_config=None,
|
| 253 |
+
labels=None,
|
| 254 |
+
attention_mask=attention_mask
|
| 255 |
+
)
|
| 256 |
+
torch.cuda.synchronize()
|
| 257 |
+
t_ed = time()
|
| 258 |
+
torch.cuda.empty_cache()
|
| 259 |
+
|
| 260 |
+
if media["image"]:
|
| 261 |
+
print(
|
| 262 |
+
"Time of vision tower and others is {:.5f} s for {} images ({} x {} x {})".format(
|
| 263 |
+
t_ed - t_st, sum(num_patches_list), media["image"][0].shape[1], media["image"][0].shape[2], media["image"][0].shape[3]
|
| 264 |
+
)
|
| 265 |
+
)
|
| 266 |
+
elif media["video"]:
|
| 267 |
+
print(
|
| 268 |
+
"Time of vision tower and others is {:.5f} s for {} video frames ({} x {} x {})".format(
|
| 269 |
+
t_ed - t_st, sum(num_patches_list), media["video"][0].shape[1], media["video"][0].shape[2], media["video"][0].shape[3]
|
| 270 |
+
)
|
| 271 |
+
)
|
| 272 |
+
output = self.language_model.benchmark(
|
| 273 |
+
inputs_embeds=inputs_embeds,
|
| 274 |
+
attention_mask=attention_mask,
|
| 275 |
+
quant_llm=quant_llm
|
| 276 |
+
)
|
| 277 |
+
response = self.tokenizer.decode(output[0], skip_special_tokens=True).strip()
|
| 278 |
+
|
| 279 |
+
return response
|
| 280 |
+
|
| 281 |
+
@torch.inference_mode()
|
| 282 |
+
def stream_gen(
|
| 283 |
+
self,
|
| 284 |
+
input_ids,
|
| 285 |
+
media,
|
| 286 |
+
media_cfg,
|
| 287 |
+
start_pos,
|
| 288 |
+
chunk_prefilling,
|
| 289 |
+
quant_llm,
|
| 290 |
+
attention_mask=None,
|
| 291 |
+
) -> str:
|
| 292 |
+
if media is None:
|
| 293 |
+
inputs_embeds = self.language_model.get_input_embeddings()(input_ids ).clone()
|
| 294 |
+
else:
|
| 295 |
+
inputs_embeds, _, _ = self._embed(input_ids, media, None, None, attention_mask)
|
| 296 |
+
|
| 297 |
+
length = inputs_embeds.shape[1]
|
| 298 |
+
if quant_llm:
|
| 299 |
+
out = self.language_model(None, start_pos, inputs_embeds, chunk_prefilling)
|
| 300 |
+
else:
|
| 301 |
+
out = self.language_model.forwardfp16(None, start_pos, inputs_embeds, chunk_prefilling)
|
| 302 |
+
return out, length
|
| 303 |
+
|
| 304 |
+
@torch.inference_mode()
|
| 305 |
+
def forward(
|
| 306 |
+
self,
|
| 307 |
+
pixel_values: torch.FloatTensor,
|
| 308 |
+
input_ids: torch.LongTensor = None,
|
| 309 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 310 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 311 |
+
image_flags: Optional[torch.LongTensor] = None,
|
| 312 |
+
past_key_values: Optional[List[torch.FloatTensor]] = None,
|
| 313 |
+
labels: Optional[torch.LongTensor] = None,
|
| 314 |
+
use_cache: Optional[bool] = None,
|
| 315 |
+
output_attentions: Optional[bool] = None,
|
| 316 |
+
output_hidden_states: Optional[bool] = None,
|
| 317 |
+
return_dict: Optional[bool] = None,
|
| 318 |
+
) -> Union[Tuple, CausalLMOutputWithPast]:
|
| 319 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 320 |
+
|
| 321 |
+
image_flags = image_flags.squeeze(-1)
|
| 322 |
+
input_embeds = self.language_model.get_input_embeddings()(input_ids).clone()
|
| 323 |
+
|
| 324 |
+
vit_embeds = self.extract_feature(pixel_values)
|
| 325 |
+
vit_embeds = vit_embeds[image_flags == 1]
|
| 326 |
+
vit_batch_size = pixel_values.shape[0]
|
| 327 |
+
|
| 328 |
+
B, N, C = input_embeds.shape
|
| 329 |
+
input_embeds = input_embeds.reshape(B * N, C)
|
| 330 |
+
|
| 331 |
+
if torch.distributed.is_initialized() and torch.distributed.get_rank() == 0:
|
| 332 |
+
print(f'dynamic ViT batch size: {vit_batch_size}, images per sample: {vit_batch_size / B}, dynamic token length: {N}')
|
| 333 |
+
|
| 334 |
+
input_ids = input_ids.reshape(B * N)
|
| 335 |
+
selected = (input_ids == self.img_context_token_id)
|
| 336 |
+
try:
|
| 337 |
+
input_embeds[selected] = input_embeds[selected] * 0.0 + vit_embeds.reshape(-1, C)
|
| 338 |
+
except Exception as e:
|
| 339 |
+
vit_embeds = vit_embeds.reshape(-1, C)
|
| 340 |
+
print(f'warning: {e}, input_embeds[selected].shape={input_embeds[selected].shape}, '
|
| 341 |
+
f'vit_embeds.shape={vit_embeds.shape}')
|
| 342 |
+
n_token = min(selected.sum(), vit_embeds.size(0))
|
| 343 |
+
input_embeds[selected][:n_token] = input_embeds[selected][:n_token] * 0.0 + vit_embeds[:n_token]
|
| 344 |
+
|
| 345 |
+
input_embeds = input_embeds.reshape(B, N, C)
|
| 346 |
+
|
| 347 |
+
outputs = self.language_model(
|
| 348 |
+
inputs_embeds=input_embeds,
|
| 349 |
+
attention_mask=attention_mask,
|
| 350 |
+
position_ids=position_ids,
|
| 351 |
+
past_key_values=past_key_values,
|
| 352 |
+
use_cache=use_cache,
|
| 353 |
+
output_attentions=output_attentions,
|
| 354 |
+
output_hidden_states=output_hidden_states,
|
| 355 |
+
return_dict=return_dict,
|
| 356 |
+
)
|
| 357 |
+
logits = outputs.logits
|
| 358 |
+
|
| 359 |
+
loss = None
|
| 360 |
+
if labels is not None:
|
| 361 |
+
# Shift so that tokens < n predict n
|
| 362 |
+
shift_logits = logits[..., :-1, :].contiguous()
|
| 363 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 364 |
+
# Flatten the tokens
|
| 365 |
+
loss_fct = CrossEntropyLoss()
|
| 366 |
+
shift_logits = shift_logits.view(-1, self.language_model.config.vocab_size)
|
| 367 |
+
shift_labels = shift_labels.view(-1)
|
| 368 |
+
# Enable model parallelism
|
| 369 |
+
shift_labels = shift_labels.to(shift_logits.device)
|
| 370 |
+
loss = loss_fct(shift_logits, shift_labels)
|
| 371 |
+
|
| 372 |
+
if not return_dict:
|
| 373 |
+
output = (logits,) + outputs[1:]
|
| 374 |
+
return (loss,) + output if loss is not None else output
|
| 375 |
+
|
| 376 |
+
return CausalLMOutputWithPast(
|
| 377 |
+
loss=loss,
|
| 378 |
+
logits=logits,
|
| 379 |
+
past_key_values=outputs.past_key_values,
|
| 380 |
+
hidden_states=outputs.hidden_states,
|
| 381 |
+
attentions=outputs.attentions,
|
| 382 |
+
)
|
| 383 |
+
|
llm-awq/tinychat/models/llama.py
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
# This software may be used and distributed according to the terms of the GNU General Public License version 3.
|
| 3 |
+
|
| 4 |
+
from typing import Optional, Tuple
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from torch import nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import awq_inference_engine
|
| 12 |
+
from transformers.models.llama.modeling_llama import LlamaRotaryEmbedding
|
| 13 |
+
|
| 14 |
+
# from flash_attn.flash_attn_interface import flash_attn_unpadded_func
|
| 15 |
+
|
| 16 |
+
import tinychat.utils.constants
|
| 17 |
+
|
| 18 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 19 |
+
multiple_of = tinychat.utils.constants.llama_multiple_of
|
| 20 |
+
max_seq_len = tinychat.utils.constants.max_seq_len
|
| 21 |
+
from flash_attn import flash_attn_func
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class RMSNorm(torch.nn.Module):
|
| 25 |
+
def __init__(self, dim: int, eps: float = 1e-6):
|
| 26 |
+
super().__init__()
|
| 27 |
+
self.eps = eps
|
| 28 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 29 |
+
|
| 30 |
+
def _norm(self, x):
|
| 31 |
+
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
|
| 32 |
+
|
| 33 |
+
def forward(self, x):
|
| 34 |
+
output = torch.empty_like(x)
|
| 35 |
+
awq_inference_engine.layernorm_forward_cuda(x, self.weight, output, self.eps)
|
| 36 |
+
return output
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def precompute_freqs_cis(
|
| 40 |
+
dim: int, end: int, theta: float = 10000.0, scale: float = 1.0
|
| 41 |
+
):
|
| 42 |
+
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
|
| 43 |
+
t = torch.arange(end, device=freqs.device) # type: ignore
|
| 44 |
+
freqs = torch.outer(t * scale, freqs).float() # type: ignore
|
| 45 |
+
|
| 46 |
+
freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # complex64
|
| 47 |
+
return freqs_cis
|
| 48 |
+
|
| 49 |
+
|
| 50 |
+
def precompute_freqs(
|
| 51 |
+
dim: int, end: int, theta: float = 10000.0, scale: float = 1.0, device=None
|
| 52 |
+
):
|
| 53 |
+
inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2).float().to(device) / dim))
|
| 54 |
+
seq = torch.arange(end, dtype=inv_freq.dtype, device=device)
|
| 55 |
+
freqs = torch.einsum("i , j -> i j", seq, inv_freq)
|
| 56 |
+
freqs = freqs.reshape(freqs.shape[0], 1, 1, -1)
|
| 57 |
+
return torch.cat((freqs, freqs), dim=-1)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
|
| 61 |
+
ndim = x.ndim
|
| 62 |
+
assert 0 <= 1 < ndim
|
| 63 |
+
assert freqs_cis.shape == (x.shape[1], x.shape[-1])
|
| 64 |
+
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 65 |
+
return freqs_cis.view(*shape)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def apply_rotary_emb(
|
| 69 |
+
xq: torch.Tensor,
|
| 70 |
+
xk: torch.Tensor,
|
| 71 |
+
freqs_cis: torch.Tensor,
|
| 72 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 73 |
+
# xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
|
| 74 |
+
# k_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
|
| 75 |
+
xq_ = torch.view_as_complex(
|
| 76 |
+
xq.float().reshape(*xq.shape[:-1], 2, -1).transpose(-2, -1).contiguous()
|
| 77 |
+
)
|
| 78 |
+
xk_ = torch.view_as_complex(
|
| 79 |
+
xk.float().reshape(*xk.shape[:-1], 2, -1).transpose(-2, -1).contiguous()
|
| 80 |
+
)
|
| 81 |
+
freqs_cis = reshape_for_broadcast(freqs_cis, xq_)
|
| 82 |
+
xq_out = torch.view_as_real(xq_ * freqs_cis).transpose(-2, -1).flatten(3)
|
| 83 |
+
xk_out = torch.view_as_real(xk_ * freqs_cis).transpose(-2, -1).flatten(3)
|
| 84 |
+
return xq_out.type_as(xq), xk_out.type_as(xk)
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
class LlamaAttentionFused(nn.Module):
|
| 88 |
+
def __init__(self, args):
|
| 89 |
+
super().__init__()
|
| 90 |
+
self.args = args
|
| 91 |
+
self.n_local_heads = args.num_attention_heads
|
| 92 |
+
self.hidden_size = args.hidden_size
|
| 93 |
+
self.num_heads = args.num_attention_heads
|
| 94 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 95 |
+
|
| 96 |
+
self.num_key_value_heads = args.num_key_value_heads
|
| 97 |
+
self.num_key_value_groups = self.num_heads // self.num_key_value_heads
|
| 98 |
+
self.max_position_embeddings = args.max_position_embeddings
|
| 99 |
+
self.rope_theta = args.rope_theta
|
| 100 |
+
self.rope_scaling = args.rope_scaling
|
| 101 |
+
if self.rope_scaling is None:
|
| 102 |
+
self.rope_scaling = 1.0
|
| 103 |
+
else:
|
| 104 |
+
self.rope_scaling = 1.0 / self.rope_scaling["factor"]
|
| 105 |
+
self.kv_max_seq_len = min(max_seq_len, self.max_position_embeddings)
|
| 106 |
+
self.q_proj = nn.Linear(
|
| 107 |
+
self.hidden_size,
|
| 108 |
+
self.num_heads * self.head_dim,
|
| 109 |
+
bias=False,
|
| 110 |
+
)
|
| 111 |
+
self.k_proj = nn.Linear(
|
| 112 |
+
self.hidden_size,
|
| 113 |
+
self.num_key_value_heads * self.head_dim,
|
| 114 |
+
bias=False,
|
| 115 |
+
)
|
| 116 |
+
self.v_proj = nn.Linear(
|
| 117 |
+
self.hidden_size,
|
| 118 |
+
self.num_key_value_heads * self.head_dim,
|
| 119 |
+
bias=False,
|
| 120 |
+
)
|
| 121 |
+
self.o_proj = nn.Linear(
|
| 122 |
+
self.num_heads * self.head_dim,
|
| 123 |
+
self.hidden_size,
|
| 124 |
+
bias=False,
|
| 125 |
+
)
|
| 126 |
+
|
| 127 |
+
# following fastertransformer definition
|
| 128 |
+
self.cache_v = (
|
| 129 |
+
torch.zeros(
|
| 130 |
+
(
|
| 131 |
+
max_batch_size,
|
| 132 |
+
self.num_key_value_heads,
|
| 133 |
+
# args.max_position_embeddings,
|
| 134 |
+
self.kv_max_seq_len,
|
| 135 |
+
self.head_dim,
|
| 136 |
+
)
|
| 137 |
+
)
|
| 138 |
+
.cuda()
|
| 139 |
+
.half()
|
| 140 |
+
) # added to half
|
| 141 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 142 |
+
self.cache_k = (
|
| 143 |
+
torch.zeros(
|
| 144 |
+
(
|
| 145 |
+
max_batch_size,
|
| 146 |
+
self.num_key_value_heads,
|
| 147 |
+
self.head_dim // 8,
|
| 148 |
+
# args.max_position_embeddings,
|
| 149 |
+
self.kv_max_seq_len,
|
| 150 |
+
8,
|
| 151 |
+
)
|
| 152 |
+
)
|
| 153 |
+
.cuda()
|
| 154 |
+
.half()
|
| 155 |
+
) # added to half
|
| 156 |
+
# dummy
|
| 157 |
+
self.rotary_emb = LlamaRotaryEmbedding(
|
| 158 |
+
self.head_dim, max_position_embeddings=2048, device="cuda:0"
|
| 159 |
+
)
|
| 160 |
+
|
| 161 |
+
def forward(
|
| 162 |
+
self,
|
| 163 |
+
x: torch.Tensor,
|
| 164 |
+
start_pos: int,
|
| 165 |
+
freqs_cis: torch.Tensor,
|
| 166 |
+
mask: Optional[torch.Tensor],
|
| 167 |
+
chunk_prefilling: bool,
|
| 168 |
+
):
|
| 169 |
+
bsz, seqlen, _ = x.shape
|
| 170 |
+
# xqkv = self.qkv_proj(x)
|
| 171 |
+
# xqkv = xqkv.view(bsz, seqlen, -1, self.n_local_heads, self.head_dim)
|
| 172 |
+
# xq = xqkv[:, :, 0]
|
| 173 |
+
# xk = xqkv[:, :, 1]
|
| 174 |
+
# xv = xqkv[:, :, 2]
|
| 175 |
+
|
| 176 |
+
xq, xk, xv = self.q_proj(x), self.k_proj(x), self.v_proj(x)
|
| 177 |
+
|
| 178 |
+
if seqlen > 1:
|
| 179 |
+
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 180 |
+
xk = xk.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 181 |
+
xv = xv.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 182 |
+
|
| 183 |
+
xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs_cis)
|
| 184 |
+
|
| 185 |
+
self.cache_k = self.cache_k.to(xq)
|
| 186 |
+
self.cache_v = self.cache_v.to(xq)
|
| 187 |
+
|
| 188 |
+
values_store = xv.transpose(2, 1)
|
| 189 |
+
keys_store = (
|
| 190 |
+
xk.reshape(bsz, seqlen, self.num_key_value_heads, self.head_dim // 8, 8)
|
| 191 |
+
.permute(0, 2, 3, 1, 4)
|
| 192 |
+
.contiguous()
|
| 193 |
+
)
|
| 194 |
+
|
| 195 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 196 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 197 |
+
|
| 198 |
+
if chunk_prefilling:
|
| 199 |
+
keys = self.cache_k[:, :, :, 0 : start_pos + seqlen, :]
|
| 200 |
+
keys = (
|
| 201 |
+
keys.permute(0, 3, 1, 2, 4)
|
| 202 |
+
.reshape(
|
| 203 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 204 |
+
)
|
| 205 |
+
.contiguous()
|
| 206 |
+
)
|
| 207 |
+
values = self.cache_v[:, :, 0 : start_pos + seqlen, :]
|
| 208 |
+
values = (
|
| 209 |
+
values.transpose(2, 1)
|
| 210 |
+
.reshape(
|
| 211 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 212 |
+
)
|
| 213 |
+
.contiguous()
|
| 214 |
+
)
|
| 215 |
+
else:
|
| 216 |
+
keys = xk
|
| 217 |
+
values = xv
|
| 218 |
+
output = flash_attn_func(
|
| 219 |
+
q=xq,
|
| 220 |
+
k=keys,
|
| 221 |
+
v=values,
|
| 222 |
+
causal=True,
|
| 223 |
+
)
|
| 224 |
+
output = output.contiguous().view(bsz, seqlen, -1)
|
| 225 |
+
else:
|
| 226 |
+
xq = xq.view(bsz, self.n_local_heads, self.head_dim)
|
| 227 |
+
xk = xk.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 228 |
+
xv = xv.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 229 |
+
|
| 230 |
+
output = awq_inference_engine.single_query_attention(
|
| 231 |
+
xq,
|
| 232 |
+
xk,
|
| 233 |
+
xv,
|
| 234 |
+
self.cache_k,
|
| 235 |
+
self.cache_v,
|
| 236 |
+
None,
|
| 237 |
+
# alibi position encodings
|
| 238 |
+
None,
|
| 239 |
+
start_pos,
|
| 240 |
+
self.head_dim,
|
| 241 |
+
self.rope_theta,
|
| 242 |
+
self.rope_scaling,
|
| 243 |
+
True,
|
| 244 |
+
)
|
| 245 |
+
output = output.reshape(bsz, 1, -1)
|
| 246 |
+
|
| 247 |
+
return self.o_proj(output)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
class LlamaMLP(nn.Module):
|
| 251 |
+
def __init__(self, args):
|
| 252 |
+
super().__init__()
|
| 253 |
+
self.hidden_size = args.hidden_size
|
| 254 |
+
self.intermediate_size = args.intermediate_size
|
| 255 |
+
|
| 256 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 257 |
+
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 258 |
+
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
| 259 |
+
|
| 260 |
+
def forward(self, x):
|
| 261 |
+
return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
class TransformerBlock(nn.Module):
|
| 265 |
+
def __init__(self, layer_id: int, args):
|
| 266 |
+
super().__init__()
|
| 267 |
+
self.n_heads = args.num_attention_heads
|
| 268 |
+
self.dim = args.hidden_size
|
| 269 |
+
self.head_dim = args.hidden_size // args.num_attention_heads
|
| 270 |
+
self.self_attn = LlamaAttentionFused(args)
|
| 271 |
+
self.mlp = LlamaMLP(args)
|
| 272 |
+
self.layer_id = layer_id
|
| 273 |
+
self.input_layernorm = RMSNorm(args.hidden_size, eps=args.rms_norm_eps)
|
| 274 |
+
self.post_attention_layernorm = RMSNorm(args.hidden_size, eps=args.rms_norm_eps)
|
| 275 |
+
|
| 276 |
+
def forward(
|
| 277 |
+
self,
|
| 278 |
+
x: torch.Tensor,
|
| 279 |
+
start_pos: int,
|
| 280 |
+
freqs_cis: torch.Tensor,
|
| 281 |
+
mask: Optional[torch.Tensor],
|
| 282 |
+
chunk_prefilling: bool,
|
| 283 |
+
):
|
| 284 |
+
h = x + self.self_attn.forward(
|
| 285 |
+
self.input_layernorm(x), start_pos, freqs_cis, mask, chunk_prefilling
|
| 286 |
+
)
|
| 287 |
+
out = h + self.mlp.forward(self.post_attention_layernorm(h))
|
| 288 |
+
return out
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
class Transformer(nn.Module):
|
| 292 |
+
def __init__(self, params):
|
| 293 |
+
super().__init__()
|
| 294 |
+
self.params = params
|
| 295 |
+
self.vocab_size = params.vocab_size
|
| 296 |
+
self.n_layers = params.num_hidden_layers
|
| 297 |
+
|
| 298 |
+
self.embed_tokens = nn.Embedding(params.vocab_size, params.hidden_size)
|
| 299 |
+
|
| 300 |
+
self.layers = torch.nn.ModuleList()
|
| 301 |
+
for layer_id in range(params.num_hidden_layers):
|
| 302 |
+
self.layers.append(TransformerBlock(layer_id, params))
|
| 303 |
+
|
| 304 |
+
self.norm = RMSNorm(params.hidden_size, eps=params.rms_norm_eps)
|
| 305 |
+
|
| 306 |
+
# Note (Haotian): rope_theta has to be defined here, otherwise context stage is wrong.
|
| 307 |
+
rope_scale = self.params.rope_scaling
|
| 308 |
+
if rope_scale is None:
|
| 309 |
+
rope_scale = 1.0
|
| 310 |
+
else:
|
| 311 |
+
rope_scale = 1.0 / rope_scale["factor"]
|
| 312 |
+
self.freqs = precompute_freqs(
|
| 313 |
+
self.params.hidden_size // self.params.num_attention_heads,
|
| 314 |
+
self.params.max_position_embeddings * 2,
|
| 315 |
+
self.params.rope_theta,
|
| 316 |
+
rope_scale,
|
| 317 |
+
)
|
| 318 |
+
self.freqs_cis = precompute_freqs_cis(
|
| 319 |
+
self.params.hidden_size // self.params.num_attention_heads,
|
| 320 |
+
self.params.max_position_embeddings * 2,
|
| 321 |
+
self.params.rope_theta,
|
| 322 |
+
rope_scale,
|
| 323 |
+
)
|
| 324 |
+
|
| 325 |
+
@torch.inference_mode()
|
| 326 |
+
def forward(
|
| 327 |
+
self,
|
| 328 |
+
tokens: torch.Tensor,
|
| 329 |
+
start_pos: int,
|
| 330 |
+
inputs_embeds: torch.Tensor = None,
|
| 331 |
+
chunk_prefilling: bool = False,
|
| 332 |
+
):
|
| 333 |
+
if tokens is not None:
|
| 334 |
+
_bsz, seqlen = tokens.shape
|
| 335 |
+
h = self.embed_tokens(tokens)
|
| 336 |
+
else:
|
| 337 |
+
h = inputs_embeds
|
| 338 |
+
seqlen = inputs_embeds.shape[1]
|
| 339 |
+
self.freqs = self.freqs.to(h.device)
|
| 340 |
+
freqs = self.freqs[start_pos : start_pos + seqlen]
|
| 341 |
+
|
| 342 |
+
mask = None
|
| 343 |
+
if seqlen > 1:
|
| 344 |
+
mask = torch.full((1, 1, seqlen, seqlen), float("-inf"), device=h.device)
|
| 345 |
+
mask = torch.triu(mask, diagonal=1).type_as(h)
|
| 346 |
+
if chunk_prefilling:
|
| 347 |
+
mask_history = torch.zeros(
|
| 348 |
+
(1, 1, seqlen, start_pos), dtype=torch.float16, device=h.device
|
| 349 |
+
).type_as(h)
|
| 350 |
+
mask = torch.cat((mask_history, mask), dim=-1)
|
| 351 |
+
for layer in self.layers:
|
| 352 |
+
h = layer(h, start_pos, freqs, mask, chunk_prefilling)
|
| 353 |
+
h = h[:, -1:, :] # Only the last token is useful
|
| 354 |
+
h = self.norm(h)
|
| 355 |
+
return h
|
| 356 |
+
|
| 357 |
+
@torch.inference_mode()
|
| 358 |
+
def forwardfp16(
|
| 359 |
+
self,
|
| 360 |
+
tokens: torch.Tensor,
|
| 361 |
+
start_pos: int,
|
| 362 |
+
inputs_embeds: torch.Tensor = None,
|
| 363 |
+
chunk_prefilling: bool = False,
|
| 364 |
+
):
|
| 365 |
+
if tokens is not None:
|
| 366 |
+
_bsz, seqlen = tokens.shape
|
| 367 |
+
h = self.embed_tokens(tokens)
|
| 368 |
+
else:
|
| 369 |
+
h = inputs_embeds
|
| 370 |
+
seqlen = inputs_embeds.shape[1]
|
| 371 |
+
self.freqs_cis = self.freqs_cis.to(h.device)
|
| 372 |
+
freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen]
|
| 373 |
+
|
| 374 |
+
mask = None
|
| 375 |
+
if seqlen > 1:
|
| 376 |
+
mask = torch.full((1, 1, seqlen, seqlen), float("-inf"), device=h.device)
|
| 377 |
+
mask = torch.triu(mask, diagonal=1).type_as(h)
|
| 378 |
+
if chunk_prefilling:
|
| 379 |
+
mask_history = torch.zeros(
|
| 380 |
+
(1, 1, seqlen, start_pos), dtype=torch.float16, device=h.device
|
| 381 |
+
).type_as(h)
|
| 382 |
+
mask = torch.cat((mask_history, mask), dim=-1)
|
| 383 |
+
for layer in self.layers:
|
| 384 |
+
h = layer(h, start_pos, freqs_cis, mask, chunk_prefilling)
|
| 385 |
+
h = h[:, -1:, :] # Only the last token is useful
|
| 386 |
+
h = self.norm(h)
|
| 387 |
+
return h
|
| 388 |
+
|
| 389 |
+
|
| 390 |
+
class LlamaForCausalLM(nn.Module):
|
| 391 |
+
def __init__(self, params):
|
| 392 |
+
super().__init__()
|
| 393 |
+
self.config = params
|
| 394 |
+
self.model = Transformer(params)
|
| 395 |
+
self.lm_head = nn.Linear(params.hidden_size, params.vocab_size, bias=False)
|
| 396 |
+
|
| 397 |
+
@torch.inference_mode()
|
| 398 |
+
def forward(
|
| 399 |
+
self,
|
| 400 |
+
tokens: torch.Tensor,
|
| 401 |
+
start_pos: int,
|
| 402 |
+
inputs_embeds: torch.Tensor = None,
|
| 403 |
+
chunk_prefilling=False,
|
| 404 |
+
quant=True,
|
| 405 |
+
):
|
| 406 |
+
if quant:
|
| 407 |
+
h = self.model(tokens, start_pos, inputs_embeds, chunk_prefilling)
|
| 408 |
+
else:
|
| 409 |
+
h = self.model.forwardfp16(
|
| 410 |
+
tokens, start_pos, inputs_embeds, chunk_prefilling
|
| 411 |
+
)
|
| 412 |
+
output = self.lm_head(h) # only compute last logits
|
| 413 |
+
return output.float()
|
llm-awq/tinychat/models/llava_base/llava_arch.py
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
from abc import ABC, abstractmethod
|
| 18 |
+
|
| 19 |
+
import torch
|
| 20 |
+
import torch.nn as nn
|
| 21 |
+
|
| 22 |
+
from .multimodal_encoder.builder import build_vision_tower
|
| 23 |
+
from .multimodal_projector.builder import build_vision_projector
|
| 24 |
+
|
| 25 |
+
from tinychat.utils.constants import (
|
| 26 |
+
LLAVA_IGNORE_INDEX,
|
| 27 |
+
LLAVA_DEFAULT_IMAGE_TOKEN_IDX,
|
| 28 |
+
LLAVA_DEFAULT_IMAGE_PATCH_TOKEN,
|
| 29 |
+
LLAVA_DEFAULT_IM_START_TOKEN,
|
| 30 |
+
LLAVA_DEFAULT_IM_END_TOKEN,
|
| 31 |
+
)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class LlavaMetaModel:
|
| 35 |
+
def __init__(self, config):
|
| 36 |
+
super(LlavaMetaModel, self).__init__(config)
|
| 37 |
+
|
| 38 |
+
if hasattr(config, "mm_vision_tower"):
|
| 39 |
+
self.vision_tower = build_vision_tower(config, delay_load=True)
|
| 40 |
+
self.mm_projector = build_vision_projector(config)
|
| 41 |
+
|
| 42 |
+
def get_vision_tower(self):
|
| 43 |
+
vision_tower = getattr(self, "vision_tower", None)
|
| 44 |
+
if type(vision_tower) is list:
|
| 45 |
+
vision_tower = vision_tower[0]
|
| 46 |
+
return vision_tower
|
| 47 |
+
|
| 48 |
+
def initialize_vision_modules(self, model_args, fsdp=None):
|
| 49 |
+
vision_tower = model_args.vision_tower
|
| 50 |
+
mm_vision_select_layer = model_args.mm_vision_select_layer
|
| 51 |
+
mm_vision_select_feature = model_args.mm_vision_select_feature
|
| 52 |
+
pretrain_mm_mlp_adapter = model_args.pretrain_mm_mlp_adapter
|
| 53 |
+
|
| 54 |
+
self.config.mm_vision_tower = vision_tower
|
| 55 |
+
|
| 56 |
+
if self.get_vision_tower() is None:
|
| 57 |
+
vision_tower = build_vision_tower(model_args)
|
| 58 |
+
|
| 59 |
+
if fsdp is not None and len(fsdp) > 0:
|
| 60 |
+
self.vision_tower = [vision_tower]
|
| 61 |
+
else:
|
| 62 |
+
self.vision_tower = vision_tower
|
| 63 |
+
else:
|
| 64 |
+
if fsdp is not None and len(fsdp) > 0:
|
| 65 |
+
vision_tower = self.vision_tower[0]
|
| 66 |
+
else:
|
| 67 |
+
vision_tower = self.vision_tower
|
| 68 |
+
vision_tower.load_model()
|
| 69 |
+
|
| 70 |
+
self.config.use_mm_proj = True
|
| 71 |
+
self.config.mm_projector_type = getattr(
|
| 72 |
+
model_args, "mm_projector_type", "linear"
|
| 73 |
+
)
|
| 74 |
+
self.config.mm_hidden_size = vision_tower.hidden_size
|
| 75 |
+
self.config.mm_vision_select_layer = mm_vision_select_layer
|
| 76 |
+
self.config.mm_vision_select_feature = mm_vision_select_feature
|
| 77 |
+
|
| 78 |
+
if getattr(self, "mm_projector", None) is None:
|
| 79 |
+
self.mm_projector = build_vision_projector(self.config)
|
| 80 |
+
else:
|
| 81 |
+
# In case it is frozen by LoRA
|
| 82 |
+
for p in self.mm_projector.parameters():
|
| 83 |
+
p.requires_grad = True
|
| 84 |
+
|
| 85 |
+
if pretrain_mm_mlp_adapter is not None:
|
| 86 |
+
mm_projector_weights = torch.load(
|
| 87 |
+
pretrain_mm_mlp_adapter, map_location="cpu"
|
| 88 |
+
)
|
| 89 |
+
|
| 90 |
+
def get_w(weights, keyword):
|
| 91 |
+
return {
|
| 92 |
+
k.split(keyword + ".")[1]: v
|
| 93 |
+
for k, v in weights.items()
|
| 94 |
+
if keyword in k
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
self.mm_projector.load_state_dict(
|
| 98 |
+
get_w(mm_projector_weights, "mm_projector")
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
class LlavaMetaForCausalLM(ABC):
|
| 103 |
+
@abstractmethod
|
| 104 |
+
def get_model(self):
|
| 105 |
+
pass
|
| 106 |
+
|
| 107 |
+
def get_vision_tower(self):
|
| 108 |
+
return self.get_model().get_vision_tower()
|
| 109 |
+
|
| 110 |
+
def encode_images(self, images):
|
| 111 |
+
vision_tower = self.get_model().get_vision_tower().half()
|
| 112 |
+
image_features = vision_tower(images)
|
| 113 |
+
image_features = self.get_model().mm_projector(image_features)
|
| 114 |
+
return image_features
|
| 115 |
+
|
| 116 |
+
def prepare_inputs_labels_for_multimodal(
|
| 117 |
+
self, input_ids, position_ids, attention_mask, past_key_values, labels, images
|
| 118 |
+
):
|
| 119 |
+
vision_tower = self.get_vision_tower()
|
| 120 |
+
if vision_tower is None or images is None or input_ids.shape[1] == 1:
|
| 121 |
+
if (
|
| 122 |
+
past_key_values is not None
|
| 123 |
+
and vision_tower is not None
|
| 124 |
+
and images is not None
|
| 125 |
+
and input_ids.shape[1] == 1
|
| 126 |
+
):
|
| 127 |
+
target_shape = past_key_values[-1][-1].shape[-2] + 1
|
| 128 |
+
attention_mask = torch.cat(
|
| 129 |
+
(
|
| 130 |
+
attention_mask,
|
| 131 |
+
torch.ones(
|
| 132 |
+
(
|
| 133 |
+
attention_mask.shape[0],
|
| 134 |
+
target_shape - attention_mask.shape[1],
|
| 135 |
+
),
|
| 136 |
+
dtype=attention_mask.dtype,
|
| 137 |
+
device=attention_mask.device,
|
| 138 |
+
),
|
| 139 |
+
),
|
| 140 |
+
dim=1,
|
| 141 |
+
)
|
| 142 |
+
position_ids = torch.sum(attention_mask, dim=1).unsqueeze(-1) - 1
|
| 143 |
+
return (
|
| 144 |
+
input_ids,
|
| 145 |
+
position_ids,
|
| 146 |
+
attention_mask,
|
| 147 |
+
past_key_values,
|
| 148 |
+
None,
|
| 149 |
+
labels,
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
if type(images) is list or images.ndim == 5:
|
| 153 |
+
concat_images = torch.cat([image for image in images], dim=0)
|
| 154 |
+
image_features = self.encode_images(concat_images)
|
| 155 |
+
split_sizes = [image.shape[0] for image in images]
|
| 156 |
+
image_features = torch.split(image_features, split_sizes, dim=0)
|
| 157 |
+
image_features = [x.flatten(0, 1).to(self.device) for x in image_features]
|
| 158 |
+
else:
|
| 159 |
+
image_features = self.encode_images(images).to(self.device)
|
| 160 |
+
|
| 161 |
+
# TODO: image start / end is not implemented here to support pretraining.
|
| 162 |
+
if getattr(self.config, "tune_mm_mlp_adapter", False) and getattr(
|
| 163 |
+
self.config, "mm_use_im_start_end", False
|
| 164 |
+
):
|
| 165 |
+
raise NotImplementedError
|
| 166 |
+
|
| 167 |
+
# Let's just add dummy tensors if they do not exist,
|
| 168 |
+
# it is a headache to deal with None all the time.
|
| 169 |
+
# But it is not ideal, and if you have a better idea,
|
| 170 |
+
# please open an issue / submit a PR, thanks.
|
| 171 |
+
_labels = labels
|
| 172 |
+
_position_ids = position_ids
|
| 173 |
+
_attention_mask = attention_mask
|
| 174 |
+
if attention_mask is None:
|
| 175 |
+
attention_mask = torch.ones_like(input_ids, dtype=torch.bool)
|
| 176 |
+
else:
|
| 177 |
+
attention_mask = attention_mask.bool()
|
| 178 |
+
if position_ids is None:
|
| 179 |
+
position_ids = torch.arange(
|
| 180 |
+
0, input_ids.shape[1], dtype=torch.long, device=input_ids.device
|
| 181 |
+
)
|
| 182 |
+
if labels is None:
|
| 183 |
+
labels = torch.full_like(input_ids, LLAVA_IGNORE_INDEX)
|
| 184 |
+
|
| 185 |
+
# remove the padding using attention_mask -- TODO: double check
|
| 186 |
+
input_ids = [
|
| 187 |
+
cur_input_ids[cur_attention_mask]
|
| 188 |
+
for cur_input_ids, cur_attention_mask in zip(input_ids, attention_mask)
|
| 189 |
+
]
|
| 190 |
+
labels = [
|
| 191 |
+
cur_labels[cur_attention_mask]
|
| 192 |
+
for cur_labels, cur_attention_mask in zip(labels, attention_mask)
|
| 193 |
+
]
|
| 194 |
+
|
| 195 |
+
new_input_embeds = []
|
| 196 |
+
new_labels = []
|
| 197 |
+
cur_image_idx = 0
|
| 198 |
+
for batch_idx, cur_input_ids in enumerate(input_ids):
|
| 199 |
+
num_images = (cur_input_ids == LLAVA_DEFAULT_IMAGE_TOKEN_IDX).sum()
|
| 200 |
+
if num_images == 0:
|
| 201 |
+
cur_image_features = image_features[cur_image_idx]
|
| 202 |
+
cur_input_embeds_1 = self.get_model().embed_tokens(cur_input_ids)
|
| 203 |
+
cur_input_embeds = torch.cat(
|
| 204 |
+
[cur_input_embeds_1, cur_image_features[0:0]], dim=0
|
| 205 |
+
)
|
| 206 |
+
new_input_embeds.append(cur_input_embeds)
|
| 207 |
+
new_labels.append(labels[batch_idx])
|
| 208 |
+
cur_image_idx += 1
|
| 209 |
+
continue
|
| 210 |
+
|
| 211 |
+
image_token_indices = (
|
| 212 |
+
[-1]
|
| 213 |
+
+ torch.where(cur_input_ids == LLAVA_DEFAULT_IMAGE_TOKEN_IDX)[
|
| 214 |
+
0
|
| 215 |
+
].tolist()
|
| 216 |
+
+ [cur_input_ids.shape[0]]
|
| 217 |
+
)
|
| 218 |
+
cur_input_ids_noim = []
|
| 219 |
+
cur_labels = labels[batch_idx]
|
| 220 |
+
cur_labels_noim = []
|
| 221 |
+
for i in range(len(image_token_indices) - 1):
|
| 222 |
+
cur_input_ids_noim.append(
|
| 223 |
+
cur_input_ids[
|
| 224 |
+
image_token_indices[i] + 1 : image_token_indices[i + 1]
|
| 225 |
+
]
|
| 226 |
+
)
|
| 227 |
+
cur_labels_noim.append(
|
| 228 |
+
cur_labels[image_token_indices[i] + 1 : image_token_indices[i + 1]]
|
| 229 |
+
)
|
| 230 |
+
split_sizes = [x.shape[0] for x in cur_labels_noim]
|
| 231 |
+
cur_input_embeds = self.get_model().embed_tokens(
|
| 232 |
+
torch.cat(cur_input_ids_noim)
|
| 233 |
+
)
|
| 234 |
+
cur_input_embeds_no_im = torch.split(cur_input_embeds, split_sizes, dim=0)
|
| 235 |
+
cur_new_input_embeds = []
|
| 236 |
+
cur_new_labels = []
|
| 237 |
+
|
| 238 |
+
for i in range(num_images + 1):
|
| 239 |
+
cur_new_input_embeds.append(cur_input_embeds_no_im[i])
|
| 240 |
+
cur_new_labels.append(cur_labels_noim[i])
|
| 241 |
+
if i < num_images:
|
| 242 |
+
cur_image_features = image_features[cur_image_idx]
|
| 243 |
+
cur_image_idx += 1
|
| 244 |
+
cur_new_input_embeds.append(cur_image_features)
|
| 245 |
+
cur_new_labels.append(
|
| 246 |
+
torch.full(
|
| 247 |
+
(cur_image_features.shape[0],),
|
| 248 |
+
LLAVA_IGNORE_INDEX,
|
| 249 |
+
device=cur_labels.device,
|
| 250 |
+
dtype=cur_labels.dtype,
|
| 251 |
+
)
|
| 252 |
+
)
|
| 253 |
+
|
| 254 |
+
cur_new_input_embeds = torch.cat(cur_new_input_embeds)
|
| 255 |
+
cur_new_labels = torch.cat(cur_new_labels)
|
| 256 |
+
|
| 257 |
+
new_input_embeds.append(cur_new_input_embeds)
|
| 258 |
+
new_labels.append(cur_new_labels)
|
| 259 |
+
|
| 260 |
+
# Truncate sequences to max length as image embeddings can make the sequence longer
|
| 261 |
+
tokenizer_model_max_length = getattr(
|
| 262 |
+
self.config, "tokenizer_model_max_length", None
|
| 263 |
+
)
|
| 264 |
+
if tokenizer_model_max_length is not None:
|
| 265 |
+
new_input_embeds = [
|
| 266 |
+
x[:tokenizer_model_max_length] for x in new_input_embeds
|
| 267 |
+
]
|
| 268 |
+
new_labels = [x[:tokenizer_model_max_length] for x in new_labels]
|
| 269 |
+
|
| 270 |
+
# Combine them
|
| 271 |
+
max_len = max(x.shape[0] for x in new_input_embeds)
|
| 272 |
+
batch_size = len(new_input_embeds)
|
| 273 |
+
|
| 274 |
+
new_input_embeds_padded = []
|
| 275 |
+
new_labels_padded = torch.full(
|
| 276 |
+
(batch_size, max_len),
|
| 277 |
+
LLAVA_IGNORE_INDEX,
|
| 278 |
+
dtype=new_labels[0].dtype,
|
| 279 |
+
device=new_labels[0].device,
|
| 280 |
+
)
|
| 281 |
+
attention_mask = torch.zeros(
|
| 282 |
+
(batch_size, max_len),
|
| 283 |
+
dtype=attention_mask.dtype,
|
| 284 |
+
device=attention_mask.device,
|
| 285 |
+
)
|
| 286 |
+
position_ids = torch.zeros(
|
| 287 |
+
(batch_size, max_len), dtype=position_ids.dtype, device=position_ids.device
|
| 288 |
+
)
|
| 289 |
+
|
| 290 |
+
for i, (cur_new_embed, cur_new_labels) in enumerate(
|
| 291 |
+
zip(new_input_embeds, new_labels)
|
| 292 |
+
):
|
| 293 |
+
cur_len = cur_new_embed.shape[0]
|
| 294 |
+
if getattr(self.config, "tokenizer_padding_side", "right") == "left":
|
| 295 |
+
new_input_embeds_padded.append(
|
| 296 |
+
torch.cat(
|
| 297 |
+
(
|
| 298 |
+
torch.zeros(
|
| 299 |
+
(max_len - cur_len, cur_new_embed.shape[1]),
|
| 300 |
+
dtype=cur_new_embed.dtype,
|
| 301 |
+
device=cur_new_embed.device,
|
| 302 |
+
),
|
| 303 |
+
cur_new_embed,
|
| 304 |
+
),
|
| 305 |
+
dim=0,
|
| 306 |
+
)
|
| 307 |
+
)
|
| 308 |
+
if cur_len > 0:
|
| 309 |
+
new_labels_padded[i, -cur_len:] = cur_new_labels
|
| 310 |
+
attention_mask[i, -cur_len:] = True
|
| 311 |
+
position_ids[i, -cur_len:] = torch.arange(
|
| 312 |
+
0, cur_len, dtype=position_ids.dtype, device=position_ids.device
|
| 313 |
+
)
|
| 314 |
+
else:
|
| 315 |
+
new_input_embeds_padded.append(
|
| 316 |
+
torch.cat(
|
| 317 |
+
(
|
| 318 |
+
cur_new_embed,
|
| 319 |
+
torch.zeros(
|
| 320 |
+
(max_len - cur_len, cur_new_embed.shape[1]),
|
| 321 |
+
dtype=cur_new_embed.dtype,
|
| 322 |
+
device=cur_new_embed.device,
|
| 323 |
+
),
|
| 324 |
+
),
|
| 325 |
+
dim=0,
|
| 326 |
+
)
|
| 327 |
+
)
|
| 328 |
+
if cur_len > 0:
|
| 329 |
+
new_labels_padded[i, :cur_len] = cur_new_labels
|
| 330 |
+
attention_mask[i, :cur_len] = True
|
| 331 |
+
position_ids[i, :cur_len] = torch.arange(
|
| 332 |
+
0, cur_len, dtype=position_ids.dtype, device=position_ids.device
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
new_input_embeds = torch.stack(new_input_embeds_padded, dim=0)
|
| 336 |
+
|
| 337 |
+
if _labels is None:
|
| 338 |
+
new_labels = None
|
| 339 |
+
else:
|
| 340 |
+
new_labels = new_labels_padded
|
| 341 |
+
|
| 342 |
+
if _attention_mask is None:
|
| 343 |
+
attention_mask = None
|
| 344 |
+
else:
|
| 345 |
+
attention_mask = attention_mask.to(dtype=_attention_mask.dtype)
|
| 346 |
+
|
| 347 |
+
if _position_ids is None:
|
| 348 |
+
position_ids = None
|
| 349 |
+
|
| 350 |
+
return (
|
| 351 |
+
None,
|
| 352 |
+
position_ids,
|
| 353 |
+
attention_mask,
|
| 354 |
+
past_key_values,
|
| 355 |
+
new_input_embeds,
|
| 356 |
+
new_labels,
|
| 357 |
+
)
|
| 358 |
+
|
| 359 |
+
def initialize_vision_tokenizer(self, model_args, tokenizer):
|
| 360 |
+
if model_args.mm_use_im_patch_token:
|
| 361 |
+
tokenizer.add_tokens([LLAVA_DEFAULT_IMAGE_PATCH_TOKEN], special_tokens=True)
|
| 362 |
+
self.resize_token_embeddings(len(tokenizer))
|
| 363 |
+
|
| 364 |
+
if model_args.mm_use_im_start_end:
|
| 365 |
+
num_new_tokens = tokenizer.add_tokens(
|
| 366 |
+
[LLAVA_DEFAULT_IM_START_TOKEN, LLAVA_DEFAULT_IM_END_TOKEN],
|
| 367 |
+
special_tokens=True,
|
| 368 |
+
)
|
| 369 |
+
self.resize_token_embeddings(len(tokenizer))
|
| 370 |
+
|
| 371 |
+
if num_new_tokens > 0:
|
| 372 |
+
input_embeddings = self.get_input_embeddings().weight.data
|
| 373 |
+
output_embeddings = self.get_output_embeddings().weight.data
|
| 374 |
+
|
| 375 |
+
input_embeddings_avg = input_embeddings[:-num_new_tokens].mean(
|
| 376 |
+
dim=0, keepdim=True
|
| 377 |
+
)
|
| 378 |
+
output_embeddings_avg = output_embeddings[:-num_new_tokens].mean(
|
| 379 |
+
dim=0, keepdim=True
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
input_embeddings[-num_new_tokens:] = input_embeddings_avg
|
| 383 |
+
output_embeddings[-num_new_tokens:] = output_embeddings_avg
|
| 384 |
+
|
| 385 |
+
if model_args.tune_mm_mlp_adapter:
|
| 386 |
+
for p in self.get_input_embeddings().parameters():
|
| 387 |
+
p.requires_grad = True
|
| 388 |
+
for p in self.get_output_embeddings().parameters():
|
| 389 |
+
p.requires_grad = False
|
| 390 |
+
|
| 391 |
+
if model_args.pretrain_mm_mlp_adapter:
|
| 392 |
+
mm_projector_weights = torch.load(
|
| 393 |
+
model_args.pretrain_mm_mlp_adapter, map_location="cpu"
|
| 394 |
+
)
|
| 395 |
+
embed_tokens_weight = mm_projector_weights["model.embed_tokens.weight"]
|
| 396 |
+
assert num_new_tokens == 2
|
| 397 |
+
if input_embeddings.shape == embed_tokens_weight.shape:
|
| 398 |
+
input_embeddings[-num_new_tokens:] = embed_tokens_weight[
|
| 399 |
+
-num_new_tokens:
|
| 400 |
+
]
|
| 401 |
+
elif embed_tokens_weight.shape[0] == num_new_tokens:
|
| 402 |
+
input_embeddings[-num_new_tokens:] = embed_tokens_weight
|
| 403 |
+
else:
|
| 404 |
+
raise ValueError(
|
| 405 |
+
f"Unexpected embed_tokens_weight shape. Pretrained: {embed_tokens_weight.shape}. Current: {input_embeddings.shape}. Numer of new tokens: {num_new_tokens}."
|
| 406 |
+
)
|
| 407 |
+
elif model_args.mm_use_im_patch_token:
|
| 408 |
+
if model_args.tune_mm_mlp_adapter:
|
| 409 |
+
for p in self.get_input_embeddings().parameters():
|
| 410 |
+
p.requires_grad = False
|
| 411 |
+
for p in self.get_output_embeddings().parameters():
|
| 412 |
+
p.requires_grad = False
|
llm-awq/tinychat/models/llava_base/multimodal_encoder/builder.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
|
| 3 |
+
import os
|
| 4 |
+
from .clip_encoder import CLIPVisionTower
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def build_vision_tower(vision_tower_cfg, **kwargs):
|
| 8 |
+
vision_tower = getattr(
|
| 9 |
+
vision_tower_cfg,
|
| 10 |
+
"mm_vision_tower",
|
| 11 |
+
getattr(vision_tower_cfg, "vision_tower", None),
|
| 12 |
+
)
|
| 13 |
+
is_absolute_path_exists = os.path.exists(vision_tower)
|
| 14 |
+
if (
|
| 15 |
+
is_absolute_path_exists
|
| 16 |
+
or vision_tower.startswith("openai")
|
| 17 |
+
or vision_tower.startswith("laion")
|
| 18 |
+
):
|
| 19 |
+
return CLIPVisionTower(vision_tower, args=vision_tower_cfg, **kwargs)
|
| 20 |
+
|
| 21 |
+
raise ValueError(f"Unknown vision tower: {vision_tower}")
|
llm-awq/tinychat/models/llava_base/multimodal_encoder/clip_encoder.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
|
| 6 |
+
from transformers import CLIPVisionModel, CLIPImageProcessor, CLIPVisionConfig
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class CLIPVisionTower(nn.Module):
|
| 10 |
+
def __init__(self, vision_tower, args, delay_load=False):
|
| 11 |
+
super().__init__()
|
| 12 |
+
|
| 13 |
+
self.is_loaded = False
|
| 14 |
+
|
| 15 |
+
self.vision_tower_name = vision_tower
|
| 16 |
+
self.select_layer = args.mm_vision_select_layer
|
| 17 |
+
self.select_feature = getattr(args, "mm_vision_select_feature", "patch")
|
| 18 |
+
|
| 19 |
+
if not delay_load:
|
| 20 |
+
self.load_model()
|
| 21 |
+
else:
|
| 22 |
+
self.cfg_only = CLIPVisionConfig.from_pretrained(self.vision_tower_name)
|
| 23 |
+
|
| 24 |
+
def load_model(self):
|
| 25 |
+
self.image_processor = CLIPImageProcessor.from_pretrained(
|
| 26 |
+
self.vision_tower_name
|
| 27 |
+
)
|
| 28 |
+
self.vision_tower = CLIPVisionModel.from_pretrained(self.vision_tower_name)
|
| 29 |
+
self.vision_tower.requires_grad_(False)
|
| 30 |
+
|
| 31 |
+
self.is_loaded = True
|
| 32 |
+
|
| 33 |
+
def feature_select(self, image_forward_outs):
|
| 34 |
+
image_features = image_forward_outs.hidden_states[self.select_layer]
|
| 35 |
+
if self.select_feature == "patch":
|
| 36 |
+
image_features = image_features[:, 1:]
|
| 37 |
+
elif self.select_feature == "cls_patch":
|
| 38 |
+
image_features = image_features
|
| 39 |
+
else:
|
| 40 |
+
raise ValueError(f"Unexpected select feature: {self.select_feature}")
|
| 41 |
+
return image_features
|
| 42 |
+
|
| 43 |
+
@torch.no_grad()
|
| 44 |
+
def forward(self, images):
|
| 45 |
+
if type(images) is list:
|
| 46 |
+
image_features = []
|
| 47 |
+
for image in images:
|
| 48 |
+
image_forward_out = self.vision_tower(
|
| 49 |
+
image.to(device=self.device, dtype=self.dtype).unsqueeze(0),
|
| 50 |
+
output_hidden_states=True,
|
| 51 |
+
)
|
| 52 |
+
image_feature = self.feature_select(image_forward_out).to(image.dtype)
|
| 53 |
+
image_features.append(image_feature)
|
| 54 |
+
else:
|
| 55 |
+
# import time
|
| 56 |
+
# torch.cuda.synchronize()
|
| 57 |
+
# image2 = images.to(self.dtype)
|
| 58 |
+
# print("model dtype:", self.dtype)
|
| 59 |
+
# print("image dtype:", images.dtype)
|
| 60 |
+
# st11 = time.time()
|
| 61 |
+
image_forward_outs = self.vision_tower(
|
| 62 |
+
images.to(device=self.device, dtype=self.dtype),
|
| 63 |
+
output_hidden_states=True,
|
| 64 |
+
)
|
| 65 |
+
# torch.cuda.synchronize()
|
| 66 |
+
# ed11 = time.time()
|
| 67 |
+
# print("hh2", (ed11 - st11)*1000)
|
| 68 |
+
image_features = self.feature_select(image_forward_outs).to(images.dtype)
|
| 69 |
+
|
| 70 |
+
return image_features
|
| 71 |
+
|
| 72 |
+
@property
|
| 73 |
+
def dummy_feature(self):
|
| 74 |
+
return torch.zeros(1, self.hidden_size, device=self.device, dtype=self.dtype)
|
| 75 |
+
|
| 76 |
+
@property
|
| 77 |
+
def dtype(self):
|
| 78 |
+
return self.vision_tower.dtype
|
| 79 |
+
|
| 80 |
+
@property
|
| 81 |
+
def device(self):
|
| 82 |
+
return self.vision_tower.device
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def config(self):
|
| 86 |
+
if self.is_loaded:
|
| 87 |
+
return self.vision_tower.config
|
| 88 |
+
else:
|
| 89 |
+
return self.cfg_only
|
| 90 |
+
|
| 91 |
+
@property
|
| 92 |
+
def hidden_size(self):
|
| 93 |
+
return self.config.hidden_size
|
| 94 |
+
|
| 95 |
+
@property
|
| 96 |
+
def num_patches(self):
|
| 97 |
+
return (self.config.image_size // self.config.patch_size) ** 2
|
llm-awq/tinychat/models/llava_base/multimodal_projector/builder.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn as nn
|
| 5 |
+
import re
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
class IdentityMap(nn.Module):
|
| 9 |
+
def __init__(self):
|
| 10 |
+
super().__init__()
|
| 11 |
+
|
| 12 |
+
def forward(self, x, *args, **kwargs):
|
| 13 |
+
return x
|
| 14 |
+
|
| 15 |
+
@property
|
| 16 |
+
def config(self):
|
| 17 |
+
return {"mm_projector_type": "identity"}
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class SimpleResBlock(nn.Module):
|
| 21 |
+
def __init__(self, channels):
|
| 22 |
+
super().__init__()
|
| 23 |
+
self.pre_norm = nn.LayerNorm(channels)
|
| 24 |
+
|
| 25 |
+
self.proj = nn.Sequential(
|
| 26 |
+
nn.Linear(channels, channels), nn.GELU(), nn.Linear(channels, channels)
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
def forward(self, x):
|
| 30 |
+
x = self.pre_norm(x)
|
| 31 |
+
return x + self.proj(x)
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def build_vision_projector(config, delay_load=False, **kwargs):
|
| 35 |
+
projector_type = getattr(config, "mm_projector_type", "linear")
|
| 36 |
+
|
| 37 |
+
if projector_type == "linear":
|
| 38 |
+
return nn.Linear(config.mm_hidden_size, config.hidden_size)
|
| 39 |
+
|
| 40 |
+
mlp_gelu_match = re.match(r"^mlp(\d+)x_gelu$", projector_type)
|
| 41 |
+
if mlp_gelu_match:
|
| 42 |
+
mlp_depth = int(mlp_gelu_match.group(1))
|
| 43 |
+
modules = [nn.Linear(config.mm_hidden_size, config.hidden_size)]
|
| 44 |
+
for _ in range(1, mlp_depth):
|
| 45 |
+
modules.append(nn.GELU())
|
| 46 |
+
modules.append(nn.Linear(config.hidden_size, config.hidden_size))
|
| 47 |
+
return nn.Sequential(*modules)
|
| 48 |
+
|
| 49 |
+
if projector_type == "identity":
|
| 50 |
+
return IdentityMap()
|
| 51 |
+
|
| 52 |
+
if projector_type == "linearclip":
|
| 53 |
+
# load min, max range
|
| 54 |
+
min_max_range = torch.load(config.min_max_range_path)
|
| 55 |
+
assert min_max_range is not None
|
| 56 |
+
|
| 57 |
+
class RangeClip(nn.Module): # actually KNN projector
|
| 58 |
+
def __init__(self, min, max) -> None:
|
| 59 |
+
super().__init__()
|
| 60 |
+
self.register_buffer("min", min.detach().view(1, -1))
|
| 61 |
+
self.register_buffer("max", max.detach().view(1, -1))
|
| 62 |
+
|
| 63 |
+
def forward(self, x):
|
| 64 |
+
# dimension broadcast auto done
|
| 65 |
+
return torch.clamp(x, self.min.detach(), self.max.detach())
|
| 66 |
+
|
| 67 |
+
return nn.Sequential(
|
| 68 |
+
nn.Linear(config.mm_hidden_size, config.hidden_size),
|
| 69 |
+
RangeClip(*min_max_range),
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
raise ValueError(f"Unknown projector type: {projector_type}")
|
llm-awq/tinychat/models/llava_llama.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import os
|
| 17 |
+
import warnings
|
| 18 |
+
import shutil
|
| 19 |
+
import torch
|
| 20 |
+
import torch.nn as nn
|
| 21 |
+
from typing import List, Optional, Tuple, Union
|
| 22 |
+
|
| 23 |
+
from transformers import CLIPVisionModel
|
| 24 |
+
|
| 25 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 26 |
+
|
| 27 |
+
from .llava_base.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM
|
| 28 |
+
from .llama import LlamaForCausalLM, Transformer
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class LlavaLlamaModel(LlavaMetaModel, Transformer):
|
| 32 |
+
def __init__(self, config):
|
| 33 |
+
super(LlavaLlamaModel, self).__init__(config)
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class LlavaLlamaForCausalLM(LlamaForCausalLM, LlavaMetaForCausalLM):
|
| 37 |
+
def __init__(self, config, dev="cuda"):
|
| 38 |
+
super(LlavaLlamaForCausalLM, self).__init__(config)
|
| 39 |
+
self.model = LlavaLlamaModel(config)
|
| 40 |
+
self.pretraining_tp = config.pretraining_tp
|
| 41 |
+
self.vocab_size = config.vocab_size
|
| 42 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 43 |
+
self.config = config
|
| 44 |
+
self.device = dev
|
| 45 |
+
|
| 46 |
+
def get_model(self):
|
| 47 |
+
return self.model
|
| 48 |
+
|
| 49 |
+
def default_inputs_embeds_for_multimodal(self, input_ids, inputs_embeds, images):
|
| 50 |
+
if inputs_embeds is None:
|
| 51 |
+
inputs_embeds = self.get_model().embed_tokens(input_ids)
|
| 52 |
+
vision_tower = self.get_vision_tower().vision_tower
|
| 53 |
+
from contextlib import nullcontext
|
| 54 |
+
|
| 55 |
+
if (
|
| 56 |
+
vision_tower is not None
|
| 57 |
+
and (input_ids.shape[1] != 1 or self.training)
|
| 58 |
+
and images is not None
|
| 59 |
+
):
|
| 60 |
+
from tinychat.utils.constants import LLAVA_DEFAULT_IMAGE_PATCH_TOKEN_IDX
|
| 61 |
+
|
| 62 |
+
with (
|
| 63 |
+
nullcontext()
|
| 64 |
+
if getattr(self.config, "tune_vision_encoder", False)
|
| 65 |
+
else torch.no_grad()
|
| 66 |
+
):
|
| 67 |
+
if type(images) is list:
|
| 68 |
+
images = [
|
| 69 |
+
image.unsqueeze(0) if len(image.shape) == 3 else image
|
| 70 |
+
for image in images
|
| 71 |
+
]
|
| 72 |
+
images = torch.cat(images, dim=0)
|
| 73 |
+
dtype = next(vision_tower.parameters()).dtype
|
| 74 |
+
if "visiontransformer" in vision_tower.__class__.__name__.lower():
|
| 75 |
+
image_features = vision_tower(images.to(dtype))
|
| 76 |
+
else:
|
| 77 |
+
image_forward_outs = vision_tower(
|
| 78 |
+
images.to(dtype), output_hidden_states=True
|
| 79 |
+
)
|
| 80 |
+
select_hidden_state_layer = getattr(
|
| 81 |
+
self.config, "mm_vision_select_layer", -1
|
| 82 |
+
)
|
| 83 |
+
if abs(select_hidden_state_layer) > 100: # TOOD: find a better impl
|
| 84 |
+
# -212 -> 12,
|
| 85 |
+
idx1, idx2 = abs(select_hidden_state_layer) % 100, -(
|
| 86 |
+
abs(select_hidden_state_layer) // 100
|
| 87 |
+
)
|
| 88 |
+
# print("selecting multiple indices", idx1, idx2)
|
| 89 |
+
image_features = torch.cat(
|
| 90 |
+
(
|
| 91 |
+
image_forward_outs.hidden_states[idx1],
|
| 92 |
+
image_forward_outs.hidden_states[idx2],
|
| 93 |
+
),
|
| 94 |
+
dim=-1,
|
| 95 |
+
)
|
| 96 |
+
else:
|
| 97 |
+
image_features = image_forward_outs.hidden_states[
|
| 98 |
+
select_hidden_state_layer
|
| 99 |
+
]
|
| 100 |
+
if isinstance(vision_tower, CLIPVisionModel): # clip case, not for sam
|
| 101 |
+
image_features = image_features[:, 1:].to(images.dtype) # (B, N, D)
|
| 102 |
+
|
| 103 |
+
image_features = self.model.mm_projector(image_features)
|
| 104 |
+
|
| 105 |
+
if hasattr(self.config, "neftune_alpha") and self.config.neftune_alpha > 0:
|
| 106 |
+
# print("using neftune tuning with alpha", self.config.neftune_alpha)
|
| 107 |
+
dims = torch.tensor(image_features.shape[-2] * image_features.shape[-1])
|
| 108 |
+
mag_norm = self.config.neftune_alpha / torch.sqrt(dims)
|
| 109 |
+
image_features = image_features + torch.zeros_like(
|
| 110 |
+
image_features
|
| 111 |
+
).uniform_(-mag_norm, mag_norm)
|
| 112 |
+
|
| 113 |
+
if self.config.mm_projector_type == "dsresampler":
|
| 114 |
+
dummy_feat_shape = (1, 1024, 1664)
|
| 115 |
+
elif self.config.mm_projector_type == "linear2":
|
| 116 |
+
dummy_feat_shape = (1, 256, self.config.mm_hidden_size * 2)
|
| 117 |
+
else:
|
| 118 |
+
dummy_feat_shape = (1, 256, self.config.mm_hidden_size)
|
| 119 |
+
|
| 120 |
+
dummy_image_features = torch.zeros(
|
| 121 |
+
*dummy_feat_shape,
|
| 122 |
+
device=inputs_embeds.device,
|
| 123 |
+
dtype=inputs_embeds.dtype,
|
| 124 |
+
)
|
| 125 |
+
dummy_image_features = self.model.mm_projector(dummy_image_features)[
|
| 126 |
+
0
|
| 127 |
+
] # (1, N, D)
|
| 128 |
+
|
| 129 |
+
new_input_embeds = []
|
| 130 |
+
cur_image_idx = 0
|
| 131 |
+
|
| 132 |
+
image_token_idx = []
|
| 133 |
+
|
| 134 |
+
num_patches = -1
|
| 135 |
+
for i_sample, (cur_input_ids, cur_input_embeds) in enumerate(
|
| 136 |
+
zip(input_ids, inputs_embeds)
|
| 137 |
+
):
|
| 138 |
+
if (cur_input_ids == LLAVA_DEFAULT_IMAGE_PATCH_TOKEN_IDX).sum() == 0:
|
| 139 |
+
# multimodal LLM, but the current sample is not multimodal
|
| 140 |
+
cur_input_embeds = (
|
| 141 |
+
cur_input_embeds + (0.0 * dummy_image_features).sum()
|
| 142 |
+
)
|
| 143 |
+
new_input_embeds.append(cur_input_embeds)
|
| 144 |
+
# cur_image_idx += 1
|
| 145 |
+
continue
|
| 146 |
+
# TODO: Need to fix if vision_tower.config.use_im_start_end == True
|
| 147 |
+
num_total_patches = (
|
| 148 |
+
(cur_input_ids == LLAVA_DEFAULT_IMAGE_PATCH_TOKEN_IDX).sum().item()
|
| 149 |
+
)
|
| 150 |
+
masked_indices = torch.where(
|
| 151 |
+
cur_input_ids == LLAVA_DEFAULT_IMAGE_PATCH_TOKEN_IDX
|
| 152 |
+
)[0]
|
| 153 |
+
|
| 154 |
+
while num_total_patches:
|
| 155 |
+
if cur_image_idx >= image_features.shape[0]: # SHOULD NOT HAPPEN!!!
|
| 156 |
+
if self.training:
|
| 157 |
+
print("%" * 20, "INDEXING ERROR!")
|
| 158 |
+
break
|
| 159 |
+
else:
|
| 160 |
+
raise ValueError("INDEXING ERROR!")
|
| 161 |
+
cur_image_features = image_features[cur_image_idx]
|
| 162 |
+
num_patches = cur_image_features.shape[0]
|
| 163 |
+
mask_index_start = masked_indices[0]
|
| 164 |
+
masked_indices = masked_indices[num_patches:]
|
| 165 |
+
|
| 166 |
+
image_token_idx.append(
|
| 167 |
+
(
|
| 168 |
+
i_sample,
|
| 169 |
+
mask_index_start.item(),
|
| 170 |
+
(mask_index_start + num_patches).item(),
|
| 171 |
+
)
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
orig_embeds_params = None
|
| 175 |
+
if orig_embeds_params is not None:
|
| 176 |
+
cur_input_embeds = torch.cat(
|
| 177 |
+
(
|
| 178 |
+
cur_input_embeds[:mask_index_start].detach(),
|
| 179 |
+
cur_image_features,
|
| 180 |
+
cur_input_embeds[
|
| 181 |
+
mask_index_start + num_patches :
|
| 182 |
+
].detach(),
|
| 183 |
+
),
|
| 184 |
+
dim=0,
|
| 185 |
+
)
|
| 186 |
+
else:
|
| 187 |
+
cur_input_embeds = torch.cat(
|
| 188 |
+
(
|
| 189 |
+
cur_input_embeds[:mask_index_start],
|
| 190 |
+
cur_image_features,
|
| 191 |
+
cur_input_embeds[mask_index_start + num_patches :],
|
| 192 |
+
),
|
| 193 |
+
dim=0,
|
| 194 |
+
)
|
| 195 |
+
num_total_patches -= num_patches
|
| 196 |
+
assert num_total_patches >= 0, (num_total_patches, num_patches)
|
| 197 |
+
cur_image_idx += 1
|
| 198 |
+
|
| 199 |
+
new_input_embeds.append(cur_input_embeds)
|
| 200 |
+
if self.training:
|
| 201 |
+
if not masked_indices.numel() == 0:
|
| 202 |
+
print("%" * 20, "ERROR! masked_indices not empty...")
|
| 203 |
+
else:
|
| 204 |
+
assert masked_indices.numel() == 0
|
| 205 |
+
|
| 206 |
+
inputs_embeds = torch.stack(new_input_embeds, dim=0)
|
| 207 |
+
|
| 208 |
+
return inputs_embeds
|
| 209 |
+
|
| 210 |
+
def forward(
|
| 211 |
+
self,
|
| 212 |
+
input_ids: torch.LongTensor = None,
|
| 213 |
+
start_pos: int = None,
|
| 214 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 215 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 216 |
+
past_key_values: Optional[List[torch.FloatTensor]] = None,
|
| 217 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 218 |
+
labels: Optional[torch.LongTensor] = None,
|
| 219 |
+
use_cache: Optional[bool] = None,
|
| 220 |
+
output_attentions: Optional[bool] = None,
|
| 221 |
+
output_hidden_states: Optional[bool] = None,
|
| 222 |
+
images: Optional[torch.FloatTensor] = None,
|
| 223 |
+
return_dict: Optional[bool] = None,
|
| 224 |
+
special_token: bool = False,
|
| 225 |
+
) -> Union[Tuple, CausalLMOutputWithPast]:
|
| 226 |
+
if inputs_embeds is None:
|
| 227 |
+
if special_token:
|
| 228 |
+
(
|
| 229 |
+
input_ids,
|
| 230 |
+
position_ids,
|
| 231 |
+
attention_mask,
|
| 232 |
+
past_key_values,
|
| 233 |
+
inputs_embeds,
|
| 234 |
+
labels,
|
| 235 |
+
) = self.prepare_inputs_labels_for_multimodal(
|
| 236 |
+
input_ids,
|
| 237 |
+
position_ids,
|
| 238 |
+
attention_mask,
|
| 239 |
+
past_key_values,
|
| 240 |
+
labels,
|
| 241 |
+
images,
|
| 242 |
+
)
|
| 243 |
+
else:
|
| 244 |
+
inputs_embeds = self.default_inputs_embeds_for_multimodal(
|
| 245 |
+
input_ids, inputs_embeds, images
|
| 246 |
+
)
|
| 247 |
+
input_ids = None
|
| 248 |
+
|
| 249 |
+
if start_pos == None:
|
| 250 |
+
out = super().forward(
|
| 251 |
+
input_ids=input_ids,
|
| 252 |
+
attention_mask=attention_mask,
|
| 253 |
+
position_ids=position_ids,
|
| 254 |
+
past_key_values=past_key_values,
|
| 255 |
+
inputs_embeds=inputs_embeds,
|
| 256 |
+
labels=labels,
|
| 257 |
+
use_cache=use_cache,
|
| 258 |
+
output_attentions=output_attentions,
|
| 259 |
+
output_hidden_states=output_hidden_states,
|
| 260 |
+
return_dict=return_dict,
|
| 261 |
+
)
|
| 262 |
+
else:
|
| 263 |
+
out = super().forward(
|
| 264 |
+
tokens=input_ids,
|
| 265 |
+
start_pos=start_pos,
|
| 266 |
+
inputs_embeds=inputs_embeds,
|
| 267 |
+
)
|
| 268 |
+
return out
|
| 269 |
+
|
| 270 |
+
def prepare_inputs_for_generation(
|
| 271 |
+
self, input_ids, past_key_values=None, inputs_embeds=None, **kwargs
|
| 272 |
+
):
|
| 273 |
+
images = kwargs.pop("images", None)
|
| 274 |
+
_inputs = super().prepare_inputs_for_generation(
|
| 275 |
+
input_ids,
|
| 276 |
+
past_key_values=past_key_values,
|
| 277 |
+
inputs_embeds=inputs_embeds,
|
| 278 |
+
**kwargs,
|
| 279 |
+
)
|
| 280 |
+
if images is not None:
|
| 281 |
+
_inputs["images"] = images
|
| 282 |
+
return _inputs
|
llm-awq/tinychat/models/mpt.py
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
# This software may be used and distributed according to the terms of the GNU General Public License version 3.
|
| 3 |
+
|
| 4 |
+
from typing import Optional, Tuple
|
| 5 |
+
from dataclasses import dataclass
|
| 6 |
+
import math
|
| 7 |
+
|
| 8 |
+
import torch
|
| 9 |
+
from torch import nn
|
| 10 |
+
import torch.nn.functional as F
|
| 11 |
+
import awq_inference_engine
|
| 12 |
+
from transformers.models.llama.modeling_llama import LlamaRotaryEmbedding
|
| 13 |
+
|
| 14 |
+
# from flash_attn.flash_attn_interface import flash_attn_unpadded_func
|
| 15 |
+
|
| 16 |
+
import tinychat.utils.constants
|
| 17 |
+
|
| 18 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 19 |
+
global_max_seq_len = tinychat.utils.constants.max_seq_len
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def gen_slopes(n_heads, alibi_bias_max=8):
|
| 23 |
+
_n_heads = 2 ** math.ceil(math.log2(n_heads))
|
| 24 |
+
m = torch.arange(1, _n_heads + 1, dtype=torch.float32)
|
| 25 |
+
m = m.mul(alibi_bias_max / _n_heads)
|
| 26 |
+
slopes = 1.0 / torch.pow(2, m)
|
| 27 |
+
if _n_heads != n_heads:
|
| 28 |
+
slopes = torch.concat([slopes[1::2], slopes[::2]])[:n_heads]
|
| 29 |
+
return slopes.view(1, n_heads, 1, 1)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def build_alibi_bias(
|
| 33 |
+
n_heads, seq_len, full=False, alibi_bias_max=8, dtype=torch.float32
|
| 34 |
+
):
|
| 35 |
+
alibi_bias = torch.arange(1 - seq_len, 1, dtype=torch.int32).view(1, 1, 1, seq_len)
|
| 36 |
+
if full:
|
| 37 |
+
alibi_bias = alibi_bias - torch.arange(1 - seq_len, 1, dtype=torch.int32).view(
|
| 38 |
+
1, 1, seq_len, 1
|
| 39 |
+
)
|
| 40 |
+
alibi_bias = alibi_bias.abs().mul(-1)
|
| 41 |
+
slopes = gen_slopes(n_heads, alibi_bias_max)
|
| 42 |
+
alibi_bias = alibi_bias * slopes
|
| 43 |
+
slopes = slopes.squeeze(0).squeeze(-1).squeeze(-1)
|
| 44 |
+
return slopes.to(dtype=dtype), alibi_bias.to(dtype=dtype)
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
def _cast_if_autocast_enabled(tensor):
|
| 48 |
+
if torch.is_autocast_enabled():
|
| 49 |
+
if tensor.device.type == "cuda":
|
| 50 |
+
dtype = torch.get_autocast_gpu_dtype()
|
| 51 |
+
elif tensor.device.type == "cpu":
|
| 52 |
+
dtype = torch.get_autocast_cpu_dtype()
|
| 53 |
+
else:
|
| 54 |
+
raise NotImplementedError()
|
| 55 |
+
return tensor.to(dtype=dtype)
|
| 56 |
+
return tensor
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
class LPLayerNorm(torch.nn.LayerNorm):
|
| 60 |
+
def __init__(self, normalized_shape, eps=1e-05, elementwise_affine=True):
|
| 61 |
+
super().__init__(
|
| 62 |
+
normalized_shape=normalized_shape,
|
| 63 |
+
eps=eps,
|
| 64 |
+
elementwise_affine=elementwise_affine,
|
| 65 |
+
)
|
| 66 |
+
|
| 67 |
+
def forward(self, x):
|
| 68 |
+
module_device = x.device
|
| 69 |
+
downcast_x = _cast_if_autocast_enabled(x)
|
| 70 |
+
downcast_weight = (
|
| 71 |
+
_cast_if_autocast_enabled(self.weight)
|
| 72 |
+
if self.weight is not None
|
| 73 |
+
else self.weight
|
| 74 |
+
)
|
| 75 |
+
downcast_bias = (
|
| 76 |
+
_cast_if_autocast_enabled(self.bias) if self.bias is not None else self.bias
|
| 77 |
+
)
|
| 78 |
+
with torch.autocast(enabled=False, device_type=module_device.type):
|
| 79 |
+
return torch.nn.functional.layer_norm(
|
| 80 |
+
downcast_x,
|
| 81 |
+
self.normalized_shape,
|
| 82 |
+
downcast_weight,
|
| 83 |
+
downcast_bias,
|
| 84 |
+
self.eps,
|
| 85 |
+
)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
class SharedEmbedding(nn.Embedding):
|
| 89 |
+
def forward(self, input: torch.Tensor, unembed: bool = False) -> torch.Tensor:
|
| 90 |
+
if unembed:
|
| 91 |
+
return F.linear(input, self.weight)
|
| 92 |
+
return super().forward(input)
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
class MPTAttentionFused(nn.Module):
|
| 96 |
+
def __init__(self, args):
|
| 97 |
+
super().__init__()
|
| 98 |
+
self.args = args
|
| 99 |
+
self.n_local_heads = args.n_heads
|
| 100 |
+
self.head_dim = args.d_model // args.n_heads
|
| 101 |
+
args.max_seq_len = min(args.max_seq_len, global_max_seq_len)
|
| 102 |
+
|
| 103 |
+
self.Wqkv = nn.Linear(
|
| 104 |
+
args.d_model,
|
| 105 |
+
args.n_heads * self.head_dim * 3,
|
| 106 |
+
bias=False,
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
self.out_proj = nn.Linear(
|
| 110 |
+
args.n_heads * self.head_dim,
|
| 111 |
+
args.d_model,
|
| 112 |
+
bias=False,
|
| 113 |
+
)
|
| 114 |
+
|
| 115 |
+
# following fastertransformer definition
|
| 116 |
+
|
| 117 |
+
self.cache_v = (
|
| 118 |
+
torch.zeros(
|
| 119 |
+
(
|
| 120 |
+
max_batch_size,
|
| 121 |
+
self.n_local_heads,
|
| 122 |
+
args.max_seq_len,
|
| 123 |
+
self.head_dim,
|
| 124 |
+
)
|
| 125 |
+
)
|
| 126 |
+
.cuda()
|
| 127 |
+
.half()
|
| 128 |
+
) # added to half
|
| 129 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 130 |
+
self.cache_k = (
|
| 131 |
+
torch.zeros(
|
| 132 |
+
(
|
| 133 |
+
max_batch_size,
|
| 134 |
+
self.n_local_heads,
|
| 135 |
+
self.head_dim // 8,
|
| 136 |
+
args.max_seq_len,
|
| 137 |
+
8,
|
| 138 |
+
)
|
| 139 |
+
)
|
| 140 |
+
.cuda()
|
| 141 |
+
.half()
|
| 142 |
+
) # added to half
|
| 143 |
+
|
| 144 |
+
alibi_slopes, alibi_bias = build_alibi_bias(
|
| 145 |
+
self.n_local_heads, args.max_seq_len
|
| 146 |
+
)
|
| 147 |
+
# TODO (Haotian): fix device
|
| 148 |
+
self.alibi_slopes = alibi_slopes.float().to("cuda:0")
|
| 149 |
+
self.alibi_bias = alibi_bias.to("cuda:0")
|
| 150 |
+
|
| 151 |
+
def forward(
|
| 152 |
+
self,
|
| 153 |
+
x: torch.Tensor,
|
| 154 |
+
start_pos: int,
|
| 155 |
+
mask: Optional[torch.Tensor],
|
| 156 |
+
):
|
| 157 |
+
bsz, seqlen, _ = x.shape
|
| 158 |
+
xqkv = self.Wqkv(x)
|
| 159 |
+
xqkv = xqkv.view(bsz, seqlen, -1, self.n_local_heads, self.head_dim)
|
| 160 |
+
xq = xqkv[:, :, 0]
|
| 161 |
+
xk = xqkv[:, :, 1]
|
| 162 |
+
xv = xqkv[:, :, 2]
|
| 163 |
+
|
| 164 |
+
if seqlen > 1:
|
| 165 |
+
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 166 |
+
xk = xk.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 167 |
+
xv = xv.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 168 |
+
|
| 169 |
+
self.cache_k = self.cache_k.to(xq)
|
| 170 |
+
self.cache_v = self.cache_v.to(xq)
|
| 171 |
+
|
| 172 |
+
values_store = xv.transpose(2, 1)
|
| 173 |
+
keys_store = (
|
| 174 |
+
xk.reshape(bsz, seqlen, self.n_local_heads, self.head_dim // 8, 8)
|
| 175 |
+
.permute(0, 2, 3, 1, 4)
|
| 176 |
+
.contiguous()
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 180 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 181 |
+
|
| 182 |
+
keys = xk
|
| 183 |
+
values = xv
|
| 184 |
+
|
| 185 |
+
xq = xq.transpose(1, 2)
|
| 186 |
+
keys = keys.transpose(1, 2)
|
| 187 |
+
values = values.transpose(1, 2)
|
| 188 |
+
scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
|
| 189 |
+
scores += self.alibi_bias[..., :seqlen]
|
| 190 |
+
if mask is not None:
|
| 191 |
+
scores = scores + mask # (bs, n_local_heads, slen, cache_len + slen)
|
| 192 |
+
scores = F.softmax(scores.float(), dim=-1).type_as(xq)
|
| 193 |
+
output = torch.matmul(scores, values) # (bs, n_local_heads, slen, head_dim)
|
| 194 |
+
output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)
|
| 195 |
+
else:
|
| 196 |
+
# xq = xq[:, 0, :, :]
|
| 197 |
+
# xk = xk[:, 0, :, :]
|
| 198 |
+
# xv = xv[:, 0, :, :]
|
| 199 |
+
xq = xq.view(bsz, self.n_local_heads, self.head_dim)
|
| 200 |
+
xk = xk.view(bsz, self.n_local_heads, self.head_dim)
|
| 201 |
+
xv = xv.view(bsz, self.n_local_heads, self.head_dim)
|
| 202 |
+
output = awq_inference_engine.single_query_attention(
|
| 203 |
+
xq,
|
| 204 |
+
xk,
|
| 205 |
+
xv,
|
| 206 |
+
self.cache_k,
|
| 207 |
+
self.cache_v,
|
| 208 |
+
None,
|
| 209 |
+
# with alibi embedding
|
| 210 |
+
self.alibi_slopes.float(),
|
| 211 |
+
start_pos,
|
| 212 |
+
# rotary embed dim = 0 => no rotary embedding
|
| 213 |
+
0,
|
| 214 |
+
10000,
|
| 215 |
+
1.0,
|
| 216 |
+
True,
|
| 217 |
+
)
|
| 218 |
+
output = output.reshape(bsz, 1, -1)
|
| 219 |
+
|
| 220 |
+
return self.out_proj(output)
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
class MPTMLP(nn.Module):
|
| 224 |
+
def __init__(self, d_model: int, expansion_ratio: int):
|
| 225 |
+
super().__init__()
|
| 226 |
+
self.up_proj = nn.Linear(d_model, expansion_ratio * d_model, bias=False)
|
| 227 |
+
self.act = nn.GELU(approximate="none")
|
| 228 |
+
self.down_proj = nn.Linear(expansion_ratio * d_model, d_model, bias=False)
|
| 229 |
+
self.down_proj._is_residual = True
|
| 230 |
+
|
| 231 |
+
def forward(self, x):
|
| 232 |
+
return self.down_proj(self.act(self.up_proj(x)))
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
class MPTBlock(nn.Module):
|
| 236 |
+
def __init__(self, layer_id: int, args):
|
| 237 |
+
super().__init__()
|
| 238 |
+
self.n_heads = args.n_heads
|
| 239 |
+
self.dim = args.d_model
|
| 240 |
+
self.head_dim = args.d_model // args.n_heads
|
| 241 |
+
self.attn = MPTAttentionFused(args)
|
| 242 |
+
self.ffn = MPTMLP(d_model=args.d_model, expansion_ratio=4)
|
| 243 |
+
self.layer_id = layer_id
|
| 244 |
+
self.norm_1 = LPLayerNorm(args.d_model, eps=1e-6)
|
| 245 |
+
self.norm_2 = LPLayerNorm(args.d_model, eps=1e-6)
|
| 246 |
+
|
| 247 |
+
def forward(
|
| 248 |
+
self,
|
| 249 |
+
x: torch.Tensor,
|
| 250 |
+
start_pos: int,
|
| 251 |
+
mask: Optional[torch.Tensor],
|
| 252 |
+
):
|
| 253 |
+
h = x + self.attn.forward(self.norm_1(x), start_pos, mask)
|
| 254 |
+
out = h + self.ffn.forward(self.norm_2(h))
|
| 255 |
+
return out
|
| 256 |
+
|
| 257 |
+
|
| 258 |
+
class Transformer(nn.Module):
|
| 259 |
+
def __init__(self, params):
|
| 260 |
+
super().__init__()
|
| 261 |
+
self.params = params
|
| 262 |
+
self.vocab_size = params.vocab_size
|
| 263 |
+
self.n_layers = params.n_layers
|
| 264 |
+
|
| 265 |
+
self.wte = SharedEmbedding(params.vocab_size, params.d_model)
|
| 266 |
+
|
| 267 |
+
self.blocks = torch.nn.ModuleList()
|
| 268 |
+
for layer_id in range(params.n_layers):
|
| 269 |
+
self.blocks.append(MPTBlock(layer_id, params))
|
| 270 |
+
|
| 271 |
+
self.norm_f = LPLayerNorm(params.d_model, eps=1e-6)
|
| 272 |
+
|
| 273 |
+
@torch.inference_mode()
|
| 274 |
+
def forward(self, tokens: torch.Tensor, start_pos: int):
|
| 275 |
+
_bsz, seqlen = tokens.shape
|
| 276 |
+
h = self.wte(tokens)
|
| 277 |
+
|
| 278 |
+
mask = None
|
| 279 |
+
if seqlen > 1:
|
| 280 |
+
mask = torch.full(
|
| 281 |
+
(1, 1, seqlen, seqlen), float("-inf"), device=tokens.device
|
| 282 |
+
)
|
| 283 |
+
mask = torch.triu(mask, diagonal=start_pos + 1).type_as(h)
|
| 284 |
+
for layer in self.blocks:
|
| 285 |
+
h = layer(h, start_pos, mask)
|
| 286 |
+
h = self.norm_f(h)
|
| 287 |
+
return h
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
class MPTForCausalLM(nn.Module):
|
| 291 |
+
def __init__(self, params):
|
| 292 |
+
super().__init__()
|
| 293 |
+
self.config = params
|
| 294 |
+
self.transformer = Transformer(params)
|
| 295 |
+
if params.no_bias:
|
| 296 |
+
for module in self.modules():
|
| 297 |
+
if hasattr(module, "bias") and isinstance(module.bias, nn.Parameter):
|
| 298 |
+
module.register_parameter("bias", None)
|
| 299 |
+
|
| 300 |
+
@torch.inference_mode()
|
| 301 |
+
def forward(self, tokens: torch.Tensor, start_pos: int):
|
| 302 |
+
h = self.transformer(tokens, start_pos)
|
| 303 |
+
output = self.transformer.wte(h, unembed=True) # only compute last logits
|
| 304 |
+
return output.float()
|
llm-awq/tinychat/models/nvila/builder.py
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 NVIDIA CORPORATION & AFFILIATES
|
| 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 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
#
|
| 15 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 16 |
+
|
| 17 |
+
import math
|
| 18 |
+
import os
|
| 19 |
+
import os.path as osp
|
| 20 |
+
import warnings
|
| 21 |
+
from dataclasses import asdict
|
| 22 |
+
from typing import Tuple
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
from huggingface_hub import file_exists, repo_exists
|
| 26 |
+
from huggingface_hub.utils import HFValidationError
|
| 27 |
+
from transformers import (
|
| 28 |
+
AutoConfig,
|
| 29 |
+
AutoModelForCausalLM,
|
| 30 |
+
AutoTokenizer,
|
| 31 |
+
PretrainedConfig,
|
| 32 |
+
PreTrainedModel,
|
| 33 |
+
PreTrainedTokenizer,
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
from llava.constants import MEDIA_TOKENS
|
| 37 |
+
from llava.model.utils import packing
|
| 38 |
+
from llava.utils.logging import logger
|
| 39 |
+
from llava.utils.tokenizer import infer_stop_tokens
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def has_tokenizer(repo_id_or_path: str) -> bool:
|
| 43 |
+
# Check if the tokenizer is in a local directory
|
| 44 |
+
if osp.exists(osp.join(repo_id_or_path, "tokenizer_config.json")):
|
| 45 |
+
return True
|
| 46 |
+
|
| 47 |
+
# Check if the tokenizer is in a Hugging Face Hub repo
|
| 48 |
+
try:
|
| 49 |
+
return repo_exists(repo_id_or_path) and file_exists(
|
| 50 |
+
repo_id_or_path, "tokenizer_config.json"
|
| 51 |
+
)
|
| 52 |
+
except HFValidationError:
|
| 53 |
+
return False
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def context_length_extension(config):
|
| 57 |
+
orig_ctx_len = getattr(config, "max_position_embeddings", None)
|
| 58 |
+
model_max_length = getattr(config, "model_max_length", None)
|
| 59 |
+
if orig_ctx_len and model_max_length > orig_ctx_len:
|
| 60 |
+
print(f"Scaling RoPE from {orig_ctx_len} to {model_max_length}")
|
| 61 |
+
scaling_factor = float(math.ceil(model_max_length / orig_ctx_len))
|
| 62 |
+
config.rope_scaling = {"type": "linear", "factor": scaling_factor}
|
| 63 |
+
return config
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def build_llm_and_tokenizer(
|
| 67 |
+
model_name_or_path: str,
|
| 68 |
+
config: PretrainedConfig,
|
| 69 |
+
attn_implementation=None,
|
| 70 |
+
model_max_length=None,
|
| 71 |
+
*args,
|
| 72 |
+
**kwargs,
|
| 73 |
+
) -> Tuple[PreTrainedModel, PreTrainedTokenizer]:
|
| 74 |
+
# print(model_name_or_path)
|
| 75 |
+
llm_cfg = AutoConfig.from_pretrained(model_name_or_path)
|
| 76 |
+
llm_cfg._attn_implementation = attn_implementation
|
| 77 |
+
llm_cfg.model_max_length = model_max_length
|
| 78 |
+
if model_max_length is not None:
|
| 79 |
+
context_length_extension(llm_cfg)
|
| 80 |
+
|
| 81 |
+
# Quantization related
|
| 82 |
+
quantization_restore_from_checkpoint = False
|
| 83 |
+
if kwargs.get("quantize_model_class") is not None:
|
| 84 |
+
assert kwargs.get("model_args") is not None
|
| 85 |
+
quantize_model_class = kwargs.pop("quantize_model_class", None)
|
| 86 |
+
model_args = kwargs.pop("model_args", None)
|
| 87 |
+
|
| 88 |
+
if (
|
| 89 |
+
quantize_model_class == "QLlamaForCausalLM"
|
| 90 |
+
): # TODO: Also change the name of this class
|
| 91 |
+
from .qllama import QLlamaConfig
|
| 92 |
+
|
| 93 |
+
llm_cfg.architectures = "QLlamaForCausalLM"
|
| 94 |
+
_attn_implementation = llm_cfg._attn_implementation
|
| 95 |
+
llm_cfg = QLlamaConfig(**llm_cfg.to_dict())
|
| 96 |
+
llm_cfg._attn_implementation = _attn_implementation
|
| 97 |
+
elif (
|
| 98 |
+
quantize_model_class == "QMemLlamaForCausalLM"
|
| 99 |
+
): # TODO: Also change the name of this class
|
| 100 |
+
from .qmemllama import QMemLlamaConfig
|
| 101 |
+
|
| 102 |
+
llm_cfg.architectures = "QMemLlamaForCausalLM"
|
| 103 |
+
llm_cfg = QMemLlamaConfig(**llm_cfg.to_dict())
|
| 104 |
+
elif quantize_model_class == "FP8LinearQwen2ForCausalLM":
|
| 105 |
+
from .configuration_quantize import QuantizationConfig
|
| 106 |
+
from .fp8linearqwen2 import FP8LinearQwen2Config
|
| 107 |
+
|
| 108 |
+
llm_cfg.architectures = "FP8LinearQwen2ForCausalLM"
|
| 109 |
+
coat_fp8_args = QuantizationConfig(**asdict(model_args))
|
| 110 |
+
|
| 111 |
+
# Remove the quantization args from llm_cfg and make it a independent config
|
| 112 |
+
model_args_dict = asdict(model_args)
|
| 113 |
+
for key in asdict(coat_fp8_args).keys():
|
| 114 |
+
model_args_dict.pop(key, None)
|
| 115 |
+
|
| 116 |
+
llm_cfg.coat_fp8_args = asdict(coat_fp8_args)
|
| 117 |
+
_attn_implementation = llm_cfg._attn_implementation
|
| 118 |
+
|
| 119 |
+
llm_cfg = FP8LinearQwen2Config(**llm_cfg.to_dict())
|
| 120 |
+
llm_cfg._attn_implementation = _attn_implementation
|
| 121 |
+
|
| 122 |
+
elif quantize_model_class == "FP8ActivationQwen2ForCausalLM":
|
| 123 |
+
from ..coat.activation.models._fp8_quantization_config import (
|
| 124 |
+
QuantizationConfig,
|
| 125 |
+
)
|
| 126 |
+
from .fp8activationqwen2 import FP8ActivationQwen2Config
|
| 127 |
+
|
| 128 |
+
quantization_restore_from_checkpoint = True
|
| 129 |
+
|
| 130 |
+
llm_cfg.architectures = "FP8ActivationQwen2ForCausalLM"
|
| 131 |
+
coat_fp8_args = QuantizationConfig(**asdict(model_args))
|
| 132 |
+
|
| 133 |
+
# Remove the quantization args from llm_cfg and make it a independent config
|
| 134 |
+
model_args_dict = asdict(model_args)
|
| 135 |
+
for key in asdict(coat_fp8_args).keys():
|
| 136 |
+
model_args_dict.pop(key, None)
|
| 137 |
+
|
| 138 |
+
llm_cfg.coat_fp8_args = asdict(coat_fp8_args)
|
| 139 |
+
_attn_implementation = llm_cfg._attn_implementation
|
| 140 |
+
|
| 141 |
+
llm_cfg = FP8ActivationQwen2Config(**llm_cfg.to_dict())
|
| 142 |
+
llm_cfg._attn_implementation = _attn_implementation
|
| 143 |
+
|
| 144 |
+
elif quantize_model_class == "FP8ActivationResidualQwen2ForCausalLM":
|
| 145 |
+
from ..coat.activation.models._fp8_quantization_config import (
|
| 146 |
+
QuantizationConfig,
|
| 147 |
+
)
|
| 148 |
+
from .fp8activationresidualqwen2 import FP8ActivationResidualQwen2Config
|
| 149 |
+
|
| 150 |
+
quantization_restore_from_checkpoint = True
|
| 151 |
+
|
| 152 |
+
llm_cfg.architectures = "FP8ActivationResidualQwen2ForCausalLM"
|
| 153 |
+
coat_fp8_args = QuantizationConfig(**asdict(model_args))
|
| 154 |
+
|
| 155 |
+
# Remove the quantization args from llm_cfg and make it a independent config
|
| 156 |
+
model_args_dict = asdict(model_args)
|
| 157 |
+
for key in asdict(coat_fp8_args).keys():
|
| 158 |
+
model_args_dict.pop(key, None)
|
| 159 |
+
|
| 160 |
+
llm_cfg.coat_fp8_args = asdict(coat_fp8_args)
|
| 161 |
+
_attn_implementation = llm_cfg._attn_implementation
|
| 162 |
+
|
| 163 |
+
llm_cfg = FP8ActivationResidualQwen2Config(**llm_cfg.to_dict())
|
| 164 |
+
llm_cfg._attn_implementation = _attn_implementation
|
| 165 |
+
else:
|
| 166 |
+
raise ValueError(
|
| 167 |
+
f"{quantize_model_class} is not supported quantize_model_class."
|
| 168 |
+
)
|
| 169 |
+
|
| 170 |
+
kwargs.pop("quantize_model_class", None)
|
| 171 |
+
|
| 172 |
+
if quantize_model_class in [
|
| 173 |
+
"FP8LinearQwen2ForCausalLM",
|
| 174 |
+
"FP8ActivationQwen2ForCausalLM",
|
| 175 |
+
"FP8ActivationResidualQwen2ForCausalLM",
|
| 176 |
+
]: # Remove the quantization args from llm_cfg and make it a independent config
|
| 177 |
+
llm_cfg.update(model_args_dict)
|
| 178 |
+
else:
|
| 179 |
+
llm_cfg.update(asdict(model_args))
|
| 180 |
+
# print(model_args)
|
| 181 |
+
|
| 182 |
+
if quantization_restore_from_checkpoint:
|
| 183 |
+
fp8_model_name_or_path = kwargs.pop("fp8_llm_cfg", None)
|
| 184 |
+
|
| 185 |
+
llm = AutoModelForCausalLM.from_pretrained(
|
| 186 |
+
fp8_model_name_or_path,
|
| 187 |
+
config=llm_cfg,
|
| 188 |
+
torch_dtype=eval(config.model_dtype),
|
| 189 |
+
*args,
|
| 190 |
+
**kwargs,
|
| 191 |
+
)
|
| 192 |
+
|
| 193 |
+
else:
|
| 194 |
+
llm = AutoModelForCausalLM.from_pretrained(
|
| 195 |
+
model_name_or_path,
|
| 196 |
+
config=llm_cfg,
|
| 197 |
+
torch_dtype=eval(config.model_dtype),
|
| 198 |
+
*args,
|
| 199 |
+
**kwargs,
|
| 200 |
+
)
|
| 201 |
+
packing.patch(llm)
|
| 202 |
+
|
| 203 |
+
# Locate the tokenizer.
|
| 204 |
+
llm_path = model_name_or_path
|
| 205 |
+
if not has_tokenizer(llm_path):
|
| 206 |
+
llm_path = osp.join(llm_path, "llm")
|
| 207 |
+
if not has_tokenizer(llm_path):
|
| 208 |
+
raise ValueError(f"Cannot find tokenizer in {llm_path}.")
|
| 209 |
+
|
| 210 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 211 |
+
llm_path, padding_side="right", use_fast=False, legacy=False
|
| 212 |
+
)
|
| 213 |
+
if model_max_length is not None:
|
| 214 |
+
tokenizer.model_max_length = model_max_length
|
| 215 |
+
|
| 216 |
+
# Load chat template if specified.
|
| 217 |
+
if getattr(config, "chat_template", None) is not None:
|
| 218 |
+
logger.info(f"Using chat template: {config.chat_template}")
|
| 219 |
+
fpath = os.path.join(
|
| 220 |
+
os.path.dirname(__file__), "chat_templates", f"{config.chat_template}.jinja"
|
| 221 |
+
)
|
| 222 |
+
with open(fpath) as fd:
|
| 223 |
+
chat_template = fd.read()
|
| 224 |
+
tokenizer.chat_template = chat_template.replace(" ", "").replace("\n", "")
|
| 225 |
+
|
| 226 |
+
# Set stop tokens for the tokenizer
|
| 227 |
+
tokenizer.stop_tokens = infer_stop_tokens(tokenizer)
|
| 228 |
+
tokenizer.stop_token_ids = tokenizer.convert_tokens_to_ids(tokenizer.stop_tokens)
|
| 229 |
+
|
| 230 |
+
# Add media tokens to the tokenizer
|
| 231 |
+
tokenizer.media_tokens = MEDIA_TOKENS
|
| 232 |
+
tokenizer.media_token_ids = {}
|
| 233 |
+
for name, token in MEDIA_TOKENS.items():
|
| 234 |
+
tokenizer.add_tokens([token], special_tokens=True)
|
| 235 |
+
tokenizer.media_token_ids[name] = tokenizer.convert_tokens_to_ids(token)
|
| 236 |
+
|
| 237 |
+
# TODO(ligeng): is this necessary for llava?
|
| 238 |
+
config.hidden_size = llm.config.hidden_size
|
| 239 |
+
return llm, tokenizer
|
| 240 |
+
|
| 241 |
+
|
| 242 |
+
def build_tokenizer(
|
| 243 |
+
model_name_or_path: str,
|
| 244 |
+
config: PretrainedConfig,
|
| 245 |
+
attn_implementation=None,
|
| 246 |
+
model_max_length=None,
|
| 247 |
+
*args,
|
| 248 |
+
**kwargs,
|
| 249 |
+
) -> Tuple[PreTrainedModel, PreTrainedTokenizer]:
|
| 250 |
+
# print(model_name_or_path)
|
| 251 |
+
llm_cfg = AutoConfig.from_pretrained(model_name_or_path)
|
| 252 |
+
llm_cfg._attn_implementation = attn_implementation
|
| 253 |
+
llm_cfg.model_max_length = model_max_length
|
| 254 |
+
if model_max_length is not None:
|
| 255 |
+
context_length_extension(llm_cfg)
|
| 256 |
+
|
| 257 |
+
# Locate the tokenizer.
|
| 258 |
+
llm_path = model_name_or_path
|
| 259 |
+
if not has_tokenizer(llm_path):
|
| 260 |
+
llm_path = osp.join(llm_path, "llm")
|
| 261 |
+
if not has_tokenizer(llm_path):
|
| 262 |
+
raise ValueError(f"Cannot find tokenizer in {llm_path}.")
|
| 263 |
+
|
| 264 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 265 |
+
llm_path, padding_side="right", use_fast=False, legacy=False
|
| 266 |
+
)
|
| 267 |
+
if model_max_length is not None:
|
| 268 |
+
tokenizer.model_max_length = model_max_length
|
| 269 |
+
|
| 270 |
+
# Load chat template if specified.
|
| 271 |
+
if getattr(config, "chat_template", None) is not None:
|
| 272 |
+
logger.info(f"Using chat template: {config.chat_template}")
|
| 273 |
+
fpath = os.path.join(
|
| 274 |
+
os.path.dirname(__file__), "chat_templates", f"{config.chat_template}.jinja"
|
| 275 |
+
)
|
| 276 |
+
with open(fpath) as fd:
|
| 277 |
+
chat_template = fd.read()
|
| 278 |
+
tokenizer.chat_template = chat_template.replace(" ", "").replace("\n", "")
|
| 279 |
+
|
| 280 |
+
# Set stop tokens for the tokenizer
|
| 281 |
+
tokenizer.stop_tokens = infer_stop_tokens(tokenizer)
|
| 282 |
+
tokenizer.stop_token_ids = tokenizer.convert_tokens_to_ids(tokenizer.stop_tokens)
|
| 283 |
+
|
| 284 |
+
# Add media tokens to the tokenizer
|
| 285 |
+
tokenizer.media_tokens = MEDIA_TOKENS
|
| 286 |
+
tokenizer.media_token_ids = {}
|
| 287 |
+
for name, token in MEDIA_TOKENS.items():
|
| 288 |
+
tokenizer.add_tokens([token], special_tokens=True)
|
| 289 |
+
tokenizer.media_token_ids[name] = tokenizer.convert_tokens_to_ids(token)
|
| 290 |
+
|
| 291 |
+
return tokenizer
|
llm-awq/tinychat/models/nvila/configuration_llava.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024 NVIDIA CORPORATION & AFFILIATES
|
| 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 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
#
|
| 15 |
+
# SPDX-License-Identifier: Apache-2.0
|
| 16 |
+
|
| 17 |
+
from typing import Optional
|
| 18 |
+
|
| 19 |
+
from transformers import PretrainedConfig
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class LlavaConfig(PretrainedConfig):
|
| 23 |
+
model_type = "llava"
|
| 24 |
+
|
| 25 |
+
def __init__(
|
| 26 |
+
self,
|
| 27 |
+
llm_cfg=None,
|
| 28 |
+
vision_tower_cfg=None,
|
| 29 |
+
mm_projector_cfg=None,
|
| 30 |
+
architectures=None,
|
| 31 |
+
resume_path=None,
|
| 32 |
+
hidden_size=None,
|
| 33 |
+
mm_hidden_size=None,
|
| 34 |
+
image_aspect_ratio=None,
|
| 35 |
+
num_video_frames=None,
|
| 36 |
+
fps=None,
|
| 37 |
+
mm_vision_select_layer=None,
|
| 38 |
+
mm_vision_select_feature=None,
|
| 39 |
+
mm_use_im_start_end=False,
|
| 40 |
+
mm_use_im_patch_token=False,
|
| 41 |
+
mm_projector_lr=None,
|
| 42 |
+
vision_tower_lr=None,
|
| 43 |
+
vision_resolution=None,
|
| 44 |
+
interpolate_mode=None,
|
| 45 |
+
s2=None,
|
| 46 |
+
dynamic_s2=None,
|
| 47 |
+
s2_scales=None,
|
| 48 |
+
s2_max_split_size=None,
|
| 49 |
+
s2_resize_output_to_scale_idx=0,
|
| 50 |
+
min_tiles: Optional[int] = 1,
|
| 51 |
+
max_tiles: Optional[int] = 12,
|
| 52 |
+
num_time_tokens=None,
|
| 53 |
+
time_token_format=None,
|
| 54 |
+
image_encoder: str = '{"_target_": "llava.model.encoders.BasicImageEncoder"}',
|
| 55 |
+
video_encoder: str = '{"_target_": "llava.model.encoders.BasicVideoEncoder"}',
|
| 56 |
+
**kwargs,
|
| 57 |
+
):
|
| 58 |
+
super().__init__()
|
| 59 |
+
self.architectures = architectures
|
| 60 |
+
self.llm_cfg = llm_cfg
|
| 61 |
+
self.vision_tower_cfg = vision_tower_cfg
|
| 62 |
+
self.mm_projector_cfg = mm_projector_cfg
|
| 63 |
+
self.resume_path = resume_path
|
| 64 |
+
|
| 65 |
+
self.hidden_size = hidden_size
|
| 66 |
+
self.mm_hidden_size = mm_hidden_size
|
| 67 |
+
self.image_aspect_ratio = image_aspect_ratio
|
| 68 |
+
self.num_video_frames = num_video_frames
|
| 69 |
+
self.fps = fps
|
| 70 |
+
self.mm_vision_select_layer = mm_vision_select_layer
|
| 71 |
+
self.mm_vision_select_feature = mm_vision_select_feature
|
| 72 |
+
self.mm_use_im_start_end = mm_use_im_start_end
|
| 73 |
+
self.mm_use_im_patch_token = mm_use_im_patch_token
|
| 74 |
+
self.mm_projector_lr = mm_projector_lr
|
| 75 |
+
self.vision_tower_lr = vision_tower_lr
|
| 76 |
+
self.vision_resolution = vision_resolution
|
| 77 |
+
self.interpolate_mode = interpolate_mode
|
| 78 |
+
self.s2 = s2
|
| 79 |
+
self.dynamic_s2 = dynamic_s2
|
| 80 |
+
self.s2_scales = s2_scales
|
| 81 |
+
self.s2_max_split_size = s2_max_split_size
|
| 82 |
+
self.s2_resize_output_to_scale_idx = s2_resize_output_to_scale_idx
|
| 83 |
+
self.min_tiles = min_tiles
|
| 84 |
+
self.max_tiles = max_tiles
|
| 85 |
+
self.num_time_tokens = num_time_tokens
|
| 86 |
+
self.time_token_format = time_token_format
|
| 87 |
+
|
| 88 |
+
self.image_encoder = image_encoder
|
| 89 |
+
self.video_encoder = video_encoder
|
llm-awq/tinychat/models/nvila/llava_arch.py
ADDED
|
@@ -0,0 +1,909 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 Haotian Liu
|
| 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 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import copy
|
| 16 |
+
import json
|
| 17 |
+
import logging
|
| 18 |
+
import os
|
| 19 |
+
import os.path as osp
|
| 20 |
+
import warnings
|
| 21 |
+
from abc import ABC
|
| 22 |
+
from collections import OrderedDict, defaultdict, deque
|
| 23 |
+
from itertools import chain
|
| 24 |
+
from typing import Any, Dict, List, Optional, Tuple, Union
|
| 25 |
+
|
| 26 |
+
import torch
|
| 27 |
+
import torch.distributed as dist
|
| 28 |
+
import torch.nn.functional as F
|
| 29 |
+
from einops import rearrange
|
| 30 |
+
from hydra.utils import instantiate
|
| 31 |
+
from transformers import AutoConfig, GenerationConfig, PreTrainedModel
|
| 32 |
+
from transformers.modeling_utils import ContextManagers, no_init_weights
|
| 33 |
+
from time import time
|
| 34 |
+
from llava.constants import DEFAULT_IMAGE_TOKEN, IGNORE_INDEX
|
| 35 |
+
from llava.mm_utils import process_image, process_images
|
| 36 |
+
from llava.model.configuration_llava import LlavaConfig
|
| 37 |
+
from llava.model.language_model.builder import build_llm_and_tokenizer
|
| 38 |
+
from llava.model.multimodal_encoder.builder import build_vision_tower
|
| 39 |
+
from llava.model.multimodal_projector.builder import build_mm_projector
|
| 40 |
+
from llava.model.utils import get_model_config
|
| 41 |
+
|
| 42 |
+
# from llava.train.sequence_parallel import get_pg_manager
|
| 43 |
+
from llava.utils import distributed as dist
|
| 44 |
+
from llava.utils.media import extract_media
|
| 45 |
+
from llava.utils.tokenizer import tokenize_conversation
|
| 46 |
+
from .builder import build_tokenizer
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class LlavaMetaModel(ABC):
|
| 50 |
+
def init_vlm(self, config, *args, **kwargs):
|
| 51 |
+
# TODO(ligeng): figure out how from_config and from_pretrained works in HF implementation.
|
| 52 |
+
if (
|
| 53 |
+
hasattr(self, "llm")
|
| 54 |
+
or hasattr(self, "vision_tower")
|
| 55 |
+
or hasattr(self, "mm_projector")
|
| 56 |
+
):
|
| 57 |
+
# already initialized, skipped
|
| 58 |
+
return
|
| 59 |
+
|
| 60 |
+
model_dtype = getattr(config, "model_dtype", "torch.float16")
|
| 61 |
+
if not hasattr(config, "model_dtype"):
|
| 62 |
+
warnings.warn(
|
| 63 |
+
"model_dtype not found in config, defaulting to torch.float16."
|
| 64 |
+
)
|
| 65 |
+
config.model_dtype = model_dtype
|
| 66 |
+
|
| 67 |
+
cfgs = get_model_config(config)
|
| 68 |
+
if len(cfgs) == 3:
|
| 69 |
+
self.llm_cfg, vision_tower_cfg, mm_projector_cfg = cfgs
|
| 70 |
+
else:
|
| 71 |
+
raise ValueError(
|
| 72 |
+
"`llm_cfg` `mm_projector_cfg` `vision_tower_cfg` not found in the config."
|
| 73 |
+
)
|
| 74 |
+
self.tokenizer = build_tokenizer(self.llm_cfg, config, *args, **kwargs)
|
| 75 |
+
self.vision_tower = build_vision_tower(vision_tower_cfg, config)
|
| 76 |
+
self.mm_projector = build_mm_projector(mm_projector_cfg, config)
|
| 77 |
+
|
| 78 |
+
self.encoders = {}
|
| 79 |
+
for name in ["image", "video"]:
|
| 80 |
+
config = getattr(self.config, f"{name}_encoder")
|
| 81 |
+
if isinstance(config, str):
|
| 82 |
+
config = json.loads(config)
|
| 83 |
+
self.encoders[name] = instantiate(config, parent=self)
|
| 84 |
+
|
| 85 |
+
self.post_config()
|
| 86 |
+
self.is_loaded = True
|
| 87 |
+
|
| 88 |
+
assert (
|
| 89 |
+
self.vision_tower is not None or self.mm_projector is not None
|
| 90 |
+
), "At least one of the components must be instantiated."
|
| 91 |
+
|
| 92 |
+
@classmethod
|
| 93 |
+
def load_from_config(cls, model_path_or_config, *args, **kwargs):
|
| 94 |
+
pass
|
| 95 |
+
|
| 96 |
+
## FIXME we will use this function to load model in the future
|
| 97 |
+
@classmethod
|
| 98 |
+
def load_pretrained(cls, model_path_or_config, *args, **kwargs):
|
| 99 |
+
kwargs.pop("config", None)
|
| 100 |
+
|
| 101 |
+
if isinstance(model_path_or_config, str):
|
| 102 |
+
config = AutoConfig.from_pretrained(model_path_or_config)
|
| 103 |
+
elif isinstance(model_path_or_config, LlavaConfig):
|
| 104 |
+
config = model_path_or_config
|
| 105 |
+
else:
|
| 106 |
+
raise NotImplementedError(
|
| 107 |
+
f"wrong type, {type(model_path_or_config)} \
|
| 108 |
+
{isinstance(model_path_or_config, LlavaConfig)}"
|
| 109 |
+
)
|
| 110 |
+
|
| 111 |
+
model_dtype = getattr(config, "model_dtype", "torch.float16")
|
| 112 |
+
if not hasattr(config, "model_dtype"):
|
| 113 |
+
warnings.warn(
|
| 114 |
+
"model_dtype not found in config, defaulting to torch.float16."
|
| 115 |
+
)
|
| 116 |
+
config.model_dtype = model_dtype
|
| 117 |
+
|
| 118 |
+
cfgs = get_model_config(config)
|
| 119 |
+
if len(cfgs) == 3:
|
| 120 |
+
llm_cfg, vision_tower_cfg, mm_projector_cfg = cfgs
|
| 121 |
+
else:
|
| 122 |
+
raise ValueError(
|
| 123 |
+
"`llm_cfg` `mm_projector_cfg` `vision_tower_cfg` not found in the config."
|
| 124 |
+
)
|
| 125 |
+
|
| 126 |
+
# print(llm_cfg, vision_tower_cfg, mm_projector_cfg); input("DEBUG load_pretrained")
|
| 127 |
+
init_context = [
|
| 128 |
+
no_init_weights(_enable=True),
|
| 129 |
+
]
|
| 130 |
+
# print("Before Init Context")
|
| 131 |
+
# if hasattr(config, "deepspeed") and "mics" in config.deepspeed:
|
| 132 |
+
# print("Using MiCS_Init")
|
| 133 |
+
# import deepspeed
|
| 134 |
+
# init_context.append(deepspeed.zero.MiCS_Init(config_dict_or_path=config.deepspeed))
|
| 135 |
+
with ContextManagers(init_context):
|
| 136 |
+
vlm = cls(config, *args, **kwargs)
|
| 137 |
+
# print(llm_cfg, vision_tower_cfg, mm_projector_cfg); input("DEBUG load_pretrained finish")
|
| 138 |
+
|
| 139 |
+
if (
|
| 140 |
+
hasattr(vlm, "llm")
|
| 141 |
+
or hasattr(vlm, "vision_tower")
|
| 142 |
+
or hasattr(vlm, "mm_projector")
|
| 143 |
+
):
|
| 144 |
+
if vlm.is_loaded:
|
| 145 |
+
return vlm
|
| 146 |
+
|
| 147 |
+
vlm.llm, vlm.tokenizer = build_llm_and_tokenizer(
|
| 148 |
+
llm_cfg, config, *args, **kwargs
|
| 149 |
+
)
|
| 150 |
+
vlm.vision_tower = build_vision_tower(vision_tower_cfg, config)
|
| 151 |
+
vlm.mm_projector = build_mm_projector(mm_projector_cfg, config)
|
| 152 |
+
|
| 153 |
+
self.post_config()
|
| 154 |
+
self.is_loaded = True
|
| 155 |
+
|
| 156 |
+
# FIXME(ligeng, yunhao): llm should never be none here.
|
| 157 |
+
assert (
|
| 158 |
+
vlm.llm is not None
|
| 159 |
+
or vlm.vision_tower is not None
|
| 160 |
+
or vlm.mm_projector is not None
|
| 161 |
+
), "At least one of the components must be instantiated."
|
| 162 |
+
return vlm
|
| 163 |
+
|
| 164 |
+
## FIXME we will use this function to save the model in the future
|
| 165 |
+
def save_pretrained(self, output_dir, state_dict=None):
|
| 166 |
+
if state_dict is None:
|
| 167 |
+
# other wise fetch from deepspeed
|
| 168 |
+
# state_dict = accelerator.get_state_dict(is_deepspeed_enabled)
|
| 169 |
+
state_dict = self.state_dict()
|
| 170 |
+
|
| 171 |
+
if getattr(self, "tokenizer", None):
|
| 172 |
+
self.tokenizer.save_pretrained(osp.join(output_dir, "llm"))
|
| 173 |
+
|
| 174 |
+
if self.get_llm():
|
| 175 |
+
print(f"saving llm to {osp.join(output_dir, 'llm')}")
|
| 176 |
+
self.llm.config._name_or_path = osp.join(output_dir, "llm")
|
| 177 |
+
llm_state_dict = OrderedDict(
|
| 178 |
+
{k.split("llm.")[-1]: v for k, v in state_dict.items() if "llm" in k}
|
| 179 |
+
)
|
| 180 |
+
self.llm.save_pretrained(
|
| 181 |
+
os.path.join(output_dir, "llm"), state_dict=llm_state_dict
|
| 182 |
+
)
|
| 183 |
+
self.config.llm_cfg = self.llm.config
|
| 184 |
+
|
| 185 |
+
if self.get_vision_tower():
|
| 186 |
+
print(f"saving vision_tower to {osp.join(output_dir, 'vision_tower')}")
|
| 187 |
+
self.vision_tower.config._name_or_path = osp.join(
|
| 188 |
+
output_dir, "vision_tower"
|
| 189 |
+
)
|
| 190 |
+
vision_tower_state_dict = OrderedDict(
|
| 191 |
+
{
|
| 192 |
+
k.split("vision_tower.vision_tower.")[-1]: v
|
| 193 |
+
for k, v in state_dict.items()
|
| 194 |
+
if "vision_tower" in k
|
| 195 |
+
}
|
| 196 |
+
)
|
| 197 |
+
self.vision_tower.vision_tower.save_pretrained(
|
| 198 |
+
os.path.join(output_dir, "vision_tower"),
|
| 199 |
+
state_dict=vision_tower_state_dict,
|
| 200 |
+
)
|
| 201 |
+
self.vision_tower.image_processor.save_pretrained(
|
| 202 |
+
os.path.join(output_dir, "vision_tower")
|
| 203 |
+
)
|
| 204 |
+
self.config.vision_tower_cfg = self.vision_tower.config
|
| 205 |
+
if hasattr(self.config.vision_tower_cfg, "auto_map"):
|
| 206 |
+
if "radio" not in self.get_vision_tower().__class__.__name__.lower():
|
| 207 |
+
delattr(self.config.vision_tower_cfg, "auto_map")
|
| 208 |
+
|
| 209 |
+
if self.get_mm_projector():
|
| 210 |
+
print(f"saving mm_projector to {osp.join(output_dir, 'mm_projector')}")
|
| 211 |
+
self.mm_projector.config._name_or_path = osp.join(
|
| 212 |
+
output_dir, "mm_projector"
|
| 213 |
+
)
|
| 214 |
+
mm_projector_state_dict = OrderedDict(
|
| 215 |
+
{
|
| 216 |
+
k.split("mm_projector.")[-1]: v
|
| 217 |
+
for k, v in state_dict.items()
|
| 218 |
+
if "mm_projector" in k
|
| 219 |
+
}
|
| 220 |
+
)
|
| 221 |
+
self.mm_projector.save_pretrained(
|
| 222 |
+
os.path.join(output_dir, "mm_projector"),
|
| 223 |
+
state_dict=mm_projector_state_dict,
|
| 224 |
+
)
|
| 225 |
+
self.config.mm_projector_cfg = self.mm_projector.config
|
| 226 |
+
## update and save top-level config
|
| 227 |
+
self.config._name_or_path = output_dir
|
| 228 |
+
self.config.architectures = [self.__class__.__name__]
|
| 229 |
+
self.config.save_pretrained(output_dir)
|
| 230 |
+
|
| 231 |
+
def get_llm(self):
|
| 232 |
+
llm = getattr(self, "llm", None)
|
| 233 |
+
if type(llm) is list:
|
| 234 |
+
llm = llm[0]
|
| 235 |
+
return llm
|
| 236 |
+
|
| 237 |
+
def get_lm_head(self):
|
| 238 |
+
lm_head = getattr(self.get_llm(), "lm_head", None)
|
| 239 |
+
return lm_head
|
| 240 |
+
|
| 241 |
+
def get_vision_tower(self):
|
| 242 |
+
vision_tower = getattr(self, "vision_tower", None)
|
| 243 |
+
if type(vision_tower) is list:
|
| 244 |
+
vision_tower = vision_tower[0]
|
| 245 |
+
return vision_tower
|
| 246 |
+
|
| 247 |
+
def get_mm_projector(self):
|
| 248 |
+
mm_projector = getattr(self, "mm_projector", None)
|
| 249 |
+
if type(mm_projector) is list:
|
| 250 |
+
mm_projector = mm_projector[0]
|
| 251 |
+
return mm_projector
|
| 252 |
+
|
| 253 |
+
def post_config(self):
|
| 254 |
+
|
| 255 |
+
if getattr(self.config, "vision_tower_cfg", None) is None:
|
| 256 |
+
self.config.vision_tower_cfg = self.vision_tower.config
|
| 257 |
+
if getattr(self.config, "mm_projector_cfg", None) is None:
|
| 258 |
+
self.config.mm_projector_cfg = self.mm_projector.config
|
| 259 |
+
|
| 260 |
+
@staticmethod
|
| 261 |
+
def merge_chessboard(x, num_split_h, num_split_w):
|
| 262 |
+
"""
|
| 263 |
+
x: b * n * c or b * h * w * c
|
| 264 |
+
out: b * c * h * w
|
| 265 |
+
Assuming x contains num_split**2 sub-squares concatenated along batch dimension, merge the sub-squares back to the original whole square.
|
| 266 |
+
"""
|
| 267 |
+
B = x.shape[0]
|
| 268 |
+
if x.dim() == 3:
|
| 269 |
+
N = x.shape[1]
|
| 270 |
+
x = rearrange(x, "b (h w) c -> b c h w", h=int(N**0.5), w=int(N**0.5))
|
| 271 |
+
|
| 272 |
+
assert B % (num_split_h * num_split_w) == 0
|
| 273 |
+
b = B // (num_split_h * num_split_w)
|
| 274 |
+
|
| 275 |
+
x_merge = torch.cat(
|
| 276 |
+
[
|
| 277 |
+
torch.cat(
|
| 278 |
+
[
|
| 279 |
+
x[(i * num_split_w + j) * b : (i * num_split_w + j + 1) * b]
|
| 280 |
+
for j in range(num_split_w)
|
| 281 |
+
],
|
| 282 |
+
dim=-1,
|
| 283 |
+
)
|
| 284 |
+
for i in range(num_split_h)
|
| 285 |
+
],
|
| 286 |
+
dim=-2,
|
| 287 |
+
)
|
| 288 |
+
|
| 289 |
+
return x_merge
|
| 290 |
+
|
| 291 |
+
@staticmethod
|
| 292 |
+
def split_chessboard(x, num_split_h, num_split_w):
|
| 293 |
+
"""
|
| 294 |
+
x: b * c * h * w
|
| 295 |
+
out: b * c * h * w
|
| 296 |
+
Deividing x into num_split**2 sub-squares, and concatenate all the sub-squares on the batch dimension
|
| 297 |
+
"""
|
| 298 |
+
B, C, H, W = x.shape
|
| 299 |
+
assert H % num_split_h == 0 and W % num_split_w == 0
|
| 300 |
+
h, w = H // num_split_h, W // num_split_w
|
| 301 |
+
x_split = torch.cat(
|
| 302 |
+
[
|
| 303 |
+
x[:, :, i * h : (i + 1) * h, j * w : (j + 1) * w]
|
| 304 |
+
for i in range(num_split_h)
|
| 305 |
+
for j in range(num_split_w)
|
| 306 |
+
],
|
| 307 |
+
dim=0,
|
| 308 |
+
)
|
| 309 |
+
return x_split
|
| 310 |
+
|
| 311 |
+
def merge_features_for_dynamic_s2(self, image_features, block_sizes):
|
| 312 |
+
scales = self.get_vision_tower().scales
|
| 313 |
+
resize_output_to_scale_idx = self.get_vision_tower().resize_output_to_scale_idx
|
| 314 |
+
|
| 315 |
+
image_features_each_image = []
|
| 316 |
+
new_block_sizes = []
|
| 317 |
+
block_cnt = 0
|
| 318 |
+
for block_size_each_image in block_sizes:
|
| 319 |
+
if block_size_each_image is None:
|
| 320 |
+
cur_features = image_features[block_cnt : block_cnt + 1]
|
| 321 |
+
cur_features = rearrange(
|
| 322 |
+
cur_features,
|
| 323 |
+
"1 (h w) c -> 1 c h w",
|
| 324 |
+
h=int(cur_features.shape[1] ** 0.5),
|
| 325 |
+
)
|
| 326 |
+
cur_features = cur_features.repeat(1, len(scales), 1, 1)
|
| 327 |
+
image_features_each_image.append(cur_features)
|
| 328 |
+
new_block_sizes.append((1, 1))
|
| 329 |
+
block_cnt += 1
|
| 330 |
+
else:
|
| 331 |
+
cur_features_each_scale = []
|
| 332 |
+
for scale in scales[:-1]:
|
| 333 |
+
num_blocks_this_scale = (scale // scales[0]) ** 2
|
| 334 |
+
cur_features_each_scale.append(
|
| 335 |
+
self.merge_chessboard(
|
| 336 |
+
image_features[
|
| 337 |
+
block_cnt : block_cnt + num_blocks_this_scale
|
| 338 |
+
],
|
| 339 |
+
num_split_h=scale // scales[0],
|
| 340 |
+
num_split_w=scale // scales[0],
|
| 341 |
+
)
|
| 342 |
+
) # 1 * C * H * W
|
| 343 |
+
block_cnt += num_blocks_this_scale
|
| 344 |
+
num_blocks_last_scale = (
|
| 345 |
+
block_size_each_image[0] * block_size_each_image[1]
|
| 346 |
+
)
|
| 347 |
+
cur_features_each_scale.append(
|
| 348 |
+
self.merge_chessboard(
|
| 349 |
+
image_features[block_cnt : block_cnt + num_blocks_last_scale],
|
| 350 |
+
num_split_h=block_size_each_image[0],
|
| 351 |
+
num_split_w=block_size_each_image[1],
|
| 352 |
+
)
|
| 353 |
+
) # 1 * C * H * W
|
| 354 |
+
block_cnt += num_blocks_last_scale
|
| 355 |
+
|
| 356 |
+
# resize and concat features from different scales
|
| 357 |
+
output_size = cur_features_each_scale[resize_output_to_scale_idx].shape[
|
| 358 |
+
-2:
|
| 359 |
+
]
|
| 360 |
+
cur_features = torch.cat(
|
| 361 |
+
[
|
| 362 |
+
F.interpolate(
|
| 363 |
+
cur_features_each_scale[i].to(torch.float32),
|
| 364 |
+
size=output_size,
|
| 365 |
+
mode="area",
|
| 366 |
+
).to(cur_features_each_scale[i].dtype)
|
| 367 |
+
for i in range(len(cur_features_each_scale))
|
| 368 |
+
],
|
| 369 |
+
dim=1,
|
| 370 |
+
)
|
| 371 |
+
# cur_features = rearrange(cur_features, "1 c h w -> (h w) c")
|
| 372 |
+
|
| 373 |
+
image_features_each_image.append(cur_features)
|
| 374 |
+
|
| 375 |
+
if (
|
| 376 |
+
resize_output_to_scale_idx == len(scales) - 1
|
| 377 |
+
or resize_output_to_scale_idx == -1
|
| 378 |
+
):
|
| 379 |
+
new_block_sizes.append(block_size_each_image)
|
| 380 |
+
else:
|
| 381 |
+
new_block_sizes.append(
|
| 382 |
+
(
|
| 383 |
+
scales[resize_output_to_scale_idx] // scales[0],
|
| 384 |
+
scales[resize_output_to_scale_idx] // scales[0],
|
| 385 |
+
)
|
| 386 |
+
)
|
| 387 |
+
|
| 388 |
+
assert block_cnt == len(image_features)
|
| 389 |
+
|
| 390 |
+
return image_features_each_image, new_block_sizes
|
| 391 |
+
|
| 392 |
+
def encode_images(
|
| 393 |
+
self, images, block_sizes: Optional[Optional[Tuple[int, ...]]] = None
|
| 394 |
+
):
|
| 395 |
+
if block_sizes is None:
|
| 396 |
+
block_sizes = [None] * len(images)
|
| 397 |
+
if getattr(self.config, "dynamic_s2", False):
|
| 398 |
+
image_features = self.get_vision_tower()(images)
|
| 399 |
+
image_features, new_block_sizes = self.merge_features_for_dynamic_s2(
|
| 400 |
+
image_features, block_sizes
|
| 401 |
+
)
|
| 402 |
+
|
| 403 |
+
image_features = [
|
| 404 |
+
self.split_chessboard(x, block_size[0], block_size[1])
|
| 405 |
+
for x, block_size in zip(image_features, new_block_sizes)
|
| 406 |
+
] # list of B * C * H * W tensors
|
| 407 |
+
image_features = torch.cat(
|
| 408 |
+
[rearrange(x, "b c h w -> b (h w) c") for x in image_features], dim=0
|
| 409 |
+
) # B * N * C
|
| 410 |
+
image_features = self.get_mm_projector()(image_features)
|
| 411 |
+
image_features = list(
|
| 412 |
+
image_features.split(
|
| 413 |
+
[block_size[0] * block_size[1] for block_size in new_block_sizes],
|
| 414 |
+
dim=0,
|
| 415 |
+
)
|
| 416 |
+
)
|
| 417 |
+
image_features = [
|
| 418 |
+
self.merge_chessboard(x, block_size[0], block_size[1])
|
| 419 |
+
for x, block_size in zip(image_features, new_block_sizes)
|
| 420 |
+
] # list of 1 * C * H * W tensors
|
| 421 |
+
image_features = [
|
| 422 |
+
rearrange(x, "1 c h w -> (h w) c") for x in image_features
|
| 423 |
+
] # list of N * C tensors
|
| 424 |
+
image_features = torch.stack(image_features, dim=0)
|
| 425 |
+
else:
|
| 426 |
+
image_features = self.get_vision_tower()(images)
|
| 427 |
+
image_features = self.get_mm_projector()(image_features)
|
| 428 |
+
return image_features
|
| 429 |
+
|
| 430 |
+
## @yunhao: is there a better way to handle function call and attributes for llm?
|
| 431 |
+
## support beam search
|
| 432 |
+
def _temporary_reorder_cache(self, past_key_values, sorted_idx):
|
| 433 |
+
return self.get_llm()._temporary_reorder_cache(past_key_values, sorted_idx)
|
| 434 |
+
|
| 435 |
+
def get_input_embeddings(self):
|
| 436 |
+
return self.get_llm().get_input_embeddings()
|
| 437 |
+
|
| 438 |
+
def get_output_embeddings(self):
|
| 439 |
+
return self.get_llm().get_output_embeddings()
|
| 440 |
+
|
| 441 |
+
def resize_token_embeddings(self, embed_size):
|
| 442 |
+
self.get_llm().resize_token_embeddings(embed_size)
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
class LlavaMetaForCausalLM(ABC):
|
| 446 |
+
def _embed(
|
| 447 |
+
self,
|
| 448 |
+
input_ids: torch.Tensor,
|
| 449 |
+
media: Dict[str, List[torch.Tensor]],
|
| 450 |
+
media_config: Dict[str, Dict[str, Any]],
|
| 451 |
+
labels: Optional[torch.Tensor],
|
| 452 |
+
attention_mask: Optional[torch.Tensor],
|
| 453 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 454 |
+
labels = (
|
| 455 |
+
labels if labels is not None else torch.full_like(input_ids, IGNORE_INDEX)
|
| 456 |
+
)
|
| 457 |
+
attention_mask = (
|
| 458 |
+
attention_mask
|
| 459 |
+
if attention_mask is not None
|
| 460 |
+
else torch.ones_like(input_ids, dtype=torch.bool)
|
| 461 |
+
)
|
| 462 |
+
|
| 463 |
+
# Extract text and media embeddings
|
| 464 |
+
text_embeds = self.llm.model.embed_tokens(input_ids)
|
| 465 |
+
media_embeds = self.__embed_media_tokens(media, media_config)
|
| 466 |
+
|
| 467 |
+
# This is a workaround to make sure the dummy embeddings are consumed
|
| 468 |
+
while media_embeds.get("dummy"):
|
| 469 |
+
dummy_embed = media_embeds["dummy"].popleft()
|
| 470 |
+
text_embeds += torch.sum(dummy_embed) * 0
|
| 471 |
+
# Remove padding
|
| 472 |
+
batch_size = labels.shape[0]
|
| 473 |
+
text_embeds = [text_embeds[k][attention_mask[k]] for k in range(batch_size)]
|
| 474 |
+
labels = [labels[k][attention_mask[k]] for k in range(batch_size)]
|
| 475 |
+
|
| 476 |
+
# Build inverse mapping from token ID to media name
|
| 477 |
+
media_tokens = {}
|
| 478 |
+
for name, token_id in self.tokenizer.media_token_ids.items():
|
| 479 |
+
media_tokens[token_id] = name
|
| 480 |
+
|
| 481 |
+
# Fuse text and media embeddings
|
| 482 |
+
inputs_m, labels_m = [], []
|
| 483 |
+
for k in range(batch_size):
|
| 484 |
+
inputs_mk, labels_mk = [], []
|
| 485 |
+
pos = 0
|
| 486 |
+
while pos < len(labels[k]):
|
| 487 |
+
if input_ids[k][pos].item() in media_tokens:
|
| 488 |
+
end = pos + 1
|
| 489 |
+
name = media_tokens[input_ids[k][pos].item()]
|
| 490 |
+
input = media_embeds[name].popleft()
|
| 491 |
+
label = torch.full(
|
| 492 |
+
[input.shape[0]],
|
| 493 |
+
IGNORE_INDEX,
|
| 494 |
+
device=labels[k].device,
|
| 495 |
+
dtype=labels[k].dtype,
|
| 496 |
+
)
|
| 497 |
+
else:
|
| 498 |
+
end = pos
|
| 499 |
+
while (
|
| 500 |
+
end < len(labels[k])
|
| 501 |
+
and input_ids[k][end].item() not in media_tokens
|
| 502 |
+
):
|
| 503 |
+
end += 1
|
| 504 |
+
input = text_embeds[k][pos:end]
|
| 505 |
+
label = labels[k][pos:end]
|
| 506 |
+
inputs_mk.append(input)
|
| 507 |
+
labels_mk.append(label)
|
| 508 |
+
pos = end
|
| 509 |
+
inputs_m.append(torch.cat(inputs_mk, dim=0))
|
| 510 |
+
labels_m.append(torch.cat(labels_mk, dim=0))
|
| 511 |
+
inputs, labels = inputs_m, labels_m
|
| 512 |
+
|
| 513 |
+
# Check if all media embeddings are consumed
|
| 514 |
+
for name in media_embeds:
|
| 515 |
+
if media_embeds[name]:
|
| 516 |
+
raise ValueError(f"Not all {name} embeddings are consumed!")
|
| 517 |
+
|
| 518 |
+
# Truncate sequences to `model_max_length` as media embeddings are inserted
|
| 519 |
+
inputs, labels = self.__truncate_sequence(inputs, labels)
|
| 520 |
+
|
| 521 |
+
# Pad sequences to the longest one in the batch
|
| 522 |
+
return self.__batchify_sequence(inputs, labels)
|
| 523 |
+
|
| 524 |
+
def __embed_media_tokens(
|
| 525 |
+
self,
|
| 526 |
+
media: Dict[str, List[torch.Tensor]],
|
| 527 |
+
media_config: Dict[str, Dict[str, Any]],
|
| 528 |
+
) -> Dict[str, List[torch.Tensor]]:
|
| 529 |
+
embeds = defaultdict(deque)
|
| 530 |
+
for name in media:
|
| 531 |
+
embeds[name] = deque(self.encoders[name](media[name], media_config[name]))
|
| 532 |
+
return embeds
|
| 533 |
+
|
| 534 |
+
def __truncate_sequence(
|
| 535 |
+
self, inputs: List[torch.Tensor], labels: List[torch.Tensor]
|
| 536 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 537 |
+
if any(len(input) > self.tokenizer.model_max_length for input in inputs):
|
| 538 |
+
warnings.warn(
|
| 539 |
+
f"Truncating sequences to `model_max_length` ({self.tokenizer.model_max_length})."
|
| 540 |
+
)
|
| 541 |
+
inputs = [input[: self.tokenizer.model_max_length] for input in inputs]
|
| 542 |
+
labels = [label[: self.tokenizer.model_max_length] for label in labels]
|
| 543 |
+
return inputs, labels
|
| 544 |
+
|
| 545 |
+
def __batchify_sequence(
|
| 546 |
+
self, inputs: List[torch.Tensor], labels: List[torch.Tensor]
|
| 547 |
+
) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
| 548 |
+
batch_size = len(inputs)
|
| 549 |
+
device = inputs[0].device
|
| 550 |
+
hidden_size = inputs[0].shape[1]
|
| 551 |
+
max_length = max(inputs[k].shape[0] for k in range(batch_size))
|
| 552 |
+
attention_mask = torch.ones(
|
| 553 |
+
(batch_size, max_length), dtype=torch.bool, device=device
|
| 554 |
+
)
|
| 555 |
+
|
| 556 |
+
inputs_p, labels_p = [], []
|
| 557 |
+
for k in range(batch_size):
|
| 558 |
+
size_pk = max_length - inputs[k].shape[0]
|
| 559 |
+
inputs_pk = torch.zeros(
|
| 560 |
+
(size_pk, hidden_size), dtype=inputs[k].dtype, device=device
|
| 561 |
+
)
|
| 562 |
+
labels_pk = torch.full(
|
| 563 |
+
(size_pk,), IGNORE_INDEX, dtype=labels[k].dtype, device=device
|
| 564 |
+
)
|
| 565 |
+
if self.tokenizer.padding_side == "right":
|
| 566 |
+
attention_mask[k, inputs[k].shape[0] :] = False
|
| 567 |
+
inputs_pk = torch.cat([inputs[k], inputs_pk], dim=0)
|
| 568 |
+
labels_pk = torch.cat([labels[k], labels_pk], dim=0)
|
| 569 |
+
else:
|
| 570 |
+
attention_mask[k, : -inputs[k].shape[0]] = False
|
| 571 |
+
inputs_pk = torch.cat([inputs_pk, inputs[k]], dim=0)
|
| 572 |
+
labels_pk = torch.cat([labels_pk, labels[k]], dim=0)
|
| 573 |
+
inputs_p.append(inputs_pk)
|
| 574 |
+
labels_p.append(labels_pk)
|
| 575 |
+
|
| 576 |
+
inputs = torch.stack(inputs_p, dim=0)
|
| 577 |
+
labels = torch.stack(labels_p, dim=0)
|
| 578 |
+
return inputs, labels, attention_mask
|
| 579 |
+
|
| 580 |
+
@torch.inference_mode()
|
| 581 |
+
def generate(
|
| 582 |
+
self,
|
| 583 |
+
input_ids: Optional[torch.FloatTensor] = None,
|
| 584 |
+
media: Optional[Dict[str, List[torch.Tensor]]] = None,
|
| 585 |
+
media_config: Dict[str, Dict[str, Any]] = None,
|
| 586 |
+
attention_mask: Optional[torch.LongTensor] = None,
|
| 587 |
+
quant_llm: Optional[bool] = True,
|
| 588 |
+
**generation_kwargs,
|
| 589 |
+
):
|
| 590 |
+
inputs_embeds, _, attention_mask = self._embed(
|
| 591 |
+
input_ids, media, media_config, None, attention_mask
|
| 592 |
+
)
|
| 593 |
+
return self.llm.generate(
|
| 594 |
+
inputs_embeds=inputs_embeds,
|
| 595 |
+
attention_mask=attention_mask,
|
| 596 |
+
quant_llm=quant_llm,
|
| 597 |
+
**generation_kwargs,
|
| 598 |
+
)
|
| 599 |
+
|
| 600 |
+
@torch.inference_mode()
|
| 601 |
+
def generate_content(
|
| 602 |
+
self,
|
| 603 |
+
prompt: Union[str, List],
|
| 604 |
+
generation_config: Optional[GenerationConfig] = None,
|
| 605 |
+
quant_llm: Optional[bool] = True,
|
| 606 |
+
) -> str:
|
| 607 |
+
# TODO(zhijianl): Support directly taking conversation as input
|
| 608 |
+
conversation = [{"from": "human", "value": prompt}]
|
| 609 |
+
|
| 610 |
+
# Extract media from the conversation
|
| 611 |
+
|
| 612 |
+
# TODO (extract and preprocess should be done together, as the preprocess of image and video can be different, i.e. when dynamic res is used)
|
| 613 |
+
media = extract_media(conversation, self.config)
|
| 614 |
+
|
| 615 |
+
# Process media
|
| 616 |
+
media_config = defaultdict(dict)
|
| 617 |
+
for name in media:
|
| 618 |
+
if name == "image":
|
| 619 |
+
if len(media["image"]) == 1 and self.config.image_aspect_ratio in [
|
| 620 |
+
"dynamic",
|
| 621 |
+
"dynamic_s2",
|
| 622 |
+
]:
|
| 623 |
+
self.config.image_processor = self.vision_tower.image_processor
|
| 624 |
+
if self.config.image_aspect_ratio == "dynamic":
|
| 625 |
+
images = process_image(
|
| 626 |
+
media["image"][0],
|
| 627 |
+
self.config,
|
| 628 |
+
None,
|
| 629 |
+
enable_dynamic_res=True,
|
| 630 |
+
).half()
|
| 631 |
+
conversation[0]["value"] = conversation[0]["value"].replace(
|
| 632 |
+
DEFAULT_IMAGE_TOKEN,
|
| 633 |
+
f"{DEFAULT_IMAGE_TOKEN}\n" * images.shape[0],
|
| 634 |
+
)
|
| 635 |
+
else:
|
| 636 |
+
if type(self.config.s2_scales) is str:
|
| 637 |
+
self.config.s2_scales = list(
|
| 638 |
+
map(int, self.config.s2_scales.split(","))
|
| 639 |
+
)
|
| 640 |
+
images, block_sizes = process_image(
|
| 641 |
+
media["image"][0], self.config, None, enable_dynamic_s2=True
|
| 642 |
+
)
|
| 643 |
+
images = images.half()
|
| 644 |
+
media_config[name]["block_sizes"] = [block_sizes]
|
| 645 |
+
else:
|
| 646 |
+
images = process_images(
|
| 647 |
+
media["image"], self.vision_tower.image_processor, self.config
|
| 648 |
+
).half()
|
| 649 |
+
media[name] = [image for image in images]
|
| 650 |
+
elif name == "video":
|
| 651 |
+
media[name] = [
|
| 652 |
+
process_images(
|
| 653 |
+
images, self.vision_tower.image_processor, self.config
|
| 654 |
+
).half()
|
| 655 |
+
for images in media[name]
|
| 656 |
+
]
|
| 657 |
+
else:
|
| 658 |
+
raise ValueError(f"Unsupported media type: {name}")
|
| 659 |
+
|
| 660 |
+
# Tokenize the conversation
|
| 661 |
+
input_ids = (
|
| 662 |
+
tokenize_conversation(
|
| 663 |
+
conversation, self.tokenizer, add_generation_prompt=True
|
| 664 |
+
)
|
| 665 |
+
.cuda()
|
| 666 |
+
.unsqueeze(0)
|
| 667 |
+
)
|
| 668 |
+
|
| 669 |
+
# Set up the generation config
|
| 670 |
+
generation_config = generation_config or self.default_generation_config
|
| 671 |
+
# Generate the response
|
| 672 |
+
try:
|
| 673 |
+
output_ids = self.generate(
|
| 674 |
+
input_ids=input_ids,
|
| 675 |
+
media=media,
|
| 676 |
+
media_config=media_config,
|
| 677 |
+
generation_config=generation_config,
|
| 678 |
+
quant_llm=quant_llm,
|
| 679 |
+
)
|
| 680 |
+
except ValueError:
|
| 681 |
+
if not generation_config.do_sample:
|
| 682 |
+
raise
|
| 683 |
+
# FIXME(zhijianl): This is a temporary workaround for the sampling issue
|
| 684 |
+
logging.warning(
|
| 685 |
+
"Generation failed with sampling, retrying with greedy decoding."
|
| 686 |
+
)
|
| 687 |
+
generation_config.do_sample = False
|
| 688 |
+
output_ids = self.generate(
|
| 689 |
+
input_ids=input_ids,
|
| 690 |
+
media=media,
|
| 691 |
+
media_config=media_config,
|
| 692 |
+
generation_config=generation_config,
|
| 693 |
+
)
|
| 694 |
+
|
| 695 |
+
# Decode the response
|
| 696 |
+
response = self.tokenizer.decode(
|
| 697 |
+
output_ids[0], skip_special_tokens=True
|
| 698 |
+
).strip()
|
| 699 |
+
return response
|
| 700 |
+
|
| 701 |
+
@torch.inference_mode()
|
| 702 |
+
def benchmark(self, prompt: Union[str, List], quant_llm) -> None:
|
| 703 |
+
# TODO(zhijianl): Support directly taking conversation as input
|
| 704 |
+
conversation = [{"from": "human", "value": prompt}]
|
| 705 |
+
|
| 706 |
+
# Extract media from the conversation
|
| 707 |
+
|
| 708 |
+
# TODO (extract and preprocess should be done together, as the preprocess of image and video can be different, i.e. when dynamic res is used)
|
| 709 |
+
media = extract_media(conversation, self.config)
|
| 710 |
+
|
| 711 |
+
# Process media
|
| 712 |
+
media_config = defaultdict(dict)
|
| 713 |
+
image_num = 0
|
| 714 |
+
for name in media:
|
| 715 |
+
if name == "image":
|
| 716 |
+
if len(media["image"]) == 1 and self.config.image_aspect_ratio in [
|
| 717 |
+
"dynamic",
|
| 718 |
+
"dynamic_s2",
|
| 719 |
+
]:
|
| 720 |
+
self.config.image_processor = self.vision_tower.image_processor
|
| 721 |
+
if self.config.image_aspect_ratio == "dynamic":
|
| 722 |
+
images = process_image(
|
| 723 |
+
media["image"][0],
|
| 724 |
+
self.config,
|
| 725 |
+
None,
|
| 726 |
+
enable_dynamic_res=True,
|
| 727 |
+
).half()
|
| 728 |
+
if len(images.shape) == 3:
|
| 729 |
+
images = images.reshape(1, *images.shape)
|
| 730 |
+
image_num += images.shape[0]
|
| 731 |
+
size = images.shape[1:]
|
| 732 |
+
conversation[0]["value"] = conversation[0]["value"].replace(
|
| 733 |
+
DEFAULT_IMAGE_TOKEN,
|
| 734 |
+
f"{DEFAULT_IMAGE_TOKEN}\n" * images.shape[0],
|
| 735 |
+
)
|
| 736 |
+
else:
|
| 737 |
+
if type(self.config.s2_scales) is str:
|
| 738 |
+
self.config.s2_scales = list(
|
| 739 |
+
map(int, self.config.s2_scales.split(","))
|
| 740 |
+
)
|
| 741 |
+
images, block_sizes = process_image(
|
| 742 |
+
media["image"][0], self.config, None, enable_dynamic_s2=True
|
| 743 |
+
)
|
| 744 |
+
images = images.half()
|
| 745 |
+
if len(images.shape) == 3:
|
| 746 |
+
images = images.reshape(1, *images.shape)
|
| 747 |
+
image_num += images.shape[0]
|
| 748 |
+
size = images.shape[1:]
|
| 749 |
+
media_config[name]["block_sizes"] = [block_sizes]
|
| 750 |
+
else:
|
| 751 |
+
images = process_images(
|
| 752 |
+
media["image"], self.vision_tower.image_processor, self.config
|
| 753 |
+
).half()
|
| 754 |
+
image_num += images.shape[0]
|
| 755 |
+
size = images.shape[1:]
|
| 756 |
+
media[name] = [image for image in images]
|
| 757 |
+
elif name == "video":
|
| 758 |
+
media[name] = [
|
| 759 |
+
process_images(
|
| 760 |
+
images, self.vision_tower.image_processor, self.config
|
| 761 |
+
).half()
|
| 762 |
+
for images in media[name]
|
| 763 |
+
]
|
| 764 |
+
for images in media[name]:
|
| 765 |
+
image_num += images.shape[0]
|
| 766 |
+
size = images.shape[1:]
|
| 767 |
+
else:
|
| 768 |
+
raise ValueError(f"Unsupported media type: {name}")
|
| 769 |
+
|
| 770 |
+
# Tokenize the conversation
|
| 771 |
+
input_ids = (
|
| 772 |
+
tokenize_conversation(
|
| 773 |
+
conversation, self.tokenizer, add_generation_prompt=True
|
| 774 |
+
)
|
| 775 |
+
.cuda()
|
| 776 |
+
.unsqueeze(0)
|
| 777 |
+
)
|
| 778 |
+
|
| 779 |
+
# Set up the generation config
|
| 780 |
+
for i in range(10):
|
| 781 |
+
torch.cuda.synchronize()
|
| 782 |
+
t_st = time()
|
| 783 |
+
inputs_embeds, _, attention_mask = self._embed(
|
| 784 |
+
input_ids, media, media_config, None, None
|
| 785 |
+
)
|
| 786 |
+
torch.cuda.synchronize()
|
| 787 |
+
t_ed = time()
|
| 788 |
+
torch.cuda.empty_cache()
|
| 789 |
+
print(
|
| 790 |
+
"Time of vision tower and others is {:.5f} s for {} images ({} x {} x {})".format(
|
| 791 |
+
t_ed - t_st, image_num, size[0], size[1], size[2]
|
| 792 |
+
)
|
| 793 |
+
)
|
| 794 |
+
output = self.llm.benchmark(
|
| 795 |
+
inputs_embeds=inputs_embeds,
|
| 796 |
+
attention_mask=attention_mask,
|
| 797 |
+
quant_llm=quant_llm,
|
| 798 |
+
)
|
| 799 |
+
# response = self.tokenizer.decode(output, skip_special_tokens=True).strip()
|
| 800 |
+
return
|
| 801 |
+
|
| 802 |
+
@property
|
| 803 |
+
def default_generation_config(self) -> GenerationConfig:
|
| 804 |
+
generation_config = copy.deepcopy(self.generation_config or GenerationConfig())
|
| 805 |
+
if self.tokenizer.eos_token_id is None:
|
| 806 |
+
raise ValueError("Tokenizer must have an EOS token")
|
| 807 |
+
if generation_config.max_length == GenerationConfig().max_length:
|
| 808 |
+
generation_config.max_length = self.tokenizer.model_max_length
|
| 809 |
+
if generation_config.pad_token_id is None:
|
| 810 |
+
generation_config.pad_token_id = (
|
| 811 |
+
self.tokenizer.pad_token_id or self.tokenizer.eos_token_id
|
| 812 |
+
)
|
| 813 |
+
if generation_config.bos_token_id is None:
|
| 814 |
+
generation_config.bos_token_id = (
|
| 815 |
+
self.tokenizer.bos_token_id or self.tokenizer.eos_token_id
|
| 816 |
+
)
|
| 817 |
+
if generation_config.eos_token_id is None:
|
| 818 |
+
generation_config.eos_token_id = self.tokenizer.stop_token_ids
|
| 819 |
+
return generation_config
|
| 820 |
+
|
| 821 |
+
# Prepare media
|
| 822 |
+
|
| 823 |
+
# Process media
|
| 824 |
+
@torch.inference_mode()
|
| 825 |
+
def prepare_media(self, conversation):
|
| 826 |
+
media = extract_media(conversation, self.config)
|
| 827 |
+
|
| 828 |
+
# Process media
|
| 829 |
+
media_config = defaultdict(dict)
|
| 830 |
+
for name in media:
|
| 831 |
+
if name == "image":
|
| 832 |
+
if len(media["image"]) == 1 and self.config.image_aspect_ratio in [
|
| 833 |
+
"dynamic",
|
| 834 |
+
"dynamic_s2",
|
| 835 |
+
]:
|
| 836 |
+
self.config.image_processor = self.vision_tower.image_processor
|
| 837 |
+
if self.config.image_aspect_ratio == "dynamic":
|
| 838 |
+
images = process_image(
|
| 839 |
+
media["image"][0],
|
| 840 |
+
self.config,
|
| 841 |
+
None,
|
| 842 |
+
enable_dynamic_res=True,
|
| 843 |
+
).half()
|
| 844 |
+
conversation[0]["value"] = conversation[0]["value"].replace(
|
| 845 |
+
DEFAULT_IMAGE_TOKEN,
|
| 846 |
+
f"{DEFAULT_IMAGE_TOKEN}\n" * images.shape[0],
|
| 847 |
+
)
|
| 848 |
+
else:
|
| 849 |
+
if type(self.config.s2_scales) is str:
|
| 850 |
+
self.config.s2_scales = list(
|
| 851 |
+
map(int, self.config.s2_scales.split(","))
|
| 852 |
+
)
|
| 853 |
+
images, block_sizes = process_image(
|
| 854 |
+
media["image"][0], self.config, None, enable_dynamic_s2=True
|
| 855 |
+
)
|
| 856 |
+
images = images.half()
|
| 857 |
+
media_config[name]["block_sizes"] = [block_sizes]
|
| 858 |
+
else:
|
| 859 |
+
images = process_images(
|
| 860 |
+
media["image"], self.vision_tower.image_processor, self.config
|
| 861 |
+
).half()
|
| 862 |
+
media[name] = [image for image in images]
|
| 863 |
+
elif name == "video":
|
| 864 |
+
media[name] = [
|
| 865 |
+
process_images(
|
| 866 |
+
images, self.vision_tower.image_processor, self.config
|
| 867 |
+
).half()
|
| 868 |
+
for images in media[name]
|
| 869 |
+
]
|
| 870 |
+
else:
|
| 871 |
+
raise ValueError(f"Unsupported media type: {name}")
|
| 872 |
+
return media, media_config
|
| 873 |
+
|
| 874 |
+
@torch.inference_mode()
|
| 875 |
+
def stream_gen(
|
| 876 |
+
self,
|
| 877 |
+
input_ids,
|
| 878 |
+
media,
|
| 879 |
+
media_cfg,
|
| 880 |
+
start_pos,
|
| 881 |
+
chunk_prefilling,
|
| 882 |
+
quant_llm,
|
| 883 |
+
attention_mask=None,
|
| 884 |
+
) -> str:
|
| 885 |
+
if media is None:
|
| 886 |
+
inputs_embeds = self.llm.model.embed_tokens(input_ids)
|
| 887 |
+
else:
|
| 888 |
+
image_num = torch.sum(input_ids == 151649)
|
| 889 |
+
if image_num == 1 and self.config.image_aspect_ratio == "dynamic":
|
| 890 |
+
patch_num = len(media["image"])
|
| 891 |
+
new_input_ids = []
|
| 892 |
+
for i, id in enumerate(input_ids[0]):
|
| 893 |
+
if id == 151649:
|
| 894 |
+
new_input_ids.extend(input_ids[0, 0:i])
|
| 895 |
+
new_input_ids.extend([198, 151649, 198] * patch_num)
|
| 896 |
+
new_input_ids.extend(input_ids[0, i + 1 :])
|
| 897 |
+
break
|
| 898 |
+
input_ids = torch.tensor(
|
| 899 |
+
[new_input_ids], dtype=torch.int, device="cuda"
|
| 900 |
+
)
|
| 901 |
+
inputs_embeds, _, _ = self._embed(
|
| 902 |
+
input_ids, media, media_cfg, None, attention_mask=None
|
| 903 |
+
)
|
| 904 |
+
length = inputs_embeds.shape[1]
|
| 905 |
+
if quant_llm:
|
| 906 |
+
out = self.llm(None, start_pos, inputs_embeds, chunk_prefilling)
|
| 907 |
+
else:
|
| 908 |
+
out = self.llm.forwardfp16(None, start_pos, inputs_embeds, chunk_prefilling)
|
| 909 |
+
return out, length
|
llm-awq/tinychat/models/nvila_qwen2.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023 Haotian Liu
|
| 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 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# This file is modified from https://github.com/haotian-liu/LLaVA/
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
import os
|
| 19 |
+
from collections import defaultdict
|
| 20 |
+
from typing import Dict, List, Optional, Tuple, Union
|
| 21 |
+
|
| 22 |
+
import torch
|
| 23 |
+
from transformers import AutoConfig, AutoModel, PretrainedConfig, PreTrainedModel
|
| 24 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
from .nvila.configuration_llava import LlavaConfig
|
| 28 |
+
from .nvila.llava_arch import LlavaMetaForCausalLM, LlavaMetaModel
|
| 29 |
+
from .qwen2 import Qwen2ForCausalLM
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def skip(*args, **kwargs):
|
| 33 |
+
pass
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 37 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 38 |
+
torch.nn.init.uniform_ = skip
|
| 39 |
+
torch.nn.init.normal_ = skip
|
| 40 |
+
from transformers import modeling_utils
|
| 41 |
+
|
| 42 |
+
modeling_utils._init_weights = False
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
class LlavaLlamaConfig(LlavaConfig):
|
| 46 |
+
model_type = "llava_llama"
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
class NVILAQwen2(LlavaMetaModel, LlavaMetaForCausalLM, PreTrainedModel):
|
| 50 |
+
config_class = LlavaLlamaConfig
|
| 51 |
+
main_input_name = "input_embeds"
|
| 52 |
+
supports_gradient_checkpointing = True
|
| 53 |
+
_supports_flash_attn_2 = True
|
| 54 |
+
|
| 55 |
+
def __init__(
|
| 56 |
+
self, config: LlavaLlamaConfig = None, llm=True, *args, **kwargs
|
| 57 |
+
) -> None:
|
| 58 |
+
super().__init__(config)
|
| 59 |
+
self.init_vlm(config=config, *args, **kwargs)
|
| 60 |
+
# TODO: Skip the weight loading to save time
|
| 61 |
+
self.llm_cfg = AutoConfig.from_pretrained(self.llm_cfg, init_weights=False)
|
| 62 |
+
if llm:
|
| 63 |
+
self.llm = Qwen2ForCausalLM.from_pretrained(self.llm_cfg._name_or_path)
|
| 64 |
+
self.llm = self.llm.cpu()
|
| 65 |
+
self.llm.resize_token_embeddings(len(self.tokenizer))
|
| 66 |
+
else:
|
| 67 |
+
self.llm = None
|
| 68 |
+
|
| 69 |
+
@classmethod
|
| 70 |
+
def from_pretrained(
|
| 71 |
+
cls,
|
| 72 |
+
pretrained_model_name_or_path: Optional[Union[str, os.PathLike]],
|
| 73 |
+
*model_args,
|
| 74 |
+
config: Optional[Union[PretrainedConfig, str, os.PathLike]] = None,
|
| 75 |
+
cache_dir: Optional[Union[str, os.PathLike]] = None,
|
| 76 |
+
ignore_mismatched_sizes: bool = False,
|
| 77 |
+
force_download: bool = False,
|
| 78 |
+
local_files_only: bool = False,
|
| 79 |
+
token: Optional[Union[str, bool]] = None,
|
| 80 |
+
revision: str = "main",
|
| 81 |
+
use_safetensors: bool = None,
|
| 82 |
+
**kwargs,
|
| 83 |
+
):
|
| 84 |
+
if hasattr(cls, "load_pretrained"):
|
| 85 |
+
return cls.load_pretrained(
|
| 86 |
+
pretrained_model_name_or_path,
|
| 87 |
+
*model_args,
|
| 88 |
+
config=config,
|
| 89 |
+
cache_dir=cache_dir,
|
| 90 |
+
ignore_mismatched_sizes=ignore_mismatched_sizes,
|
| 91 |
+
force_download=force_download,
|
| 92 |
+
local_files_only=local_files_only,
|
| 93 |
+
token=token,
|
| 94 |
+
revision=revision,
|
| 95 |
+
use_safetensors=use_safetensors,
|
| 96 |
+
**kwargs,
|
| 97 |
+
)
|
| 98 |
+
return super(NVILAQwen2).from_pretrained(
|
| 99 |
+
pretrained_model_name_or_path,
|
| 100 |
+
*model_args,
|
| 101 |
+
config=config,
|
| 102 |
+
cache_dir=cache_dir,
|
| 103 |
+
ignore_mismatched_sizes=ignore_mismatched_sizes,
|
| 104 |
+
force_download=force_download,
|
| 105 |
+
local_files_only=local_files_only,
|
| 106 |
+
token=token,
|
| 107 |
+
revision=revision,
|
| 108 |
+
use_safetensors=use_safetensors,
|
| 109 |
+
**kwargs,
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
def forward(
|
| 113 |
+
self,
|
| 114 |
+
input_ids: torch.LongTensor = None,
|
| 115 |
+
media: Optional[Dict[str, List[torch.Tensor]]] = None,
|
| 116 |
+
images: Optional[torch.FloatTensor] = None,
|
| 117 |
+
media_config: Optional[List] = None,
|
| 118 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 119 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 120 |
+
past_key_values: Optional[List[torch.FloatTensor]] = None,
|
| 121 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 122 |
+
labels: Optional[torch.LongTensor] = None,
|
| 123 |
+
packing: bool = True,
|
| 124 |
+
seqlens_in_batch: Optional[torch.LongTensor] = None,
|
| 125 |
+
dpo_forward: bool = False,
|
| 126 |
+
**kwargs,
|
| 127 |
+
) -> Union[Tuple, CausalLMOutputWithPast]:
|
| 128 |
+
self.freezed_module_patch()
|
| 129 |
+
|
| 130 |
+
if images is not None:
|
| 131 |
+
if media is not None:
|
| 132 |
+
raise ValueError(
|
| 133 |
+
"Both 'media' and 'images' are provided. Please provide only one."
|
| 134 |
+
)
|
| 135 |
+
media = {"image": images}
|
| 136 |
+
|
| 137 |
+
if media_config is None:
|
| 138 |
+
media_config = defaultdict(dict)
|
| 139 |
+
|
| 140 |
+
if inputs_embeds is None:
|
| 141 |
+
inputs_embeds, labels, attention_mask = self._embed(
|
| 142 |
+
input_ids, media, media_config, labels, attention_mask
|
| 143 |
+
)
|
| 144 |
+
|
| 145 |
+
outputs = self.llm(
|
| 146 |
+
inputs_embeds=inputs_embeds,
|
| 147 |
+
attention_mask=attention_mask,
|
| 148 |
+
position_ids=position_ids,
|
| 149 |
+
past_key_values=past_key_values,
|
| 150 |
+
labels=labels,
|
| 151 |
+
**kwargs,
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
if dpo_forward:
|
| 155 |
+
return outputs.logits, labels
|
| 156 |
+
|
| 157 |
+
return outputs
|
llm-awq/tinychat/models/qwen2.py
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/huggingface/transformers/blob/main/src/transformers/models/qwen2/modeling_qwen2.py
|
| 2 |
+
"""PyTorch Qwen2 model."""
|
| 3 |
+
|
| 4 |
+
import math
|
| 5 |
+
from typing import List, Optional, Tuple, Union
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
import torch.utils.checkpoint
|
| 9 |
+
from torch import nn
|
| 10 |
+
import awq_inference_engine
|
| 11 |
+
from torch.nn import BCEWithLogitsLoss, CrossEntropyLoss, MSELoss
|
| 12 |
+
from transformers.models.qwen2.configuration_qwen2 import Qwen2Config
|
| 13 |
+
from transformers.activations import ACT2FN
|
| 14 |
+
import tinychat
|
| 15 |
+
import torch.nn.functional as F
|
| 16 |
+
import time
|
| 17 |
+
from tqdm import tqdm
|
| 18 |
+
from transformers import GenerationMixin
|
| 19 |
+
from transformers.models.qwen2 import Qwen2ForCausalLM
|
| 20 |
+
from flash_attn import flash_attn_func
|
| 21 |
+
|
| 22 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 23 |
+
max_seq_len = tinychat.utils.constants.max_seq_len
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class Qwen2RMSNorm(nn.Module):
|
| 27 |
+
def __init__(self, dim: int, eps: float = 1e-6):
|
| 28 |
+
super().__init__()
|
| 29 |
+
self.eps = eps
|
| 30 |
+
self.weight = nn.Parameter(torch.ones(dim))
|
| 31 |
+
|
| 32 |
+
def _norm(self, x):
|
| 33 |
+
return x * torch.rsqrt(x.pow(2).mean(-1, keepdim=True) + self.eps)
|
| 34 |
+
|
| 35 |
+
def forward(self, x):
|
| 36 |
+
output = torch.empty_like(x)
|
| 37 |
+
awq_inference_engine.layernorm_forward_cuda(x, self.weight, output, self.eps)
|
| 38 |
+
return output
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def precompute_freqs_cis(
|
| 42 |
+
dim: int, end: int, theta: float = 10000.0, scale: float = 1.0
|
| 43 |
+
):
|
| 44 |
+
freqs = 1.0 / (theta ** (torch.arange(0, dim, 2)[: (dim // 2)].float() / dim))
|
| 45 |
+
t = torch.arange(end, device=freqs.device) # type: ignore
|
| 46 |
+
freqs = torch.outer(t * scale, freqs).float() # type: ignore
|
| 47 |
+
|
| 48 |
+
freqs_cis = torch.polar(torch.ones_like(freqs), freqs) # complex64
|
| 49 |
+
return freqs_cis
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def precompute_freqs(
|
| 53 |
+
dim: int, end: int, theta: float = 10000.0, scale: float = 1.0, device=None
|
| 54 |
+
):
|
| 55 |
+
inv_freq = 1.0 / (theta ** (torch.arange(0, dim, 2).float().to(device) / dim))
|
| 56 |
+
seq = torch.arange(end, dtype=inv_freq.dtype, device=device)
|
| 57 |
+
freqs = torch.einsum("i , j -> i j", seq, inv_freq)
|
| 58 |
+
freqs = freqs.reshape(freqs.shape[0], 1, 1, -1)
|
| 59 |
+
return torch.cat((freqs, freqs), dim=-1)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def reshape_for_broadcast(freqs_cis: torch.Tensor, x: torch.Tensor):
|
| 63 |
+
ndim = x.ndim
|
| 64 |
+
assert 0 <= 1 < ndim
|
| 65 |
+
assert freqs_cis.shape == (x.shape[1], x.shape[-1])
|
| 66 |
+
shape = [d if i == 1 or i == ndim - 1 else 1 for i, d in enumerate(x.shape)]
|
| 67 |
+
return freqs_cis.view(*shape)
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
def apply_rotary_emb(
|
| 71 |
+
xq: torch.Tensor,
|
| 72 |
+
xk: torch.Tensor,
|
| 73 |
+
freqs_cis: torch.Tensor,
|
| 74 |
+
) -> Tuple[torch.Tensor, torch.Tensor]:
|
| 75 |
+
# xq_ = torch.view_as_complex(xq.float().reshape(*xq.shape[:-1], -1, 2))
|
| 76 |
+
# k_ = torch.view_as_complex(xk.float().reshape(*xk.shape[:-1], -1, 2))
|
| 77 |
+
xq_ = torch.view_as_complex(
|
| 78 |
+
xq.float().reshape(*xq.shape[:-1], 2, -1).transpose(-2, -1).contiguous()
|
| 79 |
+
)
|
| 80 |
+
xk_ = torch.view_as_complex(
|
| 81 |
+
xk.float().reshape(*xk.shape[:-1], 2, -1).transpose(-2, -1).contiguous()
|
| 82 |
+
)
|
| 83 |
+
freqs_cis = reshape_for_broadcast(freqs_cis, xq_)
|
| 84 |
+
xq_out = torch.view_as_real(xq_ * freqs_cis).transpose(-2, -1).flatten(3)
|
| 85 |
+
xk_out = torch.view_as_real(xk_ * freqs_cis).transpose(-2, -1).flatten(3)
|
| 86 |
+
return xq_out.type_as(xq), xk_out.type_as(xk)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
class Qwen2MLP(nn.Module):
|
| 90 |
+
def __init__(self, config):
|
| 91 |
+
super().__init__()
|
| 92 |
+
self.hidden_size = config.hidden_size
|
| 93 |
+
self.intermediate_size = config.intermediate_size
|
| 94 |
+
self.gate_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 95 |
+
self.up_proj = nn.Linear(self.hidden_size, self.intermediate_size, bias=False)
|
| 96 |
+
self.down_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=False)
|
| 97 |
+
self.act_fn = ACT2FN[config.hidden_act]
|
| 98 |
+
|
| 99 |
+
def forward(self, hidden_state):
|
| 100 |
+
return self.down_proj(
|
| 101 |
+
self.act_fn(self.gate_proj(hidden_state)) * self.up_proj(hidden_state)
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
# Copied from transformers.models.llama.modeling_llama.repeat_kv
|
| 106 |
+
def repeat_kv(x: torch.Tensor, n_rep: int) -> torch.Tensor:
|
| 107 |
+
"""
|
| 108 |
+
This is the equivalent of torch.repeat_interleave(x, dim=1, repeats=n_rep). The hidden states go from (batch,
|
| 109 |
+
num_key_value_heads, seqlen, head_dim) to (batch, num_attention_heads, seqlen, head_dim)
|
| 110 |
+
"""
|
| 111 |
+
batch, num_key_value_heads, slen, head_dim = x.shape
|
| 112 |
+
if n_rep == 1:
|
| 113 |
+
return x
|
| 114 |
+
x = x[:, :, None, :, :].expand(batch, num_key_value_heads, n_rep, slen, head_dim)
|
| 115 |
+
return x.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
class Qwen2AttentionFused(nn.Module):
|
| 119 |
+
"""
|
| 120 |
+
Multi-headed attention from 'Attention Is All You Need' paper. Modified to use sliding window attention: Longformer
|
| 121 |
+
and "Generating Long Sequences with Sparse Transformers".
|
| 122 |
+
"""
|
| 123 |
+
|
| 124 |
+
def __init__(self, config: Qwen2Config, layer_idx: Optional[int] = None):
|
| 125 |
+
super().__init__()
|
| 126 |
+
self.args = config
|
| 127 |
+
self.layer_idx = layer_idx
|
| 128 |
+
if layer_idx is None:
|
| 129 |
+
print(
|
| 130 |
+
f"Instantiating {self.__class__.__name__} without passing `layer_idx` is not recommended and will "
|
| 131 |
+
"to errors during the forward call, if caching is used. Please make sure to provide a `layer_idx` "
|
| 132 |
+
"when creating this class."
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
self.hidden_size = config.hidden_size
|
| 136 |
+
self.num_heads = config.num_attention_heads
|
| 137 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 138 |
+
self.num_key_value_heads = config.num_key_value_heads
|
| 139 |
+
self.num_key_value_groups = self.num_heads // self.num_key_value_heads
|
| 140 |
+
self.max_position_embeddings = config.max_position_embeddings
|
| 141 |
+
self.rope_theta = config.rope_theta
|
| 142 |
+
self.is_causal = True
|
| 143 |
+
self.attention_dropout = config.attention_dropout
|
| 144 |
+
self.rope_scaling = config.rope_scaling
|
| 145 |
+
if self.rope_scaling is None:
|
| 146 |
+
self.rope_scaling = 1.0
|
| 147 |
+
elif isinstance(self.rope_scaling, dict):
|
| 148 |
+
self.rope_scaling = self.rope_scaling.get("factor", 1.0)
|
| 149 |
+
|
| 150 |
+
if (self.head_dim * self.num_heads) != self.hidden_size:
|
| 151 |
+
raise ValueError(
|
| 152 |
+
f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
|
| 153 |
+
f" and `num_heads`: {self.num_heads})."
|
| 154 |
+
)
|
| 155 |
+
self.q_proj = nn.Linear(
|
| 156 |
+
self.hidden_size, self.num_heads * self.head_dim, bias=True
|
| 157 |
+
)
|
| 158 |
+
self.k_proj = nn.Linear(
|
| 159 |
+
self.hidden_size, self.num_key_value_heads * self.head_dim, bias=True
|
| 160 |
+
)
|
| 161 |
+
self.v_proj = nn.Linear(
|
| 162 |
+
self.hidden_size, self.num_key_value_heads * self.head_dim, bias=True
|
| 163 |
+
)
|
| 164 |
+
self.o_proj = nn.Linear(
|
| 165 |
+
self.num_heads * self.head_dim, self.hidden_size, bias=False
|
| 166 |
+
)
|
| 167 |
+
self.kv_max_seq_len = min(max_seq_len, self.max_position_embeddings)
|
| 168 |
+
# following fastertransformer definition
|
| 169 |
+
self.cache_v = (
|
| 170 |
+
torch.zeros(
|
| 171 |
+
(
|
| 172 |
+
max_batch_size,
|
| 173 |
+
self.num_key_value_heads,
|
| 174 |
+
# args.max_position_embeddings,
|
| 175 |
+
self.kv_max_seq_len,
|
| 176 |
+
self.head_dim,
|
| 177 |
+
)
|
| 178 |
+
)
|
| 179 |
+
.cuda()
|
| 180 |
+
.half()
|
| 181 |
+
) # added to half
|
| 182 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 183 |
+
self.cache_k = (
|
| 184 |
+
torch.zeros(
|
| 185 |
+
(
|
| 186 |
+
max_batch_size,
|
| 187 |
+
self.num_key_value_heads,
|
| 188 |
+
self.head_dim // 8,
|
| 189 |
+
# args.max_position_embeddings,
|
| 190 |
+
self.kv_max_seq_len,
|
| 191 |
+
8,
|
| 192 |
+
)
|
| 193 |
+
)
|
| 194 |
+
.cuda()
|
| 195 |
+
.half()
|
| 196 |
+
) # added to half
|
| 197 |
+
|
| 198 |
+
def forward(
|
| 199 |
+
self,
|
| 200 |
+
x: torch.Tensor,
|
| 201 |
+
start_pos: int,
|
| 202 |
+
freqs: torch.Tensor,
|
| 203 |
+
mask: Optional[torch.Tensor],
|
| 204 |
+
chunk_prefilling: bool = False,
|
| 205 |
+
):
|
| 206 |
+
bsz, seqlen, _ = x.shape
|
| 207 |
+
|
| 208 |
+
query_states = self.q_proj(x)
|
| 209 |
+
key_states = self.k_proj(x)
|
| 210 |
+
value_states = self.v_proj(x)
|
| 211 |
+
|
| 212 |
+
if seqlen > 1:
|
| 213 |
+
xq = query_states.view(bsz, seqlen, self.num_heads, self.head_dim)
|
| 214 |
+
xk = key_states.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 215 |
+
xv = value_states.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 216 |
+
|
| 217 |
+
xq, xk = apply_rotary_emb(xq, xk, freqs_cis=freqs)
|
| 218 |
+
|
| 219 |
+
self.cache_k = self.cache_k.to(xq)
|
| 220 |
+
self.cache_v = self.cache_v.to(xq)
|
| 221 |
+
|
| 222 |
+
values_store = xv.transpose(2, 1)
|
| 223 |
+
|
| 224 |
+
keys_store = (
|
| 225 |
+
xk.reshape(bsz, seqlen, self.num_key_value_heads, self.head_dim // 8, 8)
|
| 226 |
+
.permute(0, 2, 3, 1, 4)
|
| 227 |
+
.contiguous()
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 231 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 232 |
+
if chunk_prefilling:
|
| 233 |
+
keys = self.cache_k[:, :, :, 0 : start_pos + seqlen, :]
|
| 234 |
+
keys = (
|
| 235 |
+
keys.permute(0, 3, 1, 2, 4)
|
| 236 |
+
.reshape(
|
| 237 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 238 |
+
)
|
| 239 |
+
.contiguous()
|
| 240 |
+
)
|
| 241 |
+
values = self.cache_v[:, :, 0 : start_pos + seqlen, :]
|
| 242 |
+
values = (
|
| 243 |
+
values.transpose(2, 1)
|
| 244 |
+
.reshape(
|
| 245 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 246 |
+
)
|
| 247 |
+
.contiguous()
|
| 248 |
+
)
|
| 249 |
+
else:
|
| 250 |
+
keys = xk
|
| 251 |
+
values = xv
|
| 252 |
+
output = flash_attn_func(
|
| 253 |
+
q=xq,
|
| 254 |
+
k=keys,
|
| 255 |
+
v=values,
|
| 256 |
+
causal=True,
|
| 257 |
+
)
|
| 258 |
+
output = output.contiguous().view(bsz, seqlen, -1)
|
| 259 |
+
else:
|
| 260 |
+
xq = query_states.view(bsz, self.num_heads, self.head_dim)
|
| 261 |
+
xk = key_states.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 262 |
+
xv = value_states.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 263 |
+
|
| 264 |
+
output = awq_inference_engine.single_query_attention(
|
| 265 |
+
xq,
|
| 266 |
+
xk,
|
| 267 |
+
xv,
|
| 268 |
+
self.cache_k,
|
| 269 |
+
self.cache_v,
|
| 270 |
+
None,
|
| 271 |
+
# alibi position encodings
|
| 272 |
+
None,
|
| 273 |
+
start_pos,
|
| 274 |
+
self.head_dim,
|
| 275 |
+
self.rope_theta,
|
| 276 |
+
self.rope_scaling,
|
| 277 |
+
True,
|
| 278 |
+
)
|
| 279 |
+
output = output.reshape(bsz, 1, -1)
|
| 280 |
+
|
| 281 |
+
return self.o_proj(output)
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
class Qwen2DecoderLayer(nn.Module):
|
| 285 |
+
def __init__(self, config: Qwen2Config, layer_idx: int):
|
| 286 |
+
super().__init__()
|
| 287 |
+
self.hidden_size = config.hidden_size
|
| 288 |
+
|
| 289 |
+
self.self_attn = Qwen2AttentionFused(config, layer_idx)
|
| 290 |
+
|
| 291 |
+
self.mlp = Qwen2MLP(config)
|
| 292 |
+
self.input_layernorm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 293 |
+
self.post_attention_layernorm = Qwen2RMSNorm(
|
| 294 |
+
config.hidden_size, eps=config.rms_norm_eps
|
| 295 |
+
)
|
| 296 |
+
|
| 297 |
+
def forward(
|
| 298 |
+
self,
|
| 299 |
+
x: torch.Tensor,
|
| 300 |
+
start_pos: int,
|
| 301 |
+
freqs: torch.Tensor,
|
| 302 |
+
mask: Optional[torch.Tensor],
|
| 303 |
+
chunk_prefilling: bool = False,
|
| 304 |
+
):
|
| 305 |
+
residual = x
|
| 306 |
+
x = self.input_layernorm(x)
|
| 307 |
+
|
| 308 |
+
# Self Attention
|
| 309 |
+
x = self.self_attn(
|
| 310 |
+
x=x,
|
| 311 |
+
start_pos=start_pos,
|
| 312 |
+
freqs=freqs,
|
| 313 |
+
mask=mask,
|
| 314 |
+
chunk_prefilling=chunk_prefilling,
|
| 315 |
+
)
|
| 316 |
+
x = residual + x
|
| 317 |
+
|
| 318 |
+
# Fully Connected
|
| 319 |
+
residual = x
|
| 320 |
+
x = self.post_attention_layernorm(x)
|
| 321 |
+
x = self.mlp(x)
|
| 322 |
+
x = residual + x
|
| 323 |
+
return x
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
class Qwen2Model(nn.Module):
|
| 327 |
+
def __init__(self, config: Qwen2Config):
|
| 328 |
+
super().__init__()
|
| 329 |
+
self.padding_idx = config.pad_token_id
|
| 330 |
+
self.vocab_size = config.vocab_size
|
| 331 |
+
|
| 332 |
+
self.embed_tokens = nn.Embedding(
|
| 333 |
+
config.vocab_size, config.hidden_size, self.padding_idx
|
| 334 |
+
)
|
| 335 |
+
self.layers = nn.ModuleList(
|
| 336 |
+
[
|
| 337 |
+
Qwen2DecoderLayer(config, layer_idx)
|
| 338 |
+
for layer_idx in range(config.num_hidden_layers)
|
| 339 |
+
]
|
| 340 |
+
)
|
| 341 |
+
self.norm = Qwen2RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 342 |
+
# Note (Haotian): rope_theta has to be defined here, otherwise context stage is wrong.
|
| 343 |
+
rope_scale = config.rope_scaling
|
| 344 |
+
if rope_scale is None:
|
| 345 |
+
rope_scale = 1.0
|
| 346 |
+
else:
|
| 347 |
+
rope_scale = 1.0 / rope_scale["factor"]
|
| 348 |
+
self.freqs = precompute_freqs(
|
| 349 |
+
config.hidden_size // config.num_attention_heads,
|
| 350 |
+
config.max_position_embeddings * 2,
|
| 351 |
+
config.rope_theta,
|
| 352 |
+
rope_scale,
|
| 353 |
+
)
|
| 354 |
+
self.freqs_cis = precompute_freqs_cis(
|
| 355 |
+
config.hidden_size // config.num_attention_heads,
|
| 356 |
+
config.max_position_embeddings * 2,
|
| 357 |
+
config.rope_theta,
|
| 358 |
+
rope_scale,
|
| 359 |
+
)
|
| 360 |
+
|
| 361 |
+
def forward(
|
| 362 |
+
self,
|
| 363 |
+
input_ids: torch.LongTensor = None,
|
| 364 |
+
start_pos: Optional[int] = 0,
|
| 365 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 366 |
+
chunk_prefilling: bool = False,
|
| 367 |
+
):
|
| 368 |
+
if inputs_embeds is None:
|
| 369 |
+
inputs_embeds = self.embed_tokens(input_ids)
|
| 370 |
+
seqlen = inputs_embeds.shape[1]
|
| 371 |
+
|
| 372 |
+
self.freqs = self.freqs.to(inputs_embeds.device)
|
| 373 |
+
freqs = self.freqs[start_pos : start_pos + seqlen]
|
| 374 |
+
|
| 375 |
+
mask = None
|
| 376 |
+
if seqlen > 1:
|
| 377 |
+
mask = torch.full(
|
| 378 |
+
(1, 1, seqlen, seqlen), float("-inf"), device=inputs_embeds.device
|
| 379 |
+
)
|
| 380 |
+
mask = torch.triu(mask, diagonal=1).type_as(inputs_embeds)
|
| 381 |
+
if chunk_prefilling:
|
| 382 |
+
mask_history = torch.zeros(
|
| 383 |
+
(1, 1, seqlen, start_pos),
|
| 384 |
+
dtype=torch.float16,
|
| 385 |
+
device=inputs_embeds.device,
|
| 386 |
+
).type_as(inputs_embeds)
|
| 387 |
+
mask = torch.cat((mask_history, mask), dim=-1)
|
| 388 |
+
x = inputs_embeds
|
| 389 |
+
|
| 390 |
+
for decoder_layer in self.layers:
|
| 391 |
+
x = decoder_layer(x, start_pos, freqs, mask, chunk_prefilling)
|
| 392 |
+
x = x[:, -1:, :]
|
| 393 |
+
x = self.norm(x)
|
| 394 |
+
|
| 395 |
+
return x
|
| 396 |
+
|
| 397 |
+
def forwardfp16(
|
| 398 |
+
self,
|
| 399 |
+
input_ids: torch.LongTensor = None,
|
| 400 |
+
start_pos: Optional[int] = 0,
|
| 401 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 402 |
+
chunk_prefilling: bool = False,
|
| 403 |
+
):
|
| 404 |
+
if inputs_embeds is None:
|
| 405 |
+
inputs_embeds = self.embed_tokens(input_ids)
|
| 406 |
+
seqlen = inputs_embeds.shape[1]
|
| 407 |
+
|
| 408 |
+
self.freqs_cis = self.freqs_cis.to(inputs_embeds.device)
|
| 409 |
+
freqs_cis = self.freqs_cis[start_pos : start_pos + seqlen]
|
| 410 |
+
|
| 411 |
+
mask = None
|
| 412 |
+
if seqlen > 1:
|
| 413 |
+
mask = torch.full(
|
| 414 |
+
(1, 1, seqlen, seqlen), float("-inf"), device=inputs_embeds.device
|
| 415 |
+
)
|
| 416 |
+
mask = torch.triu(mask, diagonal=1).type_as(inputs_embeds)
|
| 417 |
+
if chunk_prefilling:
|
| 418 |
+
mask_history = torch.zeros(
|
| 419 |
+
(1, 1, seqlen, start_pos),
|
| 420 |
+
dtype=torch.float16,
|
| 421 |
+
device=inputs_embeds.device,
|
| 422 |
+
).type_as(inputs_embeds)
|
| 423 |
+
mask = torch.cat((mask_history, mask), dim=-1)
|
| 424 |
+
x = inputs_embeds
|
| 425 |
+
|
| 426 |
+
for decoder_layer in self.layers:
|
| 427 |
+
x = decoder_layer(x, start_pos, freqs_cis, mask, chunk_prefilling)
|
| 428 |
+
x = x[:, -1:, :]
|
| 429 |
+
x = self.norm(x)
|
| 430 |
+
|
| 431 |
+
return x
|
| 432 |
+
|
| 433 |
+
|
| 434 |
+
class Qwen2ForCausalLM(Qwen2ForCausalLM):
|
| 435 |
+
def __init__(self, config):
|
| 436 |
+
|
| 437 |
+
def skip(*args, **kwargs):
|
| 438 |
+
pass
|
| 439 |
+
|
| 440 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 441 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 442 |
+
torch.nn.init.uniform_ = skip
|
| 443 |
+
torch.nn.init.normal_ = skip
|
| 444 |
+
from transformers import modeling_utils
|
| 445 |
+
|
| 446 |
+
modeling_utils._init_weights = False
|
| 447 |
+
|
| 448 |
+
super().__init__(config)
|
| 449 |
+
self.model = Qwen2Model(config)
|
| 450 |
+
self.vocab_size = config.vocab_size
|
| 451 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 452 |
+
self.config = config
|
| 453 |
+
|
| 454 |
+
@torch.inference_mode()
|
| 455 |
+
def forward(
|
| 456 |
+
self,
|
| 457 |
+
input_ids: torch.Tensor,
|
| 458 |
+
start_pos: int = 0,
|
| 459 |
+
inputs_embeds: torch.Tensor = None,
|
| 460 |
+
chunk_prefilling: bool = False,
|
| 461 |
+
quant=True,
|
| 462 |
+
):
|
| 463 |
+
if quant:
|
| 464 |
+
outputs = self.model(
|
| 465 |
+
input_ids=input_ids,
|
| 466 |
+
inputs_embeds=inputs_embeds,
|
| 467 |
+
start_pos=start_pos,
|
| 468 |
+
chunk_prefilling=chunk_prefilling,
|
| 469 |
+
)
|
| 470 |
+
else:
|
| 471 |
+
outputs = self.model.forwardfp16(
|
| 472 |
+
input_ids=input_ids,
|
| 473 |
+
inputs_embeds=inputs_embeds,
|
| 474 |
+
start_pos=start_pos,
|
| 475 |
+
chunk_prefilling=chunk_prefilling,
|
| 476 |
+
)
|
| 477 |
+
logits = self.lm_head(outputs)
|
| 478 |
+
return logits
|
| 479 |
+
|
| 480 |
+
def benchmark(self, inputs_embeds, attention_mask, max_output=128, quant_llm=True):
|
| 481 |
+
output_list = []
|
| 482 |
+
start_pos = 0
|
| 483 |
+
for i in range(10):
|
| 484 |
+
torch.cuda.synchronize()
|
| 485 |
+
tst = time.time()
|
| 486 |
+
token = self.forward(None, start_pos, inputs_embeds, quant=quant_llm)
|
| 487 |
+
torch.cuda.synchronize()
|
| 488 |
+
ted = time.time()
|
| 489 |
+
print(
|
| 490 |
+
"LLM TTFT: {:.6f} s for {} tokens".format(
|
| 491 |
+
(ted - tst), inputs_embeds.shape[1]
|
| 492 |
+
)
|
| 493 |
+
)
|
| 494 |
+
start_pos = inputs_embeds.shape[1]
|
| 495 |
+
token = torch.argmax(token, keepdim=True)[0]
|
| 496 |
+
output_list.append(token)
|
| 497 |
+
|
| 498 |
+
torch.cuda.synchronize()
|
| 499 |
+
tst = time.time()
|
| 500 |
+
for _ in range(max_output):
|
| 501 |
+
token = self.forward(token, start_pos)
|
| 502 |
+
token = torch.argmax(token, keepdim=True)[
|
| 503 |
+
0
|
| 504 |
+
] # Only fixed-length eager decoding is supported now
|
| 505 |
+
output_list.append(token)
|
| 506 |
+
start_pos += 1
|
| 507 |
+
torch.cuda.synchronize()
|
| 508 |
+
ted = time.time()
|
| 509 |
+
print("Decoding througput: {:.6f} tokens/s".format(max_output / (ted - tst)))
|
| 510 |
+
|
| 511 |
+
return torch.cat(output_list, dim=1)
|
llm-awq/tinychat/models/vila_llama.py
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import warnings
|
| 3 |
+
import shutil
|
| 4 |
+
import torch
|
| 5 |
+
import torch.nn as nn
|
| 6 |
+
from typing import List, Optional, Tuple, Union
|
| 7 |
+
import time
|
| 8 |
+
|
| 9 |
+
from transformers import AutoConfig, PreTrainedModel
|
| 10 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 11 |
+
|
| 12 |
+
from llava.model.utils import get_model_config
|
| 13 |
+
from llava.model.language_model.builder import build_llm_and_tokenizer
|
| 14 |
+
from llava.model.multimodal_encoder.builder import build_vision_tower
|
| 15 |
+
from llava.model.multimodal_projector.builder import build_mm_projector
|
| 16 |
+
from llava.model.llava_arch import LlavaMetaModel, LlavaMetaForCausalLM
|
| 17 |
+
from .llama import LlamaForCausalLM, Transformer
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class VilaLlamaForCausalLM(LlavaMetaModel, LlavaMetaForCausalLM, PreTrainedModel):
|
| 21 |
+
def __init__(self, config):
|
| 22 |
+
super().__init__(config)
|
| 23 |
+
self.init_vlm(config)
|
| 24 |
+
|
| 25 |
+
def init_vlm(self, config=None, *args, **kwargs):
|
| 26 |
+
if (
|
| 27 |
+
hasattr(self, "llm")
|
| 28 |
+
or hasattr(self, "vision_tower")
|
| 29 |
+
or hasattr(self, "mm_projector")
|
| 30 |
+
):
|
| 31 |
+
# already initialized, skipped
|
| 32 |
+
return
|
| 33 |
+
|
| 34 |
+
model_dtype = getattr(config, "model_dtype", "torch.float16")
|
| 35 |
+
if not hasattr(config, "model_dtype"):
|
| 36 |
+
warnings.warn(
|
| 37 |
+
"model_dtype not found in config, defaulting to torch.float16."
|
| 38 |
+
)
|
| 39 |
+
config.model_dtype = model_dtype
|
| 40 |
+
|
| 41 |
+
# print("init_vlm(): config", config); input("DEBUG init_vlm")
|
| 42 |
+
cfgs = get_model_config(config)
|
| 43 |
+
if len(cfgs) == 3:
|
| 44 |
+
llm_cfg, vision_tower_cfg, mm_projector_cfg = cfgs
|
| 45 |
+
else:
|
| 46 |
+
raise ValueError(
|
| 47 |
+
"`llm_cfg` `mm_projector_cfg` `vision_tower_cfg` not found in the config."
|
| 48 |
+
)
|
| 49 |
+
# print("init_vlm():", cfgs); input("DEBUG init_vlm")
|
| 50 |
+
llm_cfg = AutoConfig.from_pretrained(llm_cfg)
|
| 51 |
+
|
| 52 |
+
# self.llm, self.tokenizer = build_llm_and_tokenizer(llm_cfg, config, *args, **kwargs)
|
| 53 |
+
self.llm = LlamaForCausalLM(llm_cfg)
|
| 54 |
+
self.vision_tower = build_vision_tower(vision_tower_cfg, config)
|
| 55 |
+
self.mm_projector = build_mm_projector(mm_projector_cfg, config)
|
| 56 |
+
|
| 57 |
+
self.post_config()
|
| 58 |
+
self.is_loaded = True
|
| 59 |
+
|
| 60 |
+
assert (
|
| 61 |
+
self.llm is not None
|
| 62 |
+
or self.vision_tower is not None
|
| 63 |
+
or self.mm_projector is not None
|
| 64 |
+
), "At least one of the components must be instantiated."
|
| 65 |
+
|
| 66 |
+
def forward(
|
| 67 |
+
self,
|
| 68 |
+
input_ids: torch.LongTensor = None,
|
| 69 |
+
start_pos: int = None,
|
| 70 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 71 |
+
position_ids: Optional[torch.LongTensor] = None,
|
| 72 |
+
past_key_values: Optional[List[torch.FloatTensor]] = None,
|
| 73 |
+
inputs_embeds: Optional[torch.FloatTensor] = None,
|
| 74 |
+
labels: Optional[torch.LongTensor] = None,
|
| 75 |
+
use_cache: Optional[bool] = None,
|
| 76 |
+
output_attentions: Optional[bool] = None,
|
| 77 |
+
output_hidden_states: Optional[bool] = None,
|
| 78 |
+
images: Optional[torch.FloatTensor] = None,
|
| 79 |
+
return_dict: Optional[bool] = None,
|
| 80 |
+
special_token: bool = False,
|
| 81 |
+
chunk_prefilling: bool = False,
|
| 82 |
+
) -> Union[Tuple, CausalLMOutputWithPast]:
|
| 83 |
+
self.freezed_module_patch()
|
| 84 |
+
if inputs_embeds is None:
|
| 85 |
+
(
|
| 86 |
+
_,
|
| 87 |
+
_,
|
| 88 |
+
_,
|
| 89 |
+
_,
|
| 90 |
+
inputs_embeds,
|
| 91 |
+
_,
|
| 92 |
+
) = self.prepare_inputs_labels_for_multimodal(
|
| 93 |
+
input_ids, position_ids, attention_mask, past_key_values, labels, images
|
| 94 |
+
)
|
| 95 |
+
if inputs_embeds is not None:
|
| 96 |
+
outputs = self.llm.forward(
|
| 97 |
+
tokens=None,
|
| 98 |
+
start_pos=start_pos,
|
| 99 |
+
inputs_embeds=inputs_embeds,
|
| 100 |
+
chunk_prefilling=chunk_prefilling,
|
| 101 |
+
)
|
| 102 |
+
else: # tokens
|
| 103 |
+
outputs = self.llm.forward(
|
| 104 |
+
tokens=input_ids,
|
| 105 |
+
start_pos=start_pos,
|
| 106 |
+
inputs_embeds=None,
|
| 107 |
+
chunk_prefilling=chunk_prefilling,
|
| 108 |
+
)
|
| 109 |
+
return outputs
|
llm-awq/tinychat/modules/__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .fused_norm import *
|
| 2 |
+
from .fused_attn import *
|
| 3 |
+
from .fused_mlp import *
|
| 4 |
+
from .fused_vision_attn import *
|
| 5 |
+
try:
|
| 6 |
+
from .fused_siglipdecoder import *
|
| 7 |
+
from .fused_internencoder import *
|
| 8 |
+
except ImportError as e:
|
| 9 |
+
print("InternVL3 model import failure. To activate, please install VILA at https://github.com/NVlabs/VILA.")
|
llm-awq/tinychat/modules/fused_attn.py
ADDED
|
@@ -0,0 +1,634 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
from torch.nn import functional as F
|
| 5 |
+
from transformers.models.llama.modeling_llama import (
|
| 6 |
+
LlamaAttention,
|
| 7 |
+
LlamaRotaryEmbedding,
|
| 8 |
+
apply_rotary_pos_emb,
|
| 9 |
+
)
|
| 10 |
+
from typing import Optional
|
| 11 |
+
from awq.quantize.qmodule import WQLinear
|
| 12 |
+
import awq_inference_engine
|
| 13 |
+
from tinychat.models.llama import apply_rotary_emb
|
| 14 |
+
import gc
|
| 15 |
+
|
| 16 |
+
import tinychat.utils.constants
|
| 17 |
+
from flash_attn import flash_attn_func
|
| 18 |
+
from tinychat.models.llama import LlamaAttentionFused
|
| 19 |
+
from tinychat.models.qwen2 import Qwen2AttentionFused
|
| 20 |
+
|
| 21 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 22 |
+
max_seq_len = tinychat.utils.constants.max_seq_len
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
class QuantLlamaRotaryEmbedding(nn.Module):
|
| 26 |
+
def __init__(self, dim, max_position_embeddings=2048, base=10000, device=None):
|
| 27 |
+
super().__init__()
|
| 28 |
+
|
| 29 |
+
self.dim = dim
|
| 30 |
+
self.max_position_embeddings = max_position_embeddings
|
| 31 |
+
self.base = base
|
| 32 |
+
inv_freq = 1.0 / (
|
| 33 |
+
self.base ** (torch.arange(0, self.dim, 2).float().to(device) / self.dim)
|
| 34 |
+
)
|
| 35 |
+
self.register_buffer("inv_freq", inv_freq)
|
| 36 |
+
# Build here to make `torch.jit.trace` work.
|
| 37 |
+
self._set_cos_sin_cache(
|
| 38 |
+
seq_len=max_position_embeddings,
|
| 39 |
+
device=self.inv_freq.device,
|
| 40 |
+
dtype=torch.get_default_dtype(),
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
def _set_cos_sin_cache(self, seq_len, device, dtype):
|
| 44 |
+
self.max_seq_len_cached = seq_len
|
| 45 |
+
t = torch.arange(
|
| 46 |
+
self.max_seq_len_cached, device=device, dtype=self.inv_freq.dtype
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
freqs = torch.einsum("i,j->ij", t, self.inv_freq)
|
| 50 |
+
# Different from paper, but it uses a different permutation in order to obtain the same calculation
|
| 51 |
+
# emb = torch.cat((freqs, freqs), dim=-1)
|
| 52 |
+
|
| 53 |
+
cos = freqs.cos()
|
| 54 |
+
sin = freqs.sin()
|
| 55 |
+
cache = torch.cat((cos, sin), dim=-1)
|
| 56 |
+
|
| 57 |
+
# self.register_buffer("cos_cached", emb.cos()[None, None, :, :].to(dtype), persistent=False)
|
| 58 |
+
# self.register_buffer("sin_cached", emb.sin()[None, None, :, :].to(dtype), persistent=False)
|
| 59 |
+
self.register_buffer("cos_sin_cache", cache.half(), persistent=False)
|
| 60 |
+
|
| 61 |
+
def forward(
|
| 62 |
+
self,
|
| 63 |
+
query: torch.Tensor,
|
| 64 |
+
key: torch.Tensor,
|
| 65 |
+
positions: torch.Tensor,
|
| 66 |
+
):
|
| 67 |
+
# Apply rotary embedding to the query and key before passing them
|
| 68 |
+
# to the attention op.
|
| 69 |
+
# print(positions.shape, query.shape, key.shape, self.cos_sin_cache.shape)
|
| 70 |
+
query = query.contiguous()
|
| 71 |
+
key = key.contiguous()
|
| 72 |
+
awq_inference_engine.rotary_embedding_neox(
|
| 73 |
+
positions,
|
| 74 |
+
query,
|
| 75 |
+
key,
|
| 76 |
+
self.dim,
|
| 77 |
+
self.cos_sin_cache,
|
| 78 |
+
)
|
| 79 |
+
return query, key
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
class QuantLlamaAttention(nn.Module):
|
| 83 |
+
"""Multi-headed attention from 'Attention Is All You Need' paper"""
|
| 84 |
+
|
| 85 |
+
def __init__(self, hidden_size, num_heads, qkv_proj, o_proj, dev):
|
| 86 |
+
super().__init__()
|
| 87 |
+
self.hidden_size = hidden_size
|
| 88 |
+
self.num_heads = num_heads
|
| 89 |
+
self.head_dim = hidden_size // num_heads
|
| 90 |
+
|
| 91 |
+
if (self.head_dim * num_heads) != self.hidden_size:
|
| 92 |
+
raise ValueError(
|
| 93 |
+
f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
|
| 94 |
+
f" and `num_heads`: {num_heads})."
|
| 95 |
+
)
|
| 96 |
+
self.qkv_proj = qkv_proj
|
| 97 |
+
self.o_proj = o_proj
|
| 98 |
+
self.rotary_emb = QuantLlamaRotaryEmbedding(
|
| 99 |
+
self.head_dim, max_position_embeddings=2048, device=dev
|
| 100 |
+
)
|
| 101 |
+
|
| 102 |
+
def forward(
|
| 103 |
+
self,
|
| 104 |
+
hidden_states,
|
| 105 |
+
past_key_value=None,
|
| 106 |
+
attention_mask=None,
|
| 107 |
+
position_ids=None,
|
| 108 |
+
output_attentions=False,
|
| 109 |
+
use_cache=False,
|
| 110 |
+
):
|
| 111 |
+
"""Input shape: Batch x Time x Channel"""
|
| 112 |
+
|
| 113 |
+
bsz, q_len, _ = hidden_states.size()
|
| 114 |
+
|
| 115 |
+
qkv_states = self.qkv_proj(hidden_states)
|
| 116 |
+
qkv_states = qkv_states.view(bsz, q_len, 3, self.num_heads, self.head_dim)
|
| 117 |
+
|
| 118 |
+
# This updates the query and key states in-place, saving VRAM.
|
| 119 |
+
query_states, key_states, value_states = torch.split(qkv_states, 1, dim=2)
|
| 120 |
+
query_states, key_states = self.rotary_emb(
|
| 121 |
+
query_states, key_states, position_ids
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
del qkv_states
|
| 125 |
+
query_states = query_states.view(
|
| 126 |
+
bsz, q_len, self.num_heads, self.head_dim
|
| 127 |
+
).transpose(1, 2)
|
| 128 |
+
key_states = key_states.view(
|
| 129 |
+
bsz, q_len, self.num_heads, self.head_dim
|
| 130 |
+
).transpose(1, 2)
|
| 131 |
+
value_states = value_states.view(
|
| 132 |
+
bsz, q_len, self.num_heads, self.head_dim
|
| 133 |
+
).transpose(1, 2)
|
| 134 |
+
|
| 135 |
+
is_causal = past_key_value is None
|
| 136 |
+
|
| 137 |
+
kv_seq_len = q_len
|
| 138 |
+
if past_key_value is not None:
|
| 139 |
+
kv_seq_len += past_key_value[0].shape[-2]
|
| 140 |
+
|
| 141 |
+
value_states = value_states.to("cuda:0")
|
| 142 |
+
|
| 143 |
+
if past_key_value is not None:
|
| 144 |
+
# reuse k, v, self_attention
|
| 145 |
+
key_states = torch.cat([past_key_value[0], key_states], dim=2)
|
| 146 |
+
value_states = torch.cat([past_key_value[1], value_states], dim=2)
|
| 147 |
+
|
| 148 |
+
if use_cache:
|
| 149 |
+
# Since qkv_proj is fused, query_states etc will hold a reference to the original qkv_states tensor
|
| 150 |
+
# which can cause excessive memory usage by the cache. `contiguous` is a convenient way to workaround this.
|
| 151 |
+
key_states = key_states.contiguous()
|
| 152 |
+
value_states = value_states.contiguous()
|
| 153 |
+
query_states = query_states.contiguous()
|
| 154 |
+
|
| 155 |
+
past_key_value = (key_states, value_states) if use_cache else None
|
| 156 |
+
|
| 157 |
+
# with torch.backends.cuda.sdp_kernel(enable_math=False):
|
| 158 |
+
attn_output = F.scaled_dot_product_attention(
|
| 159 |
+
query_states, key_states, value_states, is_causal=is_causal
|
| 160 |
+
)
|
| 161 |
+
del query_states, key_states, value_states
|
| 162 |
+
|
| 163 |
+
attn_output = attn_output.transpose(1, 2).reshape(bsz, q_len, self.hidden_size)
|
| 164 |
+
attn_output = self.o_proj(attn_output)
|
| 165 |
+
|
| 166 |
+
return attn_output, None, past_key_value
|
| 167 |
+
|
| 168 |
+
|
| 169 |
+
class QuantLlamaAttentionFused(nn.Module):
|
| 170 |
+
def __init__(
|
| 171 |
+
self, hidden_size, num_heads, kv_max_seq_len, qkv_layer, o_proj, dev, args
|
| 172 |
+
):
|
| 173 |
+
super().__init__()
|
| 174 |
+
|
| 175 |
+
self.args = args
|
| 176 |
+
self.n_local_heads = args.num_attention_heads
|
| 177 |
+
self.hidden_size = args.hidden_size
|
| 178 |
+
self.num_heads = args.num_attention_heads
|
| 179 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 180 |
+
|
| 181 |
+
self.num_key_value_heads = args.num_key_value_heads
|
| 182 |
+
self.num_key_value_groups = self.num_heads // self.num_key_value_heads
|
| 183 |
+
self.max_position_embeddings = args.max_position_embeddings
|
| 184 |
+
self.rope_theta = args.rope_theta
|
| 185 |
+
self.rope_scaling = args.rope_scaling
|
| 186 |
+
if self.rope_scaling is None:
|
| 187 |
+
self.rope_scaling = 1.0
|
| 188 |
+
if isinstance(self.rope_scaling, dict):
|
| 189 |
+
self.rope_scaling = self.rope_scaling.get("factor", 1.0)
|
| 190 |
+
|
| 191 |
+
self.qkv_proj = qkv_layer
|
| 192 |
+
self.o_proj = o_proj
|
| 193 |
+
|
| 194 |
+
self.kv_max_seq_len = kv_max_seq_len
|
| 195 |
+
|
| 196 |
+
# following fastertransformer definition
|
| 197 |
+
self.cache_v = (
|
| 198 |
+
torch.zeros(
|
| 199 |
+
(
|
| 200 |
+
max_batch_size,
|
| 201 |
+
self.num_key_value_heads,
|
| 202 |
+
# args.max_position_embeddings,
|
| 203 |
+
self.kv_max_seq_len,
|
| 204 |
+
self.head_dim,
|
| 205 |
+
)
|
| 206 |
+
)
|
| 207 |
+
.to(dev)
|
| 208 |
+
.half()
|
| 209 |
+
) # added to half
|
| 210 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 211 |
+
self.cache_k = (
|
| 212 |
+
torch.zeros(
|
| 213 |
+
(
|
| 214 |
+
max_batch_size,
|
| 215 |
+
self.num_key_value_heads,
|
| 216 |
+
self.head_dim // 8,
|
| 217 |
+
# args.max_position_embeddings,
|
| 218 |
+
self.kv_max_seq_len,
|
| 219 |
+
8,
|
| 220 |
+
)
|
| 221 |
+
)
|
| 222 |
+
.to(dev)
|
| 223 |
+
.half()
|
| 224 |
+
) # added to half
|
| 225 |
+
|
| 226 |
+
def forward(
|
| 227 |
+
self,
|
| 228 |
+
x: torch.Tensor,
|
| 229 |
+
start_pos: int,
|
| 230 |
+
freqs: torch.Tensor,
|
| 231 |
+
mask: Optional[torch.Tensor],
|
| 232 |
+
chunk_prefilling: bool = False,
|
| 233 |
+
):
|
| 234 |
+
bsz, seqlen, _ = x.shape
|
| 235 |
+
xqkv = self.qkv_proj(x)
|
| 236 |
+
xqkv = xqkv.view(
|
| 237 |
+
bsz,
|
| 238 |
+
seqlen,
|
| 239 |
+
self.n_local_heads + self.num_key_value_heads * 2,
|
| 240 |
+
self.head_dim,
|
| 241 |
+
)
|
| 242 |
+
xq = xqkv[:, :, 0 : self.n_local_heads]
|
| 243 |
+
xk = xqkv[
|
| 244 |
+
:, :, self.n_local_heads : (self.n_local_heads + self.num_key_value_heads)
|
| 245 |
+
]
|
| 246 |
+
xv = xqkv[:, :, -self.num_key_value_heads :]
|
| 247 |
+
|
| 248 |
+
if seqlen > 1:
|
| 249 |
+
xq = xq.view(bsz, seqlen, self.n_local_heads, self.head_dim)
|
| 250 |
+
xk = xk.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 251 |
+
xv = xv.view(bsz, seqlen, self.num_key_value_heads, self.head_dim)
|
| 252 |
+
|
| 253 |
+
xq = awq_inference_engine.fused_rope_with_pos_forward_func(xq, freqs, True)
|
| 254 |
+
xk = awq_inference_engine.fused_rope_with_pos_forward_func(xk, freqs, True)
|
| 255 |
+
|
| 256 |
+
self.cache_k = self.cache_k.to(xq)
|
| 257 |
+
self.cache_v = self.cache_v.to(xq)
|
| 258 |
+
|
| 259 |
+
values_store = xv.transpose(2, 1)
|
| 260 |
+
keys_store = (
|
| 261 |
+
xk.reshape(bsz, seqlen, self.num_key_value_heads, self.head_dim // 8, 8)
|
| 262 |
+
.permute(0, 2, 3, 1, 4)
|
| 263 |
+
.contiguous()
|
| 264 |
+
)
|
| 265 |
+
|
| 266 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 267 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 268 |
+
if chunk_prefilling:
|
| 269 |
+
keys = self.cache_k[:, :, :, 0:start_pos, :]
|
| 270 |
+
keys = (
|
| 271 |
+
keys.permute(0, 3, 1, 2, 4)
|
| 272 |
+
.reshape(bsz, start_pos, self.num_key_value_heads, self.head_dim)
|
| 273 |
+
.contiguous()
|
| 274 |
+
)
|
| 275 |
+
keys = torch.cat((keys, xk), dim=1)
|
| 276 |
+
values = self.cache_v[:, :, 0:start_pos, :]
|
| 277 |
+
values = (
|
| 278 |
+
values.transpose(2, 1)
|
| 279 |
+
.reshape(bsz, start_pos, self.num_key_value_heads, self.head_dim)
|
| 280 |
+
.contiguous()
|
| 281 |
+
)
|
| 282 |
+
values = torch.cat((values, xv), dim=1)
|
| 283 |
+
else:
|
| 284 |
+
keys = xk
|
| 285 |
+
values = xv
|
| 286 |
+
|
| 287 |
+
keys = torch.repeat_interleave(
|
| 288 |
+
keys, dim=2, repeats=self.num_key_value_groups
|
| 289 |
+
)
|
| 290 |
+
values = torch.repeat_interleave(
|
| 291 |
+
values, dim=2, repeats=self.num_key_value_groups
|
| 292 |
+
)
|
| 293 |
+
|
| 294 |
+
xq = xq.transpose(1, 2)
|
| 295 |
+
keys = keys.transpose(1, 2)
|
| 296 |
+
values = values.transpose(1, 2)
|
| 297 |
+
scores = torch.matmul(xq, keys.transpose(2, 3)) / math.sqrt(self.head_dim)
|
| 298 |
+
if mask is not None:
|
| 299 |
+
scores = scores + mask # (bs, n_local_heads, slen, cache_len + slen)
|
| 300 |
+
scores = F.softmax(scores.float(), dim=-1).type_as(xq)
|
| 301 |
+
output = torch.matmul(scores, values) # (bs, n_local_heads, slen, head_dim)
|
| 302 |
+
output = output.transpose(1, 2).contiguous().view(bsz, seqlen, -1)
|
| 303 |
+
else:
|
| 304 |
+
xq = xq.view(bsz, self.n_local_heads, self.head_dim)
|
| 305 |
+
xk = xk.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 306 |
+
xv = xv.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 307 |
+
|
| 308 |
+
output = awq_inference_engine.single_query_attention(
|
| 309 |
+
xq,
|
| 310 |
+
xk,
|
| 311 |
+
xv,
|
| 312 |
+
self.cache_k,
|
| 313 |
+
self.cache_v,
|
| 314 |
+
None,
|
| 315 |
+
None,
|
| 316 |
+
start_pos,
|
| 317 |
+
self.head_dim,
|
| 318 |
+
self.rope_theta,
|
| 319 |
+
self.rope_scaling,
|
| 320 |
+
True,
|
| 321 |
+
)
|
| 322 |
+
output = output.reshape(bsz, 1, -1)
|
| 323 |
+
|
| 324 |
+
return self.o_proj(output)
|
| 325 |
+
|
| 326 |
+
|
| 327 |
+
class QuantLlamaAttentionFusedFlash(nn.Module):
|
| 328 |
+
"""Flash_attn_func from 'Flash{A}ttention-2: Faster Attention with Better Parallelism and Work Partitioning' paper"""
|
| 329 |
+
|
| 330 |
+
"""This function is faster than the varlen one but only supports single-batch inference"""
|
| 331 |
+
|
| 332 |
+
def __init__(
|
| 333 |
+
self, hidden_size, num_heads, kv_max_seq_len, qkv_layer, o_proj, dev, args
|
| 334 |
+
):
|
| 335 |
+
super().__init__()
|
| 336 |
+
|
| 337 |
+
self.args = args
|
| 338 |
+
self.n_local_heads = args.num_attention_heads
|
| 339 |
+
self.hidden_size = args.hidden_size
|
| 340 |
+
self.num_heads = args.num_attention_heads
|
| 341 |
+
self.head_dim = self.hidden_size // self.num_heads
|
| 342 |
+
|
| 343 |
+
self.num_key_value_heads = args.num_key_value_heads
|
| 344 |
+
self.num_key_value_groups = self.num_heads // self.num_key_value_heads
|
| 345 |
+
self.max_position_embeddings = args.max_position_embeddings
|
| 346 |
+
self.rope_theta = args.rope_theta
|
| 347 |
+
self.rope_scaling = args.rope_scaling
|
| 348 |
+
if self.rope_scaling is None:
|
| 349 |
+
self.rope_scaling = 1.0
|
| 350 |
+
elif isinstance(self.rope_scaling, dict):
|
| 351 |
+
self.rope_scaling = self.rope_scaling.get("factor", 1.0)
|
| 352 |
+
|
| 353 |
+
self.qkv_proj = qkv_layer
|
| 354 |
+
self.o_proj = o_proj
|
| 355 |
+
|
| 356 |
+
self.kv_max_seq_len = kv_max_seq_len
|
| 357 |
+
# following fastertransformer definition
|
| 358 |
+
# For short seqlence, we use fused kernel to accelerate decoding.
|
| 359 |
+
if self.kv_max_seq_len <= 8192:
|
| 360 |
+
self.cache_v = (
|
| 361 |
+
torch.zeros(
|
| 362 |
+
(
|
| 363 |
+
max_batch_size,
|
| 364 |
+
self.num_key_value_heads,
|
| 365 |
+
# args.max_position_embeddings,
|
| 366 |
+
self.kv_max_seq_len,
|
| 367 |
+
self.head_dim,
|
| 368 |
+
)
|
| 369 |
+
)
|
| 370 |
+
.to(dev)
|
| 371 |
+
.half()
|
| 372 |
+
) # added to half
|
| 373 |
+
# 8: pack 8 fp16 in FT, if fp32 then use 4
|
| 374 |
+
self.cache_k = (
|
| 375 |
+
torch.zeros(
|
| 376 |
+
(
|
| 377 |
+
max_batch_size,
|
| 378 |
+
self.num_key_value_heads,
|
| 379 |
+
self.head_dim // 8,
|
| 380 |
+
# args.max_position_embeddings,
|
| 381 |
+
kv_max_seq_len,
|
| 382 |
+
8,
|
| 383 |
+
)
|
| 384 |
+
)
|
| 385 |
+
.to(dev)
|
| 386 |
+
.half()
|
| 387 |
+
) # added to half
|
| 388 |
+
self.forward = self.short_forward
|
| 389 |
+
# For long sequence, we use flash attantion for both prefilling and decoding to avoid OOM.
|
| 390 |
+
else:
|
| 391 |
+
self.cache_v = (
|
| 392 |
+
torch.zeros(
|
| 393 |
+
(
|
| 394 |
+
max_batch_size,
|
| 395 |
+
self.kv_max_seq_len,
|
| 396 |
+
self.num_key_value_heads,
|
| 397 |
+
self.head_dim,
|
| 398 |
+
)
|
| 399 |
+
)
|
| 400 |
+
.to(dev)
|
| 401 |
+
.half()
|
| 402 |
+
) # added to half
|
| 403 |
+
self.cache_k = (
|
| 404 |
+
torch.zeros(
|
| 405 |
+
(
|
| 406 |
+
max_batch_size,
|
| 407 |
+
self.kv_max_seq_len,
|
| 408 |
+
self.num_key_value_heads,
|
| 409 |
+
self.head_dim,
|
| 410 |
+
)
|
| 411 |
+
)
|
| 412 |
+
.to(dev)
|
| 413 |
+
.half()
|
| 414 |
+
) # added to half
|
| 415 |
+
self.forward = self.long_forward
|
| 416 |
+
|
| 417 |
+
def short_forward(
|
| 418 |
+
self,
|
| 419 |
+
x: torch.Tensor,
|
| 420 |
+
start_pos: int,
|
| 421 |
+
freqs: torch.Tensor,
|
| 422 |
+
mask: Optional[torch.Tensor],
|
| 423 |
+
chunk_prefilling: bool = False,
|
| 424 |
+
):
|
| 425 |
+
bsz, seqlen, _ = x.shape
|
| 426 |
+
xqkv = self.qkv_proj(x)
|
| 427 |
+
xqkv = xqkv.view(
|
| 428 |
+
bsz,
|
| 429 |
+
seqlen,
|
| 430 |
+
self.n_local_heads + self.num_key_value_heads * 2,
|
| 431 |
+
self.head_dim,
|
| 432 |
+
)
|
| 433 |
+
xq = xqkv[:, :, 0 : self.n_local_heads]
|
| 434 |
+
xk = xqkv[
|
| 435 |
+
:, :, self.n_local_heads : (self.n_local_heads + self.num_key_value_heads)
|
| 436 |
+
]
|
| 437 |
+
xv = xqkv[:, :, -self.num_key_value_heads :]
|
| 438 |
+
|
| 439 |
+
if seqlen > 1:
|
| 440 |
+
xq = awq_inference_engine.fused_rope_with_pos_forward_func(xq, freqs, True)
|
| 441 |
+
xk = awq_inference_engine.fused_rope_with_pos_forward_func(xk, freqs, True)
|
| 442 |
+
|
| 443 |
+
self.cache_k = self.cache_k.to(xq)
|
| 444 |
+
self.cache_v = self.cache_v.to(xq)
|
| 445 |
+
|
| 446 |
+
values_store = xv.transpose(2, 1)
|
| 447 |
+
keys_store = (
|
| 448 |
+
xk.reshape(bsz, seqlen, self.num_key_value_heads, self.head_dim // 8, 8)
|
| 449 |
+
.permute(0, 2, 3, 1, 4)
|
| 450 |
+
.contiguous()
|
| 451 |
+
)
|
| 452 |
+
|
| 453 |
+
self.cache_v[:bsz, :, start_pos : start_pos + seqlen, :] = values_store
|
| 454 |
+
self.cache_k[:bsz, :, :, start_pos : start_pos + seqlen, :] = keys_store
|
| 455 |
+
|
| 456 |
+
if chunk_prefilling:
|
| 457 |
+
keys = self.cache_k[:, :, :, 0 : start_pos + seqlen, :]
|
| 458 |
+
keys = (
|
| 459 |
+
keys.permute(0, 3, 1, 2, 4)
|
| 460 |
+
.reshape(
|
| 461 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 462 |
+
)
|
| 463 |
+
.contiguous()
|
| 464 |
+
)
|
| 465 |
+
values = self.cache_v[:, :, 0 : start_pos + seqlen, :]
|
| 466 |
+
values = (
|
| 467 |
+
values.transpose(2, 1)
|
| 468 |
+
.reshape(
|
| 469 |
+
bsz, start_pos + seqlen, self.num_key_value_heads, self.head_dim
|
| 470 |
+
)
|
| 471 |
+
.contiguous()
|
| 472 |
+
)
|
| 473 |
+
else:
|
| 474 |
+
keys = xk
|
| 475 |
+
values = xv
|
| 476 |
+
|
| 477 |
+
output = flash_attn_func(
|
| 478 |
+
q=xq,
|
| 479 |
+
k=keys,
|
| 480 |
+
v=values,
|
| 481 |
+
causal=True,
|
| 482 |
+
)
|
| 483 |
+
output = output.contiguous().view(bsz, seqlen, -1)
|
| 484 |
+
else:
|
| 485 |
+
xq = xq.view(bsz, self.n_local_heads, self.head_dim)
|
| 486 |
+
xk = xk.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 487 |
+
xv = xv.view(bsz, self.num_key_value_heads, self.head_dim)
|
| 488 |
+
output = awq_inference_engine.single_query_attention(
|
| 489 |
+
xq,
|
| 490 |
+
xk,
|
| 491 |
+
xv,
|
| 492 |
+
self.cache_k,
|
| 493 |
+
self.cache_v,
|
| 494 |
+
None,
|
| 495 |
+
None,
|
| 496 |
+
start_pos,
|
| 497 |
+
self.head_dim,
|
| 498 |
+
self.rope_theta,
|
| 499 |
+
self.rope_scaling,
|
| 500 |
+
True,
|
| 501 |
+
)
|
| 502 |
+
output = output.reshape(bsz, 1, -1)
|
| 503 |
+
return self.o_proj(output)
|
| 504 |
+
|
| 505 |
+
def long_forward(
|
| 506 |
+
self,
|
| 507 |
+
x: torch.Tensor,
|
| 508 |
+
start_pos: int,
|
| 509 |
+
freqs: torch.Tensor,
|
| 510 |
+
mask: Optional[torch.Tensor],
|
| 511 |
+
chunk_prefilling: bool = False,
|
| 512 |
+
):
|
| 513 |
+
bsz, seqlen, _ = x.shape
|
| 514 |
+
xqkv = self.qkv_proj(x)
|
| 515 |
+
xqkv = xqkv.view(
|
| 516 |
+
bsz,
|
| 517 |
+
seqlen,
|
| 518 |
+
self.n_local_heads + self.num_key_value_heads * 2,
|
| 519 |
+
self.head_dim,
|
| 520 |
+
)
|
| 521 |
+
xq = xqkv[:, :, 0 : self.n_local_heads]
|
| 522 |
+
xk = xqkv[
|
| 523 |
+
:, :, self.n_local_heads : (self.n_local_heads + self.num_key_value_heads)
|
| 524 |
+
]
|
| 525 |
+
xv = xqkv[:, :, -self.num_key_value_heads :]
|
| 526 |
+
|
| 527 |
+
xq = awq_inference_engine.fused_rope_with_pos_forward_func(xq, freqs, True)
|
| 528 |
+
xk = awq_inference_engine.fused_rope_with_pos_forward_func(xk, freqs, True)
|
| 529 |
+
|
| 530 |
+
self.cache_k = self.cache_k.to(xq)
|
| 531 |
+
self.cache_v = self.cache_v.to(xq)
|
| 532 |
+
|
| 533 |
+
self.cache_v[:bsz, start_pos : start_pos + seqlen] = xv
|
| 534 |
+
self.cache_k[:bsz, start_pos : start_pos + seqlen] = xk
|
| 535 |
+
|
| 536 |
+
keys = self.cache_k[:, 0 : start_pos + seqlen]
|
| 537 |
+
values = self.cache_v[:, 0 : start_pos + seqlen]
|
| 538 |
+
|
| 539 |
+
output = flash_attn_func(
|
| 540 |
+
q=xq,
|
| 541 |
+
k=keys,
|
| 542 |
+
v=values,
|
| 543 |
+
causal=True,
|
| 544 |
+
)
|
| 545 |
+
output = output.view(bsz, seqlen, -1)
|
| 546 |
+
return self.o_proj(output)
|
| 547 |
+
|
| 548 |
+
|
| 549 |
+
def make_quant_attn(model, dev, flash_attn=True):
|
| 550 |
+
"""
|
| 551 |
+
Replace all LlamaAttention modules with QuantLlamaAttention modules, fusing the q, k, v projections.
|
| 552 |
+
"""
|
| 553 |
+
model = model.cpu()
|
| 554 |
+
for name, m in model.named_modules():
|
| 555 |
+
if not m.__class__.__name__ in [
|
| 556 |
+
"LlamaAttention",
|
| 557 |
+
"LlamaAttentionFused",
|
| 558 |
+
"Qwen2AttentionFused",
|
| 559 |
+
]:
|
| 560 |
+
continue
|
| 561 |
+
|
| 562 |
+
q_proj = m.q_proj
|
| 563 |
+
k_proj = m.k_proj
|
| 564 |
+
v_proj = m.v_proj
|
| 565 |
+
|
| 566 |
+
qweights = torch.cat([q_proj.qweight, k_proj.qweight, v_proj.qweight], dim=0)
|
| 567 |
+
scaled_zeros = torch.cat(
|
| 568 |
+
[q_proj.scaled_zeros, k_proj.scaled_zeros, v_proj.scaled_zeros], dim=1
|
| 569 |
+
).contiguous()
|
| 570 |
+
scales = torch.cat(
|
| 571 |
+
[q_proj.scales, k_proj.scales, v_proj.scales], dim=1
|
| 572 |
+
).contiguous()
|
| 573 |
+
# g_idx = torch.cat([q_proj.g_idx, k_proj.g_idx, v_proj.g_idx], dim=0)
|
| 574 |
+
g_idx = None
|
| 575 |
+
bias = (
|
| 576 |
+
torch.cat([q_proj.bias, k_proj.bias, v_proj.bias], dim=0)
|
| 577 |
+
if q_proj.bias is not None
|
| 578 |
+
else None
|
| 579 |
+
)
|
| 580 |
+
|
| 581 |
+
qkv_layer = WQLinear(
|
| 582 |
+
q_proj.w_bit,
|
| 583 |
+
q_proj.group_size,
|
| 584 |
+
q_proj.in_features,
|
| 585 |
+
q_proj.out_features + k_proj.out_features + v_proj.out_features,
|
| 586 |
+
q_proj.bias is not None,
|
| 587 |
+
q_proj.qweight.device,
|
| 588 |
+
)
|
| 589 |
+
qkv_layer.qweight = qweights
|
| 590 |
+
qkv_layer.scaled_zeros = scaled_zeros
|
| 591 |
+
qkv_layer.scales = scales
|
| 592 |
+
|
| 593 |
+
qkv_layer.bias = bias
|
| 594 |
+
qkv_layer.split_k_iters = q_proj.split_k_iters
|
| 595 |
+
# We're dropping the rotary embedding layer m.rotary_emb here. We don't need it in the triton branch.
|
| 596 |
+
if isinstance(m, LlamaAttention):
|
| 597 |
+
attn = QuantLlamaAttention(
|
| 598 |
+
m.hidden_size, m.num_heads, qkv_layer, m.o_proj, dev
|
| 599 |
+
)
|
| 600 |
+
else:
|
| 601 |
+
if flash_attn:
|
| 602 |
+
attn = QuantLlamaAttentionFusedFlash(
|
| 603 |
+
m.args.hidden_size,
|
| 604 |
+
m.args.num_attention_heads,
|
| 605 |
+
m.kv_max_seq_len,
|
| 606 |
+
qkv_layer,
|
| 607 |
+
m.o_proj,
|
| 608 |
+
dev,
|
| 609 |
+
m.args,
|
| 610 |
+
)
|
| 611 |
+
else:
|
| 612 |
+
attn = QuantLlamaAttentionFused(
|
| 613 |
+
m.args.hidden_size,
|
| 614 |
+
m.args.num_attention_heads,
|
| 615 |
+
m.kv_max_seq_len,
|
| 616 |
+
qkv_layer,
|
| 617 |
+
m.o_proj,
|
| 618 |
+
dev,
|
| 619 |
+
m.args,
|
| 620 |
+
)
|
| 621 |
+
if "." in name:
|
| 622 |
+
parent_name = name.rsplit(".", 1)[0]
|
| 623 |
+
child_name = name[len(parent_name) + 1 :]
|
| 624 |
+
parent = model.get_submodule(parent_name)
|
| 625 |
+
else:
|
| 626 |
+
parent_name = ""
|
| 627 |
+
parent = model
|
| 628 |
+
child_name = name
|
| 629 |
+
|
| 630 |
+
# print(f"Replacing {name} with quant_attn; parent: {parent_name}, child's name: {child_name}")
|
| 631 |
+
setattr(parent, child_name, attn)
|
| 632 |
+
gc.collect()
|
| 633 |
+
torch.cuda.empty_cache()
|
| 634 |
+
model = model.to(dev)
|
llm-awq/tinychat/modules/fused_internencoder.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Tuple, Union
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
import torch.utils.checkpoint
|
| 6 |
+
from einops import rearrange
|
| 7 |
+
from timm.layers import DropPath
|
| 8 |
+
from torch import nn
|
| 9 |
+
from transformers.activations import ACT2FN
|
| 10 |
+
from transformers.modeling_outputs import (BaseModelOutput,
|
| 11 |
+
BaseModelOutputWithPooling)
|
| 12 |
+
from transformers.modeling_utils import PreTrainedModel
|
| 13 |
+
from transformers.utils import logging
|
| 14 |
+
|
| 15 |
+
from awq.quantize import W8A8OF16LinearDynamicInputScale
|
| 16 |
+
import awq_inference_engine
|
| 17 |
+
|
| 18 |
+
from tinychat.models.internvl.internvit import (FlashAttention,
|
| 19 |
+
InternRMSNorm,
|
| 20 |
+
InternVisionEmbeddings,
|
| 21 |
+
InternAttention,
|
| 22 |
+
InternMLP,
|
| 23 |
+
InternVisionEncoderLayer,
|
| 24 |
+
InternVisionEncoder)
|
| 25 |
+
from tinychat.models.internvl.configuration_internvl import InternVisionConfig
|
| 26 |
+
|
| 27 |
+
try:
|
| 28 |
+
from flash_attn.bert_padding import pad_input, unpad_input
|
| 29 |
+
from flash_attn.flash_attn_interface import \
|
| 30 |
+
flash_attn_varlen_qkvpacked_func
|
| 31 |
+
has_flash_attn = True
|
| 32 |
+
except:
|
| 33 |
+
print('FlashAttention2 is not installed.')
|
| 34 |
+
has_flash_attn = False
|
| 35 |
+
|
| 36 |
+
logger = logging.get_logger(__name__)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class QuantInternVisionEncoder(nn.Module):
|
| 40 |
+
"""
|
| 41 |
+
Transformer encoder consisting of `config.num_hidden_layers` self attention layers. Each layer is a
|
| 42 |
+
[`InternEncoderLayer`].
|
| 43 |
+
|
| 44 |
+
Args:
|
| 45 |
+
config (`InternConfig`):
|
| 46 |
+
The corresponding vision configuration for the `InternEncoder`.
|
| 47 |
+
"""
|
| 48 |
+
|
| 49 |
+
def __init__(self, module: InternVisionEncoder, bsz=64, seqlen=1024):
|
| 50 |
+
super().__init__()
|
| 51 |
+
self.config = module.config
|
| 52 |
+
# stochastic depth decay rule
|
| 53 |
+
self.layers = nn.ModuleList([QuantInternVisionEncoderLayer(layer, self.config) for layer in module.layers])
|
| 54 |
+
self.gradient_checkpointing = True
|
| 55 |
+
self.bsz = bsz
|
| 56 |
+
self.seqlen = seqlen
|
| 57 |
+
|
| 58 |
+
def forward(
|
| 59 |
+
self,
|
| 60 |
+
inputs_embeds,
|
| 61 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 62 |
+
output_attentions: Optional[bool] = None,
|
| 63 |
+
output_hidden_states: Optional[bool] = None,
|
| 64 |
+
return_dict: Optional[bool] = None,
|
| 65 |
+
) -> Union[Tuple, BaseModelOutput]:
|
| 66 |
+
r"""
|
| 67 |
+
Args:
|
| 68 |
+
inputs_embeds (`torch.FloatTensor` of shape `(batch_size, sequence_length, hidden_size)`):
|
| 69 |
+
Embedded representation of the inputs. Should be float, not int tokens.
|
| 70 |
+
output_hidden_states (`bool`, *optional*):
|
| 71 |
+
Whether or not to return the hidden states of all layers. See `hidden_states` under returned tensors
|
| 72 |
+
for more detail.
|
| 73 |
+
return_dict (`bool`, *optional*):
|
| 74 |
+
Whether or not to return a [`~utils.ModelOutput`] instead of a plain tuple.
|
| 75 |
+
"""
|
| 76 |
+
output_hidden_states = (
|
| 77 |
+
output_hidden_states if output_hidden_states is not None else self.config.output_hidden_states
|
| 78 |
+
)
|
| 79 |
+
return_dict = return_dict if return_dict is not None else self.config.use_return_dict
|
| 80 |
+
|
| 81 |
+
encoder_states = () if output_hidden_states else None
|
| 82 |
+
hidden_states = inputs_embeds
|
| 83 |
+
|
| 84 |
+
for idx, encoder_layer in enumerate(self.layers):
|
| 85 |
+
if output_hidden_states:
|
| 86 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 87 |
+
if self.gradient_checkpointing and self.training:
|
| 88 |
+
layer_outputs = torch.utils.checkpoint.checkpoint(
|
| 89 |
+
encoder_layer,
|
| 90 |
+
hidden_states)
|
| 91 |
+
else:
|
| 92 |
+
layer_outputs = encoder_layer(
|
| 93 |
+
hidden_states,
|
| 94 |
+
)
|
| 95 |
+
hidden_states = layer_outputs
|
| 96 |
+
|
| 97 |
+
if output_hidden_states:
|
| 98 |
+
encoder_states = encoder_states + (hidden_states,)
|
| 99 |
+
|
| 100 |
+
if not return_dict:
|
| 101 |
+
return tuple(v for v in [hidden_states, encoder_states] if v is not None)
|
| 102 |
+
return BaseModelOutput(
|
| 103 |
+
last_hidden_state=hidden_states, hidden_states=encoder_states
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
class QuantInternRMSNorm(nn.Module):
|
| 107 |
+
def __init__(self, module: nn.Module, use_per_token_quant=True):
|
| 108 |
+
super().__init__()
|
| 109 |
+
self.weight = nn.Parameter(module.weight.data, requires_grad=False)
|
| 110 |
+
self.bias = nn.Parameter(module.bias.data, requires_grad=False)
|
| 111 |
+
self.variance_epsilon = module.eps
|
| 112 |
+
self.use_per_token_quant = use_per_token_quant
|
| 113 |
+
|
| 114 |
+
def forward(self, hidden_states):
|
| 115 |
+
bsz, seqlen, hidden_size = hidden_states.shape
|
| 116 |
+
output = torch.empty((bsz * seqlen), hidden_size, device=hidden_states.device, dtype=torch.int8)
|
| 117 |
+
scale = torch.empty((bsz * seqlen), device=hidden_states.device, dtype=hidden_states.dtype)
|
| 118 |
+
awq_inference_engine.rms_norm_general(
|
| 119 |
+
output,
|
| 120 |
+
hidden_states,
|
| 121 |
+
self.weight,
|
| 122 |
+
self.bias,
|
| 123 |
+
scale,
|
| 124 |
+
self.variance_epsilon,
|
| 125 |
+
self.use_per_token_quant,
|
| 126 |
+
)
|
| 127 |
+
return output, scale
|
| 128 |
+
|
| 129 |
+
class QuantInternAttention(nn.Module):
|
| 130 |
+
def __init__(self, module: InternAttention, config: InternVisionConfig, init_only=False):
|
| 131 |
+
super().__init__()
|
| 132 |
+
self.config = config
|
| 133 |
+
self.embed_dim = module.embed_dim
|
| 134 |
+
self.num_heads = module.num_heads
|
| 135 |
+
self.head_dim = self.embed_dim // self.num_heads
|
| 136 |
+
self.scale = module.scale
|
| 137 |
+
self.use_flash_attn = config.use_flash_attn
|
| 138 |
+
|
| 139 |
+
self.qkv = W8A8OF16LinearDynamicInputScale.from_linear(module.qkv, init_only=init_only)
|
| 140 |
+
self.proj = W8A8OF16LinearDynamicInputScale.from_linear(module.proj, init_only=init_only)
|
| 141 |
+
|
| 142 |
+
self.qk_normalization = module.qk_normalization
|
| 143 |
+
if self.qk_normalization:
|
| 144 |
+
self.q_norm = QuantInternRMSNorm(module.q_norm)
|
| 145 |
+
self.k_norm = QuantInternRMSNorm(module.k_norm)
|
| 146 |
+
|
| 147 |
+
if self.use_flash_attn:
|
| 148 |
+
from tinychat.models.internvl.internvit import FlashAttention
|
| 149 |
+
self.inner_attn = FlashAttention(attention_dropout=config.attention_dropout)
|
| 150 |
+
|
| 151 |
+
def forward(self, hidden_states: torch.Tensor, scale_in: torch.Tensor):
|
| 152 |
+
bsz, seqlen, hidden_size = hidden_states.shape
|
| 153 |
+
|
| 154 |
+
qkv_out = torch.empty(bsz * seqlen, 3 * hidden_size, dtype=torch.float16, device=hidden_states.device)
|
| 155 |
+
self.qkv(hidden_states.reshape(-1, hidden_size), scale_in, qkv_out)
|
| 156 |
+
|
| 157 |
+
qkv = rearrange(qkv_out.view(bsz, seqlen, -1), 'b s (three h d) -> b s three h d', three=3, h=self.num_heads)
|
| 158 |
+
|
| 159 |
+
if self.qk_normalization:
|
| 160 |
+
q, k, v = qkv.unbind(2)
|
| 161 |
+
q, _ = self.q_norm(q.flatten(-2, -1)); q = q.view_as(q)
|
| 162 |
+
k, _ = self.k_norm(k.flatten(-2, -1)); k = k.view_as(k)
|
| 163 |
+
qkv = torch.stack([q, k, v], dim=2)
|
| 164 |
+
|
| 165 |
+
attn_out, _ = self.inner_attn(qkv, need_weights=False, causal=False)
|
| 166 |
+
attn_out = rearrange(attn_out, 'b s h d -> (b s) (h d)')
|
| 167 |
+
|
| 168 |
+
quant_out = torch.empty_like(attn_out, dtype=torch.int8)
|
| 169 |
+
scale_proj_in = torch.empty(bsz * seqlen, device=hidden_states.device, dtype=torch.float16)
|
| 170 |
+
awq_inference_engine.invoke_quant(quant_out, attn_out, scale_proj_in)
|
| 171 |
+
|
| 172 |
+
proj_out = torch.empty_like(attn_out)
|
| 173 |
+
self.proj(quant_out, scale_proj_in, proj_out)
|
| 174 |
+
|
| 175 |
+
return proj_out
|
| 176 |
+
|
| 177 |
+
class QuantInternMLP(nn.Module):
|
| 178 |
+
def __init__(self, module: InternMLP, config: InternVisionConfig):
|
| 179 |
+
super().__init__()
|
| 180 |
+
self.config = config
|
| 181 |
+
self.act = module.act
|
| 182 |
+
self.fc1 = W8A8OF16LinearDynamicInputScale.from_linear(module.fc1)
|
| 183 |
+
self.fc2 = W8A8OF16LinearDynamicInputScale.from_linear(module.fc2)
|
| 184 |
+
|
| 185 |
+
def forward(self, hidden_states: torch.Tensor, scale_in: torch.Tensor):
|
| 186 |
+
bsz, seqlen, hidden_size = hidden_states.shape
|
| 187 |
+
device = hidden_states.device
|
| 188 |
+
|
| 189 |
+
fc1_out = torch.empty((bsz * seqlen), self.config.intermediate_size, dtype=torch.float16, device=device)
|
| 190 |
+
self.fc1(hidden_states.reshape(-1, hidden_size), scale_in, fc1_out)
|
| 191 |
+
|
| 192 |
+
tmp = torch.empty(
|
| 193 |
+
((bsz * seqlen) * self.config.intermediate_size),
|
| 194 |
+
device=device,
|
| 195 |
+
dtype=torch.float16,
|
| 196 |
+
)
|
| 197 |
+
act_out = torch.empty_like(fc1_out, dtype=torch.int8)
|
| 198 |
+
scale_act = torch.empty(bsz * seqlen, device=device, dtype=torch.float16)
|
| 199 |
+
awq_inference_engine.gelu_and_quant(act_out, fc1_out, scale_act, tmp)
|
| 200 |
+
|
| 201 |
+
fc2_out = torch.empty((bsz * seqlen), hidden_size, dtype=torch.float16, device=device)
|
| 202 |
+
self.fc2(act_out, scale_act, fc2_out)
|
| 203 |
+
|
| 204 |
+
return fc2_out
|
| 205 |
+
|
| 206 |
+
class QuantInternVisionEncoderLayer(nn.Module):
|
| 207 |
+
def __init__(self, module: InternVisionEncoderLayer, config: InternVisionConfig):
|
| 208 |
+
super().__init__()
|
| 209 |
+
self.config = config
|
| 210 |
+
self.embed_dim = config.hidden_size
|
| 211 |
+
self.intermediate_size = config.intermediate_size
|
| 212 |
+
|
| 213 |
+
self.attn = QuantInternAttention(module.attn, config)
|
| 214 |
+
self.mlp = QuantInternMLP(module.mlp, config)
|
| 215 |
+
|
| 216 |
+
self.norm1 = QuantInternRMSNorm(module.norm1)
|
| 217 |
+
self.norm2 = QuantInternRMSNorm(module.norm2)
|
| 218 |
+
|
| 219 |
+
self.ls1 = module.ls1
|
| 220 |
+
self.ls2 = module.ls2
|
| 221 |
+
|
| 222 |
+
def forward(self, hidden_states: torch.Tensor):
|
| 223 |
+
bsz, seqlen, hidden_size = hidden_states.shape
|
| 224 |
+
|
| 225 |
+
residual = hidden_states
|
| 226 |
+
norm1_out, scale1 = self.norm1(hidden_states)
|
| 227 |
+
attn_out = self.attn(norm1_out.view(bsz, seqlen, hidden_size), scale1)
|
| 228 |
+
hidden_states = residual + attn_out.view(bsz, seqlen, hidden_size) * self.ls1
|
| 229 |
+
|
| 230 |
+
residual = hidden_states
|
| 231 |
+
norm2_out, scale2 = self.norm2(hidden_states)
|
| 232 |
+
mlp_out = self.mlp(norm2_out.view(bsz, seqlen, hidden_size), scale2)
|
| 233 |
+
hidden_states = residual + mlp_out.view(bsz, seqlen, hidden_size) * self.ls2
|
| 234 |
+
|
| 235 |
+
return hidden_states
|
| 236 |
+
|
| 237 |
+
|
llm-awq/tinychat/modules/fused_mlp.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from torch.cuda.amp import custom_bwd, custom_fwd
|
| 6 |
+
from transformers.models.llama.modeling_llama import LlamaMLP
|
| 7 |
+
|
| 8 |
+
import awq_inference_engine
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
class QuantLlamaMLP(nn.Module):
|
| 12 |
+
def __init__(
|
| 13 |
+
self,
|
| 14 |
+
gate_proj,
|
| 15 |
+
down_proj,
|
| 16 |
+
up_proj,
|
| 17 |
+
):
|
| 18 |
+
super().__init__()
|
| 19 |
+
self.register_buffer("gate_proj_qweight", gate_proj.qweight)
|
| 20 |
+
self.register_buffer("gate_proj_scales", gate_proj.scales)
|
| 21 |
+
self.register_buffer("gate_proj_scaled_zeros", gate_proj.scaled_zeros)
|
| 22 |
+
self.register_buffer("up_proj_qweight", up_proj.qweight)
|
| 23 |
+
self.register_buffer("up_proj_scales", up_proj.scales)
|
| 24 |
+
self.register_buffer("up_proj_scaled_zeros", up_proj.scaled_zeros)
|
| 25 |
+
|
| 26 |
+
self.in_features = gate_proj.in_features
|
| 27 |
+
self.intermediate_size = gate_proj.out_features
|
| 28 |
+
self.out_features = down_proj.out_features
|
| 29 |
+
self.w_bit = gate_proj.w_bit
|
| 30 |
+
self.down_proj = down_proj
|
| 31 |
+
self.split_k_iters = down_proj.split_k_iters
|
| 32 |
+
|
| 33 |
+
def forward(self, x):
|
| 34 |
+
return self.down_proj(self.our_llama_mlp(x))
|
| 35 |
+
|
| 36 |
+
def our_llama_mlp(self, x):
|
| 37 |
+
# out_shape = x.shape[:-1] + (self.intermediate_size,)
|
| 38 |
+
# x = x.reshape(-1, x.shape[-1])
|
| 39 |
+
if x.numel() // x.shape[-1] < 8:
|
| 40 |
+
gate_output = awq_inference_engine.gemv_forward_cuda_new(
|
| 41 |
+
x,
|
| 42 |
+
self.gate_proj_qweight,
|
| 43 |
+
self.gate_proj_scales,
|
| 44 |
+
self.gate_proj_scaled_zeros,
|
| 45 |
+
x.numel() // x.shape[-1],
|
| 46 |
+
self.intermediate_size,
|
| 47 |
+
self.in_features,
|
| 48 |
+
self.down_proj.group_size,
|
| 49 |
+
)
|
| 50 |
+
gate_output = F.silu(gate_output)
|
| 51 |
+
up_output = awq_inference_engine.gemv_forward_cuda_new(
|
| 52 |
+
x,
|
| 53 |
+
self.up_proj_qweight,
|
| 54 |
+
self.up_proj_scales,
|
| 55 |
+
self.up_proj_scaled_zeros,
|
| 56 |
+
x.numel() // x.shape[-1],
|
| 57 |
+
self.intermediate_size,
|
| 58 |
+
self.in_features,
|
| 59 |
+
self.down_proj.group_size,
|
| 60 |
+
)
|
| 61 |
+
else:
|
| 62 |
+
# num_mn_tiles = (x.shape[0] // 32) * (self.intermediate_size // 128)
|
| 63 |
+
# cuda_Semaphores_gate = torch.empty(num_mn_tiles).int().to(x.device)
|
| 64 |
+
# cuda_Semaphores_up = torch.empty(num_mn_tiles).int().to(x.device)
|
| 65 |
+
gate_output = awq_inference_engine.gemm_forward_cuda_new(
|
| 66 |
+
x,
|
| 67 |
+
self.gate_proj_qweight,
|
| 68 |
+
self.gate_proj_scales,
|
| 69 |
+
self.gate_proj_scaled_zeros - 8 * self.gate_proj_scales,
|
| 70 |
+
# self.gate_cuda_semaphores
|
| 71 |
+
)
|
| 72 |
+
up_output = awq_inference_engine.gemm_forward_cuda_new(
|
| 73 |
+
x,
|
| 74 |
+
self.up_proj_qweight,
|
| 75 |
+
self.up_proj_scales,
|
| 76 |
+
self.up_proj_scaled_zeros - 8 * self.up_proj_scales,
|
| 77 |
+
# self.up_cuda_semaphores
|
| 78 |
+
)
|
| 79 |
+
gate_output = F.silu(gate_output)
|
| 80 |
+
|
| 81 |
+
c = gate_output * up_output
|
| 82 |
+
# c = c.reshape(out_shape)
|
| 83 |
+
return c
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def make_fused_mlp(m, parent_name=""):
|
| 87 |
+
if not hasattr(make_fused_mlp, "called"):
|
| 88 |
+
# print("[Warning] Calling a fake MLP fusion. But still faster than Huggingface Implimentation.")
|
| 89 |
+
make_fused_mlp.called = True
|
| 90 |
+
"""
|
| 91 |
+
Replace all LlamaMLP modules with QuantLlamaMLP modules, which fuses many of the operations.
|
| 92 |
+
"""
|
| 93 |
+
if m.__class__.__name__ in ["LlamaMLP"]:
|
| 94 |
+
return QuantLlamaMLP(m.gate_proj, m.down_proj, m.up_proj)
|
| 95 |
+
|
| 96 |
+
for name, child in m.named_children():
|
| 97 |
+
child = make_fused_mlp(child, parent_name=f"{parent_name}.{name}")
|
| 98 |
+
|
| 99 |
+
if isinstance(child, QuantLlamaMLP):
|
| 100 |
+
setattr(m, name, child)
|
| 101 |
+
return m
|
llm-awq/tinychat/modules/fused_norm.py
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import torch
|
| 2 |
+
from torch import nn
|
| 3 |
+
from transformers.models.llama.modeling_llama import LlamaRMSNorm
|
| 4 |
+
import awq_inference_engine
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class FTLlamaRMSNorm(nn.Module):
|
| 8 |
+
def __init__(self, weight, eps=1e-6):
|
| 9 |
+
"""
|
| 10 |
+
LlamaRMSNorm is equivalent to T5LayerNorm
|
| 11 |
+
"""
|
| 12 |
+
super().__init__()
|
| 13 |
+
self.weight = weight
|
| 14 |
+
self.variance_epsilon = eps
|
| 15 |
+
|
| 16 |
+
def forward(self, x):
|
| 17 |
+
output = torch.empty_like(x)
|
| 18 |
+
awq_inference_engine.layernorm_forward_cuda(
|
| 19 |
+
x, self.weight, output, self.variance_epsilon
|
| 20 |
+
)
|
| 21 |
+
return output
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def make_quant_norm(model):
|
| 25 |
+
"""
|
| 26 |
+
Replace all LlamaRMSNorm modules with FTLlamaRMSNorm modules
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
for name, m in model.named_modules():
|
| 30 |
+
if not isinstance(m, LlamaRMSNorm):
|
| 31 |
+
continue
|
| 32 |
+
|
| 33 |
+
norm = FTLlamaRMSNorm(m.weight, m.variance_epsilon)
|
| 34 |
+
|
| 35 |
+
if "." in name:
|
| 36 |
+
parent_name = name.rsplit(".", 1)[0]
|
| 37 |
+
child_name = name[len(parent_name) + 1 :]
|
| 38 |
+
parent = model.get_submodule(parent_name)
|
| 39 |
+
else:
|
| 40 |
+
parent_name = ""
|
| 41 |
+
parent = model
|
| 42 |
+
child_name = name
|
| 43 |
+
|
| 44 |
+
# print(f"Replacing {name} with quant_attn; parent: {parent_name}, child's name: {child_name}")
|
| 45 |
+
|
| 46 |
+
setattr(parent, child_name, norm)
|
llm-awq/tinychat/modules/fused_siglipdecoder.py
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import numpy as np
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
import torch.nn.functional as F
|
| 5 |
+
from awq.quantize import W8A8OF16LinearDynamicInputScale
|
| 6 |
+
from llava.model.multimodal_encoder.siglip.modeling_siglip import (
|
| 7 |
+
SiglipMLP,
|
| 8 |
+
SiglipEncoder,
|
| 9 |
+
SiglipAttention,
|
| 10 |
+
SiglipEncoderLayer,
|
| 11 |
+
)
|
| 12 |
+
from tinychat.utils.input_metadata import ActivationBuffer
|
| 13 |
+
from transformers.modeling_outputs import BaseModelOutput
|
| 14 |
+
from typing import Optional, Tuple, Union
|
| 15 |
+
from flash_attn import flash_attn_func
|
| 16 |
+
import time
|
| 17 |
+
|
| 18 |
+
CLIP_RANGE = 5
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
import awq_inference_engine
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class QuantSiglipEncoder(nn.Module):
|
| 25 |
+
def __init__(self, module: SiglipEncoder, bsz=64, seqlen=1024):
|
| 26 |
+
super().__init__()
|
| 27 |
+
self.config = module.config
|
| 28 |
+
self.layers = [QuantSiglipEncoderLayer(layer) for layer in module.layers]
|
| 29 |
+
self.buffer = ActivationBuffer(module)
|
| 30 |
+
self.bsz = bsz
|
| 31 |
+
self.seqlen = seqlen
|
| 32 |
+
self.buffer.allocate_activation_buffer(self.bsz * self.seqlen)
|
| 33 |
+
|
| 34 |
+
# Ignore copy
|
| 35 |
+
def forward(
|
| 36 |
+
self,
|
| 37 |
+
inputs_embeds,
|
| 38 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 39 |
+
output_attentions: Optional[bool] = None, # dummy
|
| 40 |
+
output_hidden_states: Optional[bool] = None,
|
| 41 |
+
return_dict: Optional[bool] = None,
|
| 42 |
+
) -> Union[Tuple, BaseModelOutput]:
|
| 43 |
+
# TODO Find why this code is necessary
|
| 44 |
+
# torch.sum(inputs_embeds!=inputs_embeds)
|
| 45 |
+
bsz, seqlen, _ = inputs_embeds.shape
|
| 46 |
+
if self.bsz != bsz or self.seqlen != seqlen:
|
| 47 |
+
self.buffer.allocate_activation_buffer(bsz * seqlen)
|
| 48 |
+
self.bsz = bsz
|
| 49 |
+
self.seqlen = seqlen
|
| 50 |
+
|
| 51 |
+
output_hidden_states = (
|
| 52 |
+
output_hidden_states
|
| 53 |
+
if output_hidden_states is not None
|
| 54 |
+
else self.config.output_hidden_states
|
| 55 |
+
)
|
| 56 |
+
return_dict = (
|
| 57 |
+
return_dict if return_dict is not None else self.config.use_return_dict
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
encoder_states = () if output_hidden_states else None
|
| 61 |
+
|
| 62 |
+
hidden_states = inputs_embeds
|
| 63 |
+
for i, encoder_layer in enumerate(self.layers):
|
| 64 |
+
if output_hidden_states:
|
| 65 |
+
encoder_states = encoder_states + (
|
| 66 |
+
hidden_states.reshape(bsz, seqlen, -1),
|
| 67 |
+
)
|
| 68 |
+
hidden_states = encoder_layer(
|
| 69 |
+
hidden_states, self.buffer, attention_mask, bsz, seqlen
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
if output_hidden_states:
|
| 73 |
+
encoder_states = encoder_states + (hidden_states.reshape(bsz, seqlen, -1),)
|
| 74 |
+
if not return_dict:
|
| 75 |
+
return tuple(v for v in [hidden_states, encoder_states] if v is not None)
|
| 76 |
+
return BaseModelOutput(
|
| 77 |
+
last_hidden_state=hidden_states.reshape(bsz, seqlen, -1),
|
| 78 |
+
hidden_states=encoder_states,
|
| 79 |
+
attentions=None,
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class QuantSiglipMLP(nn.Module):
|
| 84 |
+
def __init__(self, siglipmlp, init_only=False):
|
| 85 |
+
super().__init__()
|
| 86 |
+
self.config = siglipmlp.config
|
| 87 |
+
self.activation_fn = siglipmlp.activation_fn
|
| 88 |
+
self.fc1 = W8A8OF16LinearDynamicInputScale.from_linear(
|
| 89 |
+
siglipmlp.fc1, init_only=init_only, fc1=False
|
| 90 |
+
)
|
| 91 |
+
self.fc2 = W8A8OF16LinearDynamicInputScale.from_linear(
|
| 92 |
+
siglipmlp.fc2, init_only=init_only
|
| 93 |
+
)
|
| 94 |
+
self.invoke_quant = self.invoke_quant_mlp
|
| 95 |
+
|
| 96 |
+
def invoke_quant_mlp(self, buffer, actfn_output):
|
| 97 |
+
awq_inference_engine.invoke_quant(
|
| 98 |
+
buffer.quantized_mlp_act_buffer,
|
| 99 |
+
actfn_output,
|
| 100 |
+
buffer.quantized_scale_buffer,
|
| 101 |
+
)
|
| 102 |
+
|
| 103 |
+
def forward(self, buffer: ActivationBuffer) -> torch.Tensor:
|
| 104 |
+
# INT8 in, FP16 out
|
| 105 |
+
self.fc1(
|
| 106 |
+
buffer.quantized_hidden_states_buffer,
|
| 107 |
+
buffer.quantized_scale_buffer,
|
| 108 |
+
buffer.fc1_buffer,
|
| 109 |
+
)
|
| 110 |
+
# Act & quantization
|
| 111 |
+
awq_inference_engine.gelu_and_quant(
|
| 112 |
+
buffer.quantized_mlp_act_buffer,
|
| 113 |
+
buffer.fc1_buffer,
|
| 114 |
+
buffer.quantized_scale_buffer,
|
| 115 |
+
buffer.tmp,
|
| 116 |
+
)
|
| 117 |
+
# INT8 in, FP16 out
|
| 118 |
+
self.fc2(
|
| 119 |
+
buffer.quantized_mlp_act_buffer,
|
| 120 |
+
buffer.quantized_scale_buffer,
|
| 121 |
+
buffer.in_out_fc2_act_buffer,
|
| 122 |
+
)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class QuantSiglipFlashAttention2(nn.Module):
|
| 126 |
+
def __init__(
|
| 127 |
+
self,
|
| 128 |
+
module: SiglipAttention,
|
| 129 |
+
init_only=False,
|
| 130 |
+
):
|
| 131 |
+
super().__init__()
|
| 132 |
+
self.config = module.config
|
| 133 |
+
self.embed_dim = module.embed_dim
|
| 134 |
+
self.num_heads = module.num_heads
|
| 135 |
+
self.head_dim = self.embed_dim // self.num_heads
|
| 136 |
+
|
| 137 |
+
self.qkv_proj = W8A8OF16LinearDynamicInputScale.from_qkv(
|
| 138 |
+
module.q_proj, module.k_proj, module.v_proj, init_only=init_only
|
| 139 |
+
)
|
| 140 |
+
self.out_proj = W8A8OF16LinearDynamicInputScale.from_linear(
|
| 141 |
+
module.out_proj, init_only=init_only
|
| 142 |
+
)
|
| 143 |
+
self.invoke_quant = self.invoke_quant_wo
|
| 144 |
+
|
| 145 |
+
def invoke_quant_wo(self, buffer, attn_output):
|
| 146 |
+
awq_inference_engine.invoke_quant(
|
| 147 |
+
buffer.quantized_hidden_states_buffer,
|
| 148 |
+
attn_output,
|
| 149 |
+
buffer.quantized_scale_buffer,
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
# Adapted from transformers.models.llama.modeling_llama.LlamaFlashAttention2.forward
|
| 153 |
+
def forward(
|
| 154 |
+
self, buffer: ActivationBuffer, bsz=64, seqlen=1024
|
| 155 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
|
| 156 |
+
# qkv
|
| 157 |
+
self.qkv_proj(
|
| 158 |
+
buffer.quantized_hidden_states_buffer,
|
| 159 |
+
buffer.quantized_scale_buffer,
|
| 160 |
+
buffer.qkv_proj_act_buffer,
|
| 161 |
+
)
|
| 162 |
+
q, k, v = buffer.qkv_proj_act_buffer.split(
|
| 163 |
+
[self.embed_dim, self.embed_dim, self.embed_dim], dim=-1
|
| 164 |
+
)
|
| 165 |
+
q = q.reshape(bsz, seqlen, self.num_heads, self.head_dim)
|
| 166 |
+
k = k.reshape(bsz, seqlen, self.num_heads, self.head_dim)
|
| 167 |
+
v = v.reshape(bsz, seqlen, self.num_heads, self.head_dim)
|
| 168 |
+
attn_output = flash_attn_func(q, k, v, softmax_scale=None, causal=False)
|
| 169 |
+
attn_output = attn_output.reshape(bsz * seqlen, -1)
|
| 170 |
+
# FP16 -> int8
|
| 171 |
+
self.invoke_quant(buffer, attn_output)
|
| 172 |
+
# INT8 in, FP16 out
|
| 173 |
+
self.out_proj(
|
| 174 |
+
buffer.quantized_hidden_states_buffer,
|
| 175 |
+
buffer.quantized_scale_buffer,
|
| 176 |
+
buffer.in_out_fc2_act_buffer,
|
| 177 |
+
)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
class QuantSiglipEncoderLayer(nn.Module):
|
| 181 |
+
def __init__(self, module: SiglipEncoderLayer):
|
| 182 |
+
super().__init__()
|
| 183 |
+
self.embed_dim = module.embed_dim
|
| 184 |
+
self.self_attn = QuantSiglipFlashAttention2(module.self_attn)
|
| 185 |
+
self.layer_norm1 = RMSNormGeneral(
|
| 186 |
+
module.layer_norm1.weight.data,
|
| 187 |
+
module.layer_norm1.bias.data,
|
| 188 |
+
module.layer_norm1.eps,
|
| 189 |
+
True,
|
| 190 |
+
).cuda()
|
| 191 |
+
self.mlp = QuantSiglipMLP(module.mlp)
|
| 192 |
+
self.layer_norm2 = RMSNormGeneral(
|
| 193 |
+
module.layer_norm2.weight.data,
|
| 194 |
+
module.layer_norm2.bias.data,
|
| 195 |
+
module.layer_norm2.eps,
|
| 196 |
+
True,
|
| 197 |
+
).cuda()
|
| 198 |
+
self.quant = self.invoke_quant_norm
|
| 199 |
+
|
| 200 |
+
def invoke_quant_norm(self, buffer, normfn_output):
|
| 201 |
+
awq_inference_engine.invoke_quant(
|
| 202 |
+
buffer.quantized_hidden_states_buffer,
|
| 203 |
+
normfn_output,
|
| 204 |
+
buffer.quantized_scale_buffer,
|
| 205 |
+
)
|
| 206 |
+
|
| 207 |
+
def forward(
|
| 208 |
+
self,
|
| 209 |
+
hidden_states: torch.Tensor,
|
| 210 |
+
buffer: ActivationBuffer,
|
| 211 |
+
attention_mask,
|
| 212 |
+
bsz,
|
| 213 |
+
seqlen,
|
| 214 |
+
) -> Tuple[torch.FloatTensor]:
|
| 215 |
+
# Attention block
|
| 216 |
+
# FP16 in int8 out, layernorm & quantization
|
| 217 |
+
residual = hidden_states
|
| 218 |
+
self.layer_norm1(
|
| 219 |
+
hidden_states.reshape(-1, self.embed_dim),
|
| 220 |
+
buffer.quantized_hidden_states_buffer,
|
| 221 |
+
buffer.quantized_scale_buffer,
|
| 222 |
+
)
|
| 223 |
+
|
| 224 |
+
# INT8 -> FP16
|
| 225 |
+
self.self_attn(buffer, bsz, seqlen)
|
| 226 |
+
hidden_states = (
|
| 227 |
+
residual.reshape(-1, self.embed_dim) + buffer.in_out_fc2_act_buffer
|
| 228 |
+
)
|
| 229 |
+
# Fully Connected
|
| 230 |
+
residual = hidden_states
|
| 231 |
+
# FP16 in int8 out, layernorm & quantization
|
| 232 |
+
self.layer_norm2(
|
| 233 |
+
hidden_states.reshape(-1, self.embed_dim),
|
| 234 |
+
buffer.quantized_hidden_states_buffer,
|
| 235 |
+
buffer.quantized_scale_buffer,
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
# INT8 -> FP16
|
| 239 |
+
self.mlp(buffer)
|
| 240 |
+
hidden_states = (
|
| 241 |
+
residual.reshape(-1, self.embed_dim) + buffer.in_out_fc2_act_buffer
|
| 242 |
+
)
|
| 243 |
+
return hidden_states
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class RMSNormGeneral(nn.Module):
|
| 247 |
+
"""Root mean square normalization (w/ per-token or per-tensor quant).
|
| 248 |
+
|
| 249 |
+
Computes x -> w * x / sqrt(E[x^2] + eps) where w is the learned weight.
|
| 250 |
+
Refer to https://arxiv.org/abs/1910.07467
|
| 251 |
+
"""
|
| 252 |
+
|
| 253 |
+
def __init__(
|
| 254 |
+
self,
|
| 255 |
+
weight: torch.tensor,
|
| 256 |
+
bias: torch.tensor,
|
| 257 |
+
eps: float = 1e-6,
|
| 258 |
+
use_per_token_quant: bool = True,
|
| 259 |
+
) -> None:
|
| 260 |
+
super().__init__()
|
| 261 |
+
self.weight = nn.Parameter(weight, requires_grad=False)
|
| 262 |
+
self.bias = nn.Parameter(bias, requires_grad=False)
|
| 263 |
+
self.variance_epsilon = eps
|
| 264 |
+
self.use_per_token_quant = use_per_token_quant
|
| 265 |
+
|
| 266 |
+
def forward(
|
| 267 |
+
self,
|
| 268 |
+
x: torch.Tensor,
|
| 269 |
+
quantized_hidden_states_buffer: torch.Tensor,
|
| 270 |
+
quantized_scale_buffer: torch.Tensor,
|
| 271 |
+
quantized_sum_buffer: torch.Tensor = None,
|
| 272 |
+
) -> torch.Tensor:
|
| 273 |
+
# quantized_sum_buffer is not used, only to keep the consistency of the interface
|
| 274 |
+
awq_inference_engine.rms_norm_general(
|
| 275 |
+
quantized_hidden_states_buffer,
|
| 276 |
+
x,
|
| 277 |
+
self.weight.data,
|
| 278 |
+
self.bias.data,
|
| 279 |
+
quantized_scale_buffer,
|
| 280 |
+
self.variance_epsilon,
|
| 281 |
+
self.use_per_token_quant,
|
| 282 |
+
)
|
llm-awq/tinychat/modules/fused_vision_attn.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import torch
|
| 3 |
+
import torch.nn as nn
|
| 4 |
+
from torch.nn import functional as F
|
| 5 |
+
from typing import Optional, Tuple
|
| 6 |
+
|
| 7 |
+
# from awq.quantize.qmodule import WQLinear
|
| 8 |
+
# import awq_inference_engine
|
| 9 |
+
# from tinychat.models.llama import apply_rotary_emb
|
| 10 |
+
import gc
|
| 11 |
+
|
| 12 |
+
import tinychat.utils.constants
|
| 13 |
+
|
| 14 |
+
max_batch_size = tinychat.utils.constants.max_batch_size
|
| 15 |
+
max_seq_len = tinychat.utils.constants.max_seq_len
|
| 16 |
+
|
| 17 |
+
from transformers.activations import ACT2FN
|
| 18 |
+
from transformers.models.clip.configuration_clip import (
|
| 19 |
+
CLIPConfig,
|
| 20 |
+
CLIPTextConfig,
|
| 21 |
+
CLIPVisionConfig,
|
| 22 |
+
)
|
| 23 |
+
from transformers.models.clip.modeling_clip import CLIPAttention
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class CLIPAttentionFused(nn.Module):
|
| 27 |
+
"""Multi-headed attention from 'Attention Is All You Need' paper"""
|
| 28 |
+
|
| 29 |
+
def __init__(
|
| 30 |
+
self, hidden_size, num_heads, qkv_proj, out_proj, dev, attention_dropout=0.0
|
| 31 |
+
):
|
| 32 |
+
super().__init__()
|
| 33 |
+
self.embed_dim = hidden_size
|
| 34 |
+
self.num_heads = num_heads
|
| 35 |
+
self.head_dim = hidden_size // num_heads
|
| 36 |
+
self.scale = self.head_dim**-0.5
|
| 37 |
+
self.dropout = attention_dropout
|
| 38 |
+
|
| 39 |
+
if (self.head_dim * num_heads) != self.embed_dim:
|
| 40 |
+
raise ValueError(
|
| 41 |
+
f"hidden_size must be divisible by num_heads (got `hidden_size`: {self.hidden_size}"
|
| 42 |
+
f" and `num_heads`: {num_heads})."
|
| 43 |
+
)
|
| 44 |
+
self.qkv_proj = qkv_proj
|
| 45 |
+
self.out_proj = out_proj
|
| 46 |
+
|
| 47 |
+
def _shape(self, tensor: torch.Tensor, seq_len: int, bsz: int):
|
| 48 |
+
return (
|
| 49 |
+
tensor.view(bsz, seq_len, self.num_heads, self.head_dim)
|
| 50 |
+
.transpose(1, 2)
|
| 51 |
+
.contiguous()
|
| 52 |
+
)
|
| 53 |
+
|
| 54 |
+
def forward(
|
| 55 |
+
self,
|
| 56 |
+
hidden_states: torch.Tensor,
|
| 57 |
+
attention_mask: Optional[torch.Tensor] = None,
|
| 58 |
+
causal_attention_mask: Optional[torch.Tensor] = None,
|
| 59 |
+
output_attentions: Optional[bool] = False,
|
| 60 |
+
) -> Tuple[torch.Tensor, Optional[torch.Tensor], Optional[Tuple[torch.Tensor]]]:
|
| 61 |
+
"""Input shape: Batch x Time x Channel"""
|
| 62 |
+
|
| 63 |
+
bsz, tgt_len, embed_dim = hidden_states.size()
|
| 64 |
+
|
| 65 |
+
qkv_states = self.qkv_proj(hidden_states)
|
| 66 |
+
qkv_states = qkv_states.view(bsz, tgt_len, 3, self.num_heads, self.head_dim)
|
| 67 |
+
|
| 68 |
+
# This updates the query and key states in-place, saving VRAM.
|
| 69 |
+
query_states, key_states, value_states = torch.split(qkv_states, 1, dim=2)
|
| 70 |
+
del qkv_states
|
| 71 |
+
|
| 72 |
+
proj_shape = (bsz * self.num_heads, -1, self.head_dim)
|
| 73 |
+
|
| 74 |
+
query_states = (
|
| 75 |
+
query_states.view(bsz, tgt_len, self.num_heads, self.head_dim)
|
| 76 |
+
.transpose(1, 2)
|
| 77 |
+
.view(*proj_shape)
|
| 78 |
+
* self.scale
|
| 79 |
+
)
|
| 80 |
+
key_states = (
|
| 81 |
+
key_states.view(bsz, tgt_len, self.num_heads, self.head_dim)
|
| 82 |
+
.transpose(1, 2)
|
| 83 |
+
.view(*proj_shape)
|
| 84 |
+
)
|
| 85 |
+
value_states = (
|
| 86 |
+
value_states.view(bsz, tgt_len, self.num_heads, self.head_dim)
|
| 87 |
+
.transpose(1, 2)
|
| 88 |
+
.view(*proj_shape)
|
| 89 |
+
)
|
| 90 |
+
|
| 91 |
+
src_len = key_states.size(1)
|
| 92 |
+
attn_weights = torch.bmm(query_states, key_states.transpose(1, 2))
|
| 93 |
+
|
| 94 |
+
if attn_weights.size() != (bsz * self.num_heads, tgt_len, src_len):
|
| 95 |
+
raise ValueError(
|
| 96 |
+
f"Attention weights should be of size {(bsz * self.num_heads, tgt_len, src_len)}, but is"
|
| 97 |
+
f" {attn_weights.size()}"
|
| 98 |
+
)
|
| 99 |
+
|
| 100 |
+
# apply the causal_attention_mask first
|
| 101 |
+
if causal_attention_mask is not None:
|
| 102 |
+
if causal_attention_mask.size() != (bsz, 1, tgt_len, src_len):
|
| 103 |
+
raise ValueError(
|
| 104 |
+
f"Attention mask should be of size {(bsz, 1, tgt_len, src_len)}, but is"
|
| 105 |
+
f" {causal_attention_mask.size()}"
|
| 106 |
+
)
|
| 107 |
+
attn_weights = (
|
| 108 |
+
attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
|
| 109 |
+
+ causal_attention_mask
|
| 110 |
+
)
|
| 111 |
+
attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len, src_len)
|
| 112 |
+
|
| 113 |
+
if attention_mask is not None:
|
| 114 |
+
if attention_mask.size() != (bsz, 1, tgt_len, src_len):
|
| 115 |
+
raise ValueError(
|
| 116 |
+
f"Attention mask should be of size {(bsz, 1, tgt_len, src_len)}, but is {attention_mask.size()}"
|
| 117 |
+
)
|
| 118 |
+
attn_weights = (
|
| 119 |
+
attn_weights.view(bsz, self.num_heads, tgt_len, src_len)
|
| 120 |
+
+ attention_mask
|
| 121 |
+
)
|
| 122 |
+
attn_weights = attn_weights.view(bsz * self.num_heads, tgt_len, src_len)
|
| 123 |
+
|
| 124 |
+
attn_weights = nn.functional.softmax(attn_weights, dim=-1)
|
| 125 |
+
|
| 126 |
+
if output_attentions:
|
| 127 |
+
# this operation is a bit akward, but it's required to
|
| 128 |
+
# make sure that attn_weights keeps its gradient.
|
| 129 |
+
# In order to do so, attn_weights have to reshaped
|
| 130 |
+
# twice and have to be reused in the following
|
| 131 |
+
attn_weights_reshaped = attn_weights.view(
|
| 132 |
+
bsz, self.num_heads, tgt_len, src_len
|
| 133 |
+
)
|
| 134 |
+
attn_weights = attn_weights_reshaped.view(
|
| 135 |
+
bsz * self.num_heads, tgt_len, src_len
|
| 136 |
+
)
|
| 137 |
+
else:
|
| 138 |
+
attn_weights_reshaped = None
|
| 139 |
+
|
| 140 |
+
attn_probs = nn.functional.dropout(
|
| 141 |
+
attn_weights, p=self.dropout, training=self.training
|
| 142 |
+
)
|
| 143 |
+
|
| 144 |
+
attn_output = torch.bmm(attn_probs, value_states)
|
| 145 |
+
|
| 146 |
+
if attn_output.size() != (bsz * self.num_heads, tgt_len, self.head_dim):
|
| 147 |
+
raise ValueError(
|
| 148 |
+
f"`attn_output` should be of size {(bsz, self.num_heads, tgt_len, self.head_dim)}, but is"
|
| 149 |
+
f" {attn_output.size()}"
|
| 150 |
+
)
|
| 151 |
+
|
| 152 |
+
attn_output = attn_output.view(bsz, self.num_heads, tgt_len, self.head_dim)
|
| 153 |
+
attn_output = attn_output.transpose(1, 2)
|
| 154 |
+
attn_output = attn_output.reshape(bsz, tgt_len, embed_dim)
|
| 155 |
+
|
| 156 |
+
attn_output = self.out_proj(attn_output)
|
| 157 |
+
|
| 158 |
+
return attn_output, attn_weights_reshaped
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
class CLIPMLP(nn.Module):
|
| 162 |
+
def __init__(self, config):
|
| 163 |
+
super().__init__()
|
| 164 |
+
self.config = config
|
| 165 |
+
self.activation_fn = ACT2FN[config.hidden_act]
|
| 166 |
+
self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
|
| 167 |
+
self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)
|
| 168 |
+
|
| 169 |
+
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
|
| 170 |
+
hidden_states = self.fc1(hidden_states)
|
| 171 |
+
hidden_states = self.activation_fn(hidden_states)
|
| 172 |
+
hidden_states = self.fc2(hidden_states)
|
| 173 |
+
return hidden_states
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
class CLIPEncoderLayer(nn.Module):
|
| 177 |
+
def __init__(self, config: CLIPConfig):
|
| 178 |
+
super().__init__()
|
| 179 |
+
self.embed_dim = config.hidden_size
|
| 180 |
+
self.self_attn = CLIPAttention(config)
|
| 181 |
+
self.layer_norm1 = nn.LayerNorm(self.embed_dim)
|
| 182 |
+
self.mlp = CLIPMLP(config)
|
| 183 |
+
self.layer_norm2 = nn.LayerNorm(self.embed_dim)
|
| 184 |
+
|
| 185 |
+
def forward(
|
| 186 |
+
self,
|
| 187 |
+
hidden_states: torch.Tensor,
|
| 188 |
+
attention_mask: torch.Tensor,
|
| 189 |
+
causal_attention_mask: torch.Tensor,
|
| 190 |
+
output_attentions: Optional[bool] = False,
|
| 191 |
+
) -> Tuple[torch.FloatTensor]:
|
| 192 |
+
"""
|
| 193 |
+
Args:
|
| 194 |
+
hidden_states (`torch.FloatTensor`): input to the layer of shape `(batch, seq_len, embed_dim)`
|
| 195 |
+
attention_mask (`torch.FloatTensor`): attention mask of size
|
| 196 |
+
`(batch, 1, tgt_len, src_len)` where padding elements are indicated by very large negative values.
|
| 197 |
+
`(config.encoder_attention_heads,)`.
|
| 198 |
+
output_attentions (`bool`, *optional*):
|
| 199 |
+
Whether or not to return the attentions tensors of all attention layers. See `attentions` under
|
| 200 |
+
returned tensors for more detail.
|
| 201 |
+
"""
|
| 202 |
+
residual = hidden_states
|
| 203 |
+
|
| 204 |
+
hidden_states = self.layer_norm1(hidden_states)
|
| 205 |
+
hidden_states, attn_weights = self.self_attn(
|
| 206 |
+
hidden_states=hidden_states,
|
| 207 |
+
attention_mask=attention_mask,
|
| 208 |
+
causal_attention_mask=causal_attention_mask,
|
| 209 |
+
output_attentions=output_attentions,
|
| 210 |
+
)
|
| 211 |
+
hidden_states = residual + hidden_states
|
| 212 |
+
|
| 213 |
+
residual = hidden_states
|
| 214 |
+
hidden_states = self.layer_norm2(hidden_states)
|
| 215 |
+
hidden_states = self.mlp(hidden_states)
|
| 216 |
+
hidden_states = residual + hidden_states
|
| 217 |
+
|
| 218 |
+
outputs = (hidden_states,)
|
| 219 |
+
|
| 220 |
+
if output_attentions:
|
| 221 |
+
outputs += (attn_weights,)
|
| 222 |
+
|
| 223 |
+
return outputs
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def make_fused_vision_attn(model, dev):
|
| 227 |
+
"""
|
| 228 |
+
Replace all LlamaAttention modules with QuantLlamaAttention modules, fusing the q, k, v projections.
|
| 229 |
+
"""
|
| 230 |
+
model = model.cpu()
|
| 231 |
+
for name, m in model.named_modules():
|
| 232 |
+
if not m.__class__.__name__ in ["CLIPAttention", "CLIPAttentionFused"]:
|
| 233 |
+
continue
|
| 234 |
+
|
| 235 |
+
q_proj = m.q_proj
|
| 236 |
+
k_proj = m.k_proj
|
| 237 |
+
v_proj = m.v_proj
|
| 238 |
+
|
| 239 |
+
weights = torch.cat([q_proj.weight, k_proj.weight, v_proj.weight], dim=0)
|
| 240 |
+
bias = (
|
| 241 |
+
torch.cat([q_proj.bias, k_proj.bias, v_proj.bias], dim=0)
|
| 242 |
+
if q_proj.bias is not None
|
| 243 |
+
else None
|
| 244 |
+
)
|
| 245 |
+
|
| 246 |
+
qkv_layer = nn.Linear(
|
| 247 |
+
q_proj.in_features,
|
| 248 |
+
q_proj.out_features + k_proj.out_features + v_proj.out_features,
|
| 249 |
+
q_proj.bias is not None,
|
| 250 |
+
q_proj.weight.device,
|
| 251 |
+
)
|
| 252 |
+
qkv_layer.weight.data = weights
|
| 253 |
+
|
| 254 |
+
qkv_layer.bias.data = bias
|
| 255 |
+
if isinstance(m, CLIPAttention):
|
| 256 |
+
attn = CLIPAttentionFused(
|
| 257 |
+
m.embed_dim, m.num_heads, qkv_layer, m.out_proj, dev
|
| 258 |
+
)
|
| 259 |
+
if "." in name:
|
| 260 |
+
parent_name = name.rsplit(".", 1)[0]
|
| 261 |
+
child_name = name[len(parent_name) + 1 :]
|
| 262 |
+
parent = model.get_submodule(parent_name)
|
| 263 |
+
else:
|
| 264 |
+
parent_name = ""
|
| 265 |
+
parent = model
|
| 266 |
+
child_name = name
|
| 267 |
+
|
| 268 |
+
# print(f"Replacing {name} with quant_attn; parent: {parent_name}, child's name: {child_name}")
|
| 269 |
+
setattr(parent, child_name, attn)
|
| 270 |
+
gc.collect()
|
| 271 |
+
torch.cuda.empty_cache()
|
| 272 |
+
model = model.to(dev)
|
llm-awq/tinychat/nvila_benchmark.py
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
|
| 3 |
+
from termcolor import colored
|
| 4 |
+
|
| 5 |
+
import llava
|
| 6 |
+
from llava import conversation as clib
|
| 7 |
+
from llava.media import Image, Video
|
| 8 |
+
import torch
|
| 9 |
+
from awq.quantize import fake_quant
|
| 10 |
+
from awq.quantize.quantizer import real_quantize_model_weight
|
| 11 |
+
from transformers import AutoConfig
|
| 12 |
+
import tinychat
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def skip(*args, **kwargs):
|
| 16 |
+
pass
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def main() -> None:
|
| 20 |
+
parser = argparse.ArgumentParser()
|
| 21 |
+
parser.add_argument(
|
| 22 |
+
"--model-path",
|
| 23 |
+
"-m",
|
| 24 |
+
type=str,
|
| 25 |
+
default="/home/yuming/workspace/qwen/models/nvila-internal-8b-v1",
|
| 26 |
+
)
|
| 27 |
+
parser.add_argument(
|
| 28 |
+
"--quant_path",
|
| 29 |
+
type=str,
|
| 30 |
+
default="/PATH/TO/QUANT",
|
| 31 |
+
)
|
| 32 |
+
# parser.add_argument("--model-path", "-m", type=str, default="Efficient-Large-Model/J65")
|
| 33 |
+
# parser.add_argument("--quant_path", type=str, default="/home/yuming/workspace/qwen/models/J65/llm/vila2-J65-w4-g128-awq-v2.pt")
|
| 34 |
+
parser.add_argument("--conv-mode", "-c", type=str, default="auto")
|
| 35 |
+
# parser.add_argument("--media", type=str, default="/home/yuming/workspace/space_woaudio.mp4")
|
| 36 |
+
parser.add_argument("--device", type=str, default="cuda:0")
|
| 37 |
+
parser.add_argument(
|
| 38 |
+
"--act_scale_path",
|
| 39 |
+
type=str,
|
| 40 |
+
default="/PATH/TO/SCALE",
|
| 41 |
+
)
|
| 42 |
+
# quantization options
|
| 43 |
+
parser.add_argument("--quant_llm", action="store_true")
|
| 44 |
+
parser.add_argument("--quant_VT", action="store_true")
|
| 45 |
+
# Four basic tasks
|
| 46 |
+
parser.add_argument("--video_caption", action="store_true")
|
| 47 |
+
parser.add_argument("--video_QA", action="store_true")
|
| 48 |
+
parser.add_argument("--image_caption", action="store_true")
|
| 49 |
+
parser.add_argument("--image_QA", action="store_true")
|
| 50 |
+
|
| 51 |
+
parser.add_argument(
|
| 52 |
+
"--all",
|
| 53 |
+
action="store_true",
|
| 54 |
+
help="Whether to quantize visiontower and llm, and test all 4 tasks",
|
| 55 |
+
)
|
| 56 |
+
parser.add_argument(
|
| 57 |
+
"--fakequant_VT",
|
| 58 |
+
action="store_true",
|
| 59 |
+
help="Use fake quant or real quant for VisionTower",
|
| 60 |
+
)
|
| 61 |
+
parser.add_argument(
|
| 62 |
+
"--all_task", action="store_true", help="Whether to test all 4 tasks"
|
| 63 |
+
)
|
| 64 |
+
parser.add_argument(
|
| 65 |
+
"--video_path", type=str, default="../figures/nvila_demo_video.mp4"
|
| 66 |
+
)
|
| 67 |
+
parser.add_argument("--image_path", type=str, default="../figures/vila-logo.jpg")
|
| 68 |
+
parser.add_argument("--max_seq_len", type=int, default=8192)
|
| 69 |
+
args = parser.parse_args()
|
| 70 |
+
|
| 71 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 72 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 73 |
+
torch.nn.init.uniform_ = skip
|
| 74 |
+
torch.nn.init.normal_ = skip
|
| 75 |
+
import tinychat.utils.constants
|
| 76 |
+
|
| 77 |
+
tinychat.utils.constants.max_seq_len = args.max_seq_len
|
| 78 |
+
from transformers import modeling_utils
|
| 79 |
+
|
| 80 |
+
modeling_utils._init_weights = False
|
| 81 |
+
|
| 82 |
+
# Load model
|
| 83 |
+
from tinychat.models.nvila_qwen2 import NVILAQwen2
|
| 84 |
+
|
| 85 |
+
config = AutoConfig.from_pretrained(args.model_path)
|
| 86 |
+
config.resume_path = args.model_path
|
| 87 |
+
model = NVILAQwen2(config).half()
|
| 88 |
+
model.llm = model.llm.eval()
|
| 89 |
+
if args.quant_llm or args.all:
|
| 90 |
+
from tinychat.modules import (
|
| 91 |
+
make_quant_norm,
|
| 92 |
+
make_quant_attn,
|
| 93 |
+
make_fused_mlp,
|
| 94 |
+
make_fused_vision_attn,
|
| 95 |
+
)
|
| 96 |
+
|
| 97 |
+
real_quantize_model_weight(
|
| 98 |
+
model.llm,
|
| 99 |
+
w_bit=4,
|
| 100 |
+
q_config=dict(q_group_size=128, zero_point=True),
|
| 101 |
+
init_only=True,
|
| 102 |
+
)
|
| 103 |
+
make_quant_attn(model.llm, "cuda", True)
|
| 104 |
+
make_quant_norm(model.llm)
|
| 105 |
+
make_fused_mlp(model.llm)
|
| 106 |
+
model = model.to("cuda")
|
| 107 |
+
model = model.to(args.device)
|
| 108 |
+
if args.quant_VT or args.all:
|
| 109 |
+
from tinychat.modules import QuantSiglipEncoder
|
| 110 |
+
|
| 111 |
+
model.vision_tower.vision_tower.vision_model.encoder = QuantSiglipEncoder(
|
| 112 |
+
model.vision_tower.vision_tower.vision_model.encoder
|
| 113 |
+
)
|
| 114 |
+
model = model.cuda().eval()
|
| 115 |
+
|
| 116 |
+
if args.video_caption or args.all or args.all_task:
|
| 117 |
+
print("-" * 80)
|
| 118 |
+
print("Video_Caption")
|
| 119 |
+
# Set conversation mode
|
| 120 |
+
clib.default_conversation = clib.conv_templates[args.conv_mode].copy()
|
| 121 |
+
media = Video(args.video_path)
|
| 122 |
+
text = "Elaborate on the visual and narrative elements of the video in detail." # + "1"+" 1"*3069
|
| 123 |
+
prompt = [media, text]
|
| 124 |
+
# Generate response
|
| 125 |
+
with torch.no_grad():
|
| 126 |
+
response = model.benchmark(prompt, args.quant_llm)
|
| 127 |
+
if args.video_QA or args.all or args.all_task:
|
| 128 |
+
print("-" * 80)
|
| 129 |
+
print("Video_QA")
|
| 130 |
+
# Set conversation mode
|
| 131 |
+
clib.default_conversation = clib.conv_templates[args.conv_mode].copy()
|
| 132 |
+
media = Video(args.video_path)
|
| 133 |
+
text = "What is the person in the video doing? Select the option that best describes their action: A. Folding paper B. Playing computer games C. Sleeping." # + "1"+" 1"*3069
|
| 134 |
+
prompt = [media, text]
|
| 135 |
+
# Generate response
|
| 136 |
+
with torch.no_grad():
|
| 137 |
+
response = model.benchmark(prompt, args.quant_llm)
|
| 138 |
+
if args.image_caption or args.all or args.all_task:
|
| 139 |
+
print("-" * 80)
|
| 140 |
+
print("Image_Caption")
|
| 141 |
+
# Set conversation mode
|
| 142 |
+
clib.default_conversation = clib.conv_templates[args.conv_mode].copy()
|
| 143 |
+
media = Image(args.image_path)
|
| 144 |
+
text = "Describe the image in detail."
|
| 145 |
+
prompt = [media, text]
|
| 146 |
+
# Generate response
|
| 147 |
+
with torch.no_grad():
|
| 148 |
+
response = model.benchmark(prompt, args.quant_llm)
|
| 149 |
+
if args.image_QA or args.all or args.all_task:
|
| 150 |
+
print("-" * 80)
|
| 151 |
+
print("Image_QA")
|
| 152 |
+
# Set conversation mode
|
| 153 |
+
clib.default_conversation = clib.conv_templates[args.conv_mode].copy()
|
| 154 |
+
media = Image(args.image_path)
|
| 155 |
+
text = "What does the text in the image say? Choose the option that best matches: A. VILA B. AIIV C. ALIV."
|
| 156 |
+
prompt = [media, text]
|
| 157 |
+
# Generate response
|
| 158 |
+
with torch.no_grad():
|
| 159 |
+
response = model.benchmark(prompt, args.quant_llm)
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
if __name__ == "__main__":
|
| 163 |
+
main()
|
llm-awq/tinychat/nvila_demo.py
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
|
| 3 |
+
from termcolor import colored
|
| 4 |
+
from huggingface_hub import hf_hub_download
|
| 5 |
+
import os
|
| 6 |
+
import llava
|
| 7 |
+
from llava import conversation as clib
|
| 8 |
+
from llava.media import Image, Video
|
| 9 |
+
import torch
|
| 10 |
+
from awq.quantize import fake_quant
|
| 11 |
+
from transformers import AutoConfig
|
| 12 |
+
from tinychat.utils.load_quant import load_awq_model
|
| 13 |
+
from tinychat.utils.llava_image_processing import (
|
| 14 |
+
load_images,
|
| 15 |
+
vis_images,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def skip(*args, **kwargs):
|
| 20 |
+
pass
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
from tinychat.utils.tune import (
|
| 24 |
+
device_warmup,
|
| 25 |
+
tune_all_wqlinears,
|
| 26 |
+
tune_llava_patch_embedding,
|
| 27 |
+
)
|
| 28 |
+
from tinychat.utils.prompt_templates import (
|
| 29 |
+
get_prompter,
|
| 30 |
+
get_stop_token_ids,
|
| 31 |
+
get_image_token,
|
| 32 |
+
)
|
| 33 |
+
from llava.utils.media import extract_media
|
| 34 |
+
import tinychat.utils.constants
|
| 35 |
+
from tinychat.stream_generators.NVILA_stream_gen import NVILAStreamGenerator
|
| 36 |
+
from tinychat.utils.conversation_utils import gen_params, stream_output, TimeStats
|
| 37 |
+
|
| 38 |
+
os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
| 39 |
+
|
| 40 |
+
def download_model_file(
|
| 41 |
+
repo_id: str = "Efficient-Large-Model/NVILA-AWQ",
|
| 42 |
+
filename: str = None,
|
| 43 |
+
local_dir: str = "./hf_cache",
|
| 44 |
+
force_download: bool = False,
|
| 45 |
+
) -> str:
|
| 46 |
+
os.makedirs(local_dir, exist_ok=True)
|
| 47 |
+
local_path = os.path.join(local_dir, filename)
|
| 48 |
+
if force_download or not os.path.exists(local_path):
|
| 49 |
+
print(f"Downloading {filename} from {repo_id}...")
|
| 50 |
+
hf_hub_download(
|
| 51 |
+
repo_id=repo_id,
|
| 52 |
+
filename=filename,
|
| 53 |
+
local_dir=local_dir,
|
| 54 |
+
local_dir_use_symlinks=False,
|
| 55 |
+
resume_download=True,
|
| 56 |
+
force_download=force_download,
|
| 57 |
+
)
|
| 58 |
+
print(f"File saved to: {local_path}")
|
| 59 |
+
|
| 60 |
+
return local_path
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def main(args):
|
| 65 |
+
# Accelerate model initialization
|
| 66 |
+
setattr(torch.nn.Linear, "reset_parameters", lambda self: None)
|
| 67 |
+
setattr(torch.nn.LayerNorm, "reset_parameters", lambda self: None)
|
| 68 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 69 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 70 |
+
torch.nn.init.uniform_ = skip
|
| 71 |
+
torch.nn.init.normal_ = skip
|
| 72 |
+
tinychat.utils.constants.max_seq_len = args.max_seq_len
|
| 73 |
+
|
| 74 |
+
# Prepare model
|
| 75 |
+
from tinychat.models.nvila_qwen2 import NVILAQwen2
|
| 76 |
+
from transformers import AutoConfig
|
| 77 |
+
from tinychat.models.qwen2 import Qwen2ForCausalLM
|
| 78 |
+
|
| 79 |
+
config = AutoConfig.from_pretrained(args.model_path)
|
| 80 |
+
config.resume_path = args.model_path
|
| 81 |
+
if args.quant_llm or args.all:
|
| 82 |
+
model = NVILAQwen2(config, False).half()
|
| 83 |
+
else:
|
| 84 |
+
model = NVILAQwen2(config, True).half()
|
| 85 |
+
|
| 86 |
+
if args.smooth_VT or args.all:
|
| 87 |
+
from awq.quantize import smooth_lm
|
| 88 |
+
args.act_scale_path=download_model_file(filename=args.act_scale_path)
|
| 89 |
+
act_scales = torch.load(args.act_scale_path)
|
| 90 |
+
smooth_lm(model.vision_tower, act_scales, 0.3)
|
| 91 |
+
if args.quant_llm or args.all:
|
| 92 |
+
from tinychat.modules import (
|
| 93 |
+
make_quant_norm,
|
| 94 |
+
make_quant_attn,
|
| 95 |
+
make_fused_mlp,
|
| 96 |
+
make_fused_vision_attn,
|
| 97 |
+
)
|
| 98 |
+
args.quant_path=download_model_file(filename=args.quant_path)
|
| 99 |
+
model.llm = Qwen2ForCausalLM(model.llm_cfg).half()
|
| 100 |
+
model.llm = load_awq_model(model.llm, args.quant_path, 4, 128, args.device)
|
| 101 |
+
make_quant_attn(model.llm, args.device, True)
|
| 102 |
+
make_quant_norm(model.llm)
|
| 103 |
+
model.llm.cpu()
|
| 104 |
+
model.llm.resize_token_embeddings(len(model.tokenizer))
|
| 105 |
+
|
| 106 |
+
if args.quant_VT or args.all:
|
| 107 |
+
from tinychat.modules import QuantSiglipEncoder
|
| 108 |
+
|
| 109 |
+
if args.fakequant_VT:
|
| 110 |
+
fake_quant(model.vision_tower.vision_tower.vision_model.encoder)
|
| 111 |
+
else:
|
| 112 |
+
model.vision_tower.vision_tower.vision_model.encoder = QuantSiglipEncoder(
|
| 113 |
+
model.vision_tower.vision_tower.vision_model.encoder
|
| 114 |
+
)
|
| 115 |
+
model = model.cuda().eval()
|
| 116 |
+
device_warmup(args.device)
|
| 117 |
+
tune_llava_patch_embedding(model.vision_tower, device=args.device)
|
| 118 |
+
|
| 119 |
+
# Pre-prepare media
|
| 120 |
+
prompt = []
|
| 121 |
+
media_files = []
|
| 122 |
+
if args.media is not None:
|
| 123 |
+
for media in args.media or []:
|
| 124 |
+
if any(media.endswith(ext) for ext in [".jpg", ".jpeg", ".png"]):
|
| 125 |
+
media = Image(media)
|
| 126 |
+
media_files.append(media)
|
| 127 |
+
media_prompt = "<image>"
|
| 128 |
+
elif any(media.endswith(ext) for ext in [".mp4", ".mkv", ".webm"]):
|
| 129 |
+
media = Video(media)
|
| 130 |
+
media_files.append(media)
|
| 131 |
+
media_prompt = "<vila/video>"
|
| 132 |
+
else:
|
| 133 |
+
raise ValueError(f"Unsupported media type: {media}")
|
| 134 |
+
prompt.append(media)
|
| 135 |
+
media_num = len(media_files)
|
| 136 |
+
if args.vis_image:
|
| 137 |
+
print("=" * 50)
|
| 138 |
+
print("Input Image:")
|
| 139 |
+
vis_images(args.media)
|
| 140 |
+
conversation = [{"from": "human", "value": prompt}]
|
| 141 |
+
media, media_cfg = model.prepare_media(conversation)
|
| 142 |
+
# Prepare streaming
|
| 143 |
+
stream_generator = NVILAStreamGenerator
|
| 144 |
+
# Prepare prompt
|
| 145 |
+
if args.max_seq_len <= 1024:
|
| 146 |
+
short_prompt = True
|
| 147 |
+
else:
|
| 148 |
+
short_prompt = False
|
| 149 |
+
model_prompter = get_prompter(
|
| 150 |
+
args.model_type, args.model_path, short_prompt, args.empty_prompt
|
| 151 |
+
)
|
| 152 |
+
stop_token_ids = get_stop_token_ids(args.model_type, args.model_path)
|
| 153 |
+
count = 0
|
| 154 |
+
|
| 155 |
+
if args.empty_prompt:
|
| 156 |
+
input_indicator = "Input: "
|
| 157 |
+
output_indicator = "Generated: "
|
| 158 |
+
else:
|
| 159 |
+
input_indicator = "USER: "
|
| 160 |
+
output_indicator = "ASSISTANT: "
|
| 161 |
+
|
| 162 |
+
count = 0
|
| 163 |
+
model.eval()
|
| 164 |
+
time_stats = TimeStats()
|
| 165 |
+
start_pos = 0
|
| 166 |
+
while True:
|
| 167 |
+
# Get input from the user
|
| 168 |
+
print("=" * 50)
|
| 169 |
+
input_prompt = input(input_indicator)
|
| 170 |
+
print("-" * 50)
|
| 171 |
+
if input_prompt == "":
|
| 172 |
+
print("EXIT...")
|
| 173 |
+
time_stats.show()
|
| 174 |
+
break
|
| 175 |
+
if count == 0: # Insert media here
|
| 176 |
+
if args.media is not None:
|
| 177 |
+
if media_prompt in input_prompt:
|
| 178 |
+
input_prompt = input_prompt
|
| 179 |
+
else:
|
| 180 |
+
input_prompt = media_prompt * media_num + input_prompt
|
| 181 |
+
model_prompter.insert_prompt(input_prompt)
|
| 182 |
+
else:
|
| 183 |
+
model_prompter.insert_prompt(input_prompt)
|
| 184 |
+
if args.chunk_prefilling:
|
| 185 |
+
media = None
|
| 186 |
+
media_cfg = None
|
| 187 |
+
output_stream = stream_generator(
|
| 188 |
+
model,
|
| 189 |
+
gen_params,
|
| 190 |
+
model_prompter.model_input,
|
| 191 |
+
media,
|
| 192 |
+
media_cfg,
|
| 193 |
+
start_pos,
|
| 194 |
+
device=args.device,
|
| 195 |
+
stop_token_ids=stop_token_ids,
|
| 196 |
+
chunk_prefilling=args.chunk_prefilling,
|
| 197 |
+
quant_llm=args.quant_llm or args.all,
|
| 198 |
+
)
|
| 199 |
+
print(output_indicator, end="", flush=True)
|
| 200 |
+
if count == 0:
|
| 201 |
+
outputs, total_tokens = stream_output(output_stream, time_stats)
|
| 202 |
+
else:
|
| 203 |
+
outputs, total_tokens = stream_output(output_stream)
|
| 204 |
+
if args.chunk_prefilling:
|
| 205 |
+
start_pos += total_tokens
|
| 206 |
+
if (
|
| 207 |
+
args.single_round is not True and args.max_seq_len > 512
|
| 208 |
+
): # Only memorize previous conversations when kv_cache_size > 512
|
| 209 |
+
model_prompter.update_template(outputs, args.chunk_prefilling)
|
| 210 |
+
count += 1
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
if __name__ == "__main__":
|
| 214 |
+
parser = argparse.ArgumentParser()
|
| 215 |
+
parser.add_argument(
|
| 216 |
+
"--model_type", type=str, default="LLaMa", help="type of the model"
|
| 217 |
+
)
|
| 218 |
+
parser.add_argument(
|
| 219 |
+
"--model-path", type=str, default="/data/llm/checkpoints/llava/llava-v1.5-7b"
|
| 220 |
+
)
|
| 221 |
+
parser.add_argument(
|
| 222 |
+
"--quant_path",
|
| 223 |
+
type=str,
|
| 224 |
+
default="/data/llm/checkpoints/llava/llava-v1.5-7b-w4-g128-awq.pt",
|
| 225 |
+
)
|
| 226 |
+
parser.add_argument(
|
| 227 |
+
"--act_scale_path",
|
| 228 |
+
type=str,
|
| 229 |
+
default="/PATH/TO/SCALE",
|
| 230 |
+
)
|
| 231 |
+
parser.add_argument(
|
| 232 |
+
"--media", type=str, nargs="+", help="Multi-modal input (Video or image path)"
|
| 233 |
+
)
|
| 234 |
+
parser.add_argument("--device", type=str, default="cuda:0")
|
| 235 |
+
parser.add_argument("--max_seq_len", type=int, default=2048)
|
| 236 |
+
parser.add_argument(
|
| 237 |
+
"--single_round",
|
| 238 |
+
action="store_true",
|
| 239 |
+
help="whether to memorize previous conversations",
|
| 240 |
+
)
|
| 241 |
+
parser.add_argument(
|
| 242 |
+
"--vis-image",
|
| 243 |
+
action="store_true",
|
| 244 |
+
help="whether to visualize the image while chatting",
|
| 245 |
+
)
|
| 246 |
+
parser.add_argument(
|
| 247 |
+
"--empty-prompt",
|
| 248 |
+
action="store_true",
|
| 249 |
+
help="whether to use empty prompt template",
|
| 250 |
+
)
|
| 251 |
+
parser.add_argument(
|
| 252 |
+
"--flash_attn",
|
| 253 |
+
action="store_true",
|
| 254 |
+
help="whether to use flash attention",
|
| 255 |
+
)
|
| 256 |
+
parser.add_argument(
|
| 257 |
+
"--chunk_prefilling",
|
| 258 |
+
action="store_true",
|
| 259 |
+
help="If used, in context stage, the history tokens will not be recalculated, greatly speeding up the calculation",
|
| 260 |
+
)
|
| 261 |
+
# smooth and quantization options
|
| 262 |
+
parser.add_argument("--quant_llm", action="store_true")
|
| 263 |
+
parser.add_argument("--quant_VT", action="store_true")
|
| 264 |
+
parser.add_argument("--smooth_VT", action="store_true")
|
| 265 |
+
parser.add_argument("--all", action="store_true")
|
| 266 |
+
parser.add_argument(
|
| 267 |
+
"--fakequant_VT",
|
| 268 |
+
action="store_true",
|
| 269 |
+
help="Use fake quant or real quant for VisionTower",
|
| 270 |
+
)
|
| 271 |
+
args = parser.parse_args()
|
| 272 |
+
main(args)
|
llm-awq/tinychat/offline-weight-repacker.py
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import argparse
|
| 2 |
+
import torch
|
| 3 |
+
import numpy as np
|
| 4 |
+
from typing import List
|
| 5 |
+
from collections import OrderedDict
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def qweight_unpack(qweight):
|
| 9 |
+
assert qweight.dtype == torch.int32
|
| 10 |
+
n = qweight.shape[0]
|
| 11 |
+
k = qweight.shape[1] * 8
|
| 12 |
+
unpacked_qweight = torch.zeros((n, k), dtype=torch.int32, device=qweight.device)
|
| 13 |
+
mask = 0x0000000F
|
| 14 |
+
for kk in range(k):
|
| 15 |
+
ele_offset = kk // 8
|
| 16 |
+
bit_offset = (kk % 8) * 4
|
| 17 |
+
unpacked_qweight[:, kk] = (qweight[:, ele_offset] >> bit_offset) & mask
|
| 18 |
+
|
| 19 |
+
return unpacked_qweight
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def packing_v2_from_unpacked(unpacked_qweight, interleave, kstride):
|
| 23 |
+
# unpacked_qweight: [N, K]
|
| 24 |
+
N = unpacked_qweight.shape[0]
|
| 25 |
+
K = unpacked_qweight.shape[1]
|
| 26 |
+
|
| 27 |
+
Packed_Kernel = unpacked_qweight.cpu().numpy().reshape(N, K // 32, 32)
|
| 28 |
+
# np.arange(32).reshape(4, 4, 2).transpose(1, 0, 2) => [0, 1, 8, 9, 16, 17, 24, 25, ...]
|
| 29 |
+
Packed_Kernel = Packed_Kernel.reshape(N, K // 32, 4, 4, 2).transpose(0, 1, 3, 2, 4)
|
| 30 |
+
Packed_Kernel = Packed_Kernel.reshape(N, K // 32, 32)
|
| 31 |
+
|
| 32 |
+
# reorder each 8 weights for fast dequantization
|
| 33 |
+
# [0, 1, 2, 3, 4, 5, 6, 7] => [0, 2, 4, 6, 1, 3, 5, 7]
|
| 34 |
+
Packed_Kernel = Packed_Kernel.reshape(N, K // 32, 4, 8)
|
| 35 |
+
Packed_Kernel = Packed_Kernel.reshape(N, K // 32, 4, 4, 2).transpose(0, 1, 2, 4, 3)
|
| 36 |
+
Packed_Kernel = Packed_Kernel.reshape(N, K)
|
| 37 |
+
|
| 38 |
+
# interleaving every four rows
|
| 39 |
+
Packed_Kernel = Packed_Kernel.reshape(
|
| 40 |
+
N // interleave, interleave, K // kstride, kstride
|
| 41 |
+
)
|
| 42 |
+
# N // 4, K // 64, 4, 64
|
| 43 |
+
Packed_Kernel = Packed_Kernel.transpose(0, 2, 1, 3)
|
| 44 |
+
Packed_Kernel = Packed_Kernel.reshape(
|
| 45 |
+
N // interleave, K // kstride, kstride, interleave
|
| 46 |
+
)
|
| 47 |
+
# Packing -> (N // 4, K // 64, 64)
|
| 48 |
+
Packed_Kernel = (
|
| 49 |
+
Packed_Kernel[..., 0]
|
| 50 |
+
| (Packed_Kernel[..., 1] << 4)
|
| 51 |
+
| (Packed_Kernel[..., 2] << 8)
|
| 52 |
+
| (Packed_Kernel[..., 3] << 12)
|
| 53 |
+
)
|
| 54 |
+
# reshape to (N // 4, K), FP16 format
|
| 55 |
+
Packed_Kernel = Packed_Kernel.reshape(N // interleave, K)
|
| 56 |
+
qweight_v2 = (
|
| 57 |
+
torch.tensor(Packed_Kernel.astype("int16"))
|
| 58 |
+
.to(unpacked_qweight.device)
|
| 59 |
+
.contiguous()
|
| 60 |
+
)
|
| 61 |
+
return qweight_v2
|
| 62 |
+
|
| 63 |
+
|
| 64 |
+
def multiply_scale_qzero_negative(scales, qzeros, zp_shift=-8):
|
| 65 |
+
pack_size = 8
|
| 66 |
+
k_groups = scales.shape[1]
|
| 67 |
+
scaled_zeros = torch.zeros_like(scales)
|
| 68 |
+
for group_idx in range(k_groups):
|
| 69 |
+
zero_idx = group_idx // pack_size
|
| 70 |
+
zero_offset = group_idx % pack_size
|
| 71 |
+
zero = qzeros[:, zero_idx] >> (4 * zero_offset) & 0x0000000F
|
| 72 |
+
scaled_zeros[:, group_idx] = scales[:, group_idx] * zero.to(scales.dtype)
|
| 73 |
+
return -(scaled_zeros + (zp_shift * scales))
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def qweight_pack_v1_to_v2(qweight, interleave, kstride):
|
| 77 |
+
unpacked_qweight = qweight_unpack(qweight)
|
| 78 |
+
qweight_v2 = packing_v2_from_unpacked(unpacked_qweight, interleave, kstride)
|
| 79 |
+
return qweight_v2
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def ckpt_check():
|
| 83 |
+
parser = argparse.ArgumentParser()
|
| 84 |
+
parser.add_argument("--input1", type=str, default="./vicuna-7b-w4-g128-awq-v2-1.pt")
|
| 85 |
+
parser.add_argument("--input2", type=str, default="./vicuna-7b-w4-g128-awq-v2-2.pt")
|
| 86 |
+
args = parser.parse_args()
|
| 87 |
+
|
| 88 |
+
model_dict1 = torch.load(args.input1)
|
| 89 |
+
model_dict2 = torch.load(args.input2)
|
| 90 |
+
|
| 91 |
+
keys = model_dict1.keys()
|
| 92 |
+
for key in keys:
|
| 93 |
+
param = model_dict1[key]
|
| 94 |
+
assert type(param) == torch.Tensor
|
| 95 |
+
if (
|
| 96 |
+
"qweight" in key
|
| 97 |
+
or "scales" in key
|
| 98 |
+
or "qzeros" in key
|
| 99 |
+
or "scaled_zeros" in key
|
| 100 |
+
):
|
| 101 |
+
print("=" * 50)
|
| 102 |
+
print(key)
|
| 103 |
+
# print(model_dict1[key])
|
| 104 |
+
# print(model_dict2[key])
|
| 105 |
+
diff = torch.max(torch.abs(model_dict2[key] - model_dict1[key]))
|
| 106 |
+
print(diff)
|
| 107 |
+
assert diff < 1e-6
|
| 108 |
+
print("=" * 50)
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def offline_repacker():
|
| 112 |
+
parser = argparse.ArgumentParser()
|
| 113 |
+
parser.add_argument("--input", type=str, default="./vicuna-7b-w4-g128-awq.pt")
|
| 114 |
+
parser.add_argument("--output", type=str, default="./vicuna-7b-w4-g128-awq-v2.pt")
|
| 115 |
+
args = parser.parse_args()
|
| 116 |
+
|
| 117 |
+
model_dict = torch.load(args.input)
|
| 118 |
+
model_dict_v2 = OrderedDict()
|
| 119 |
+
|
| 120 |
+
keys = model_dict.keys()
|
| 121 |
+
for key in keys:
|
| 122 |
+
param = model_dict[key]
|
| 123 |
+
assert type(param) == torch.Tensor
|
| 124 |
+
if "qweight" in key:
|
| 125 |
+
print("repacking:", key)
|
| 126 |
+
qweight = param
|
| 127 |
+
qweight_v2 = qweight_pack_v1_to_v2(qweight, interleave=4, kstride=64)
|
| 128 |
+
model_dict_v2[key] = qweight_v2
|
| 129 |
+
elif "scales" in key:
|
| 130 |
+
print("repacking:", key)
|
| 131 |
+
scales = param
|
| 132 |
+
# print(scales.shape)
|
| 133 |
+
scales_v2 = scales.transpose(1, 0).contiguous()
|
| 134 |
+
model_dict_v2[key] = scales_v2
|
| 135 |
+
|
| 136 |
+
# deal with corresponding zero points
|
| 137 |
+
zeros_key = key.replace("scales", "qzeros")
|
| 138 |
+
print("repacking:", zeros_key)
|
| 139 |
+
|
| 140 |
+
zeros_key_v2 = key.replace("scales", "scaled_zeros")
|
| 141 |
+
qzeros = model_dict[zeros_key]
|
| 142 |
+
scaled_zeros_v2 = multiply_scale_qzero_negative(scales, qzeros, zp_shift=0)
|
| 143 |
+
# K // G, N
|
| 144 |
+
scaled_zeros_v2 = scaled_zeros_v2.transpose(1, 0).contiguous()
|
| 145 |
+
model_dict_v2[zeros_key_v2] = scaled_zeros_v2
|
| 146 |
+
elif "qzeros" in key:
|
| 147 |
+
pass
|
| 148 |
+
else:
|
| 149 |
+
print("copying:", key)
|
| 150 |
+
model_dict_v2[key] = param
|
| 151 |
+
|
| 152 |
+
torch.save(model_dict_v2, args.output)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
if __name__ == "__main__":
|
| 156 |
+
offline_repacker()
|
| 157 |
+
# ckpt_check()
|
llm-awq/tinychat/scripts/internvl_demo.sh
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL_PATH=PATH_TO_INTERNVL
|
| 2 |
+
MODEL_NAME=InternVL3-8B
|
| 3 |
+
|
| 4 |
+
# run AWQ search
|
| 5 |
+
python -m awq.entry --model_path $MODEL_PATH \
|
| 6 |
+
--w_bit 4 --q_group_size 128 \
|
| 7 |
+
--run_awq --dump_awq awq_cache/$MODEL_NAME-w4-g128.pt
|
| 8 |
+
|
| 9 |
+
# generate real quantized weights (w4)
|
| 10 |
+
python -m awq.entry --model_path $MODEL_PATH \
|
| 11 |
+
--w_bit 4 --q_group_size 128 --load_awq awq_cache/$MODEL_NAME-w4-g128.pt \
|
| 12 |
+
--q_backend real --dump_quant quant_cache/$MODEL_NAME-w4-128-awq.pt
|
| 13 |
+
|
| 14 |
+
# Run the TinyChat demo:
|
| 15 |
+
PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True python internvl_demo.py --model-path $MODEL_PATH \
|
| 16 |
+
--quant_path quant_cache/$MODEL_NAME-w4-128-awq-v2.pt \
|
| 17 |
+
--media ../figures/vila-logo.jpg --max_seq_len 4096 --chunk \
|
| 18 |
+
--model_type internvl3 --quant_VT --quant_llm
|
llm-awq/tinychat/scripts/llama2_demo.sh
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL_PATH=/data/llm/checkpoints/llama2-hf
|
| 2 |
+
MODEL_NAME=llama-2-7b-chat
|
| 3 |
+
|
| 4 |
+
# # Perform AWQ search and save search results (we already did it for you):
|
| 5 |
+
# mkdir -p awq_cache
|
| 6 |
+
# python -m awq.entry --model_path $MODEL_PATH/$MODEL_NAME \
|
| 7 |
+
# --w_bit 4 --q_group_size 128 \
|
| 8 |
+
# --run_awq --dump_awq awq_cache/llama-2-7b-chat-w4-g128.pt
|
| 9 |
+
|
| 10 |
+
# Generate real quantized weights (INT4):
|
| 11 |
+
mkdir -p quant_cache
|
| 12 |
+
python -m awq.entry --model_path $MODEL_PATH/$MODEL_NAME \
|
| 13 |
+
--w_bit 4 --q_group_size 128 \
|
| 14 |
+
--load_awq awq_cache/llama-2-7b-chat-w4-g128.pt \
|
| 15 |
+
--q_backend real --dump_quant quant_cache/llama-2-7b-chat-w4-g128-awq.pt
|
| 16 |
+
|
| 17 |
+
# Run the TinyChat demo:
|
| 18 |
+
python demo.py --model_type llama \
|
| 19 |
+
--model_path $MODEL_PATH/$MODEL_NAME \
|
| 20 |
+
--q_group_size 128 --load_quant quant_cache/llama-2-7b-chat-w4-g128-awq.pt \
|
| 21 |
+
--precision W4A16
|
| 22 |
+
|
| 23 |
+
# Split checkpoint into shards for mem-efficient loading:
|
| 24 |
+
python split_ckpt.py --input_path quant_cache/llama-2-7b-chat-w4-g128-awq.pt \
|
| 25 |
+
--output_path quant_cache/llama-2-7b-chat-w4-g128-awq
|
| 26 |
+
|
| 27 |
+
# Run the TinyChat demo in mem_efficient_load mode:
|
| 28 |
+
python demo.py --model_type llama \
|
| 29 |
+
--model_path $MODEL_PATH/$MODEL_NAME \
|
| 30 |
+
--q_group_size 128 --load_quant quant_cache/llama-2-7b-chat-w4-g128-awq \
|
| 31 |
+
--precision W4A16 --mem_efficient_load --flash --chunk_prefilling
|
llm-awq/tinychat/scripts/nvila_demo.sh
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MODEL_PATH=PATH_TO_NVILA
|
| 2 |
+
MODEL_NAME=NVILA-8B
|
| 3 |
+
|
| 4 |
+
# run AWQ search
|
| 5 |
+
python -m awq.entry --model_path $MODEL_PATH \
|
| 6 |
+
--smooth_scale --media_path https://qianwen-res.oss-cn-beijing.aliyuncs.com/Qwen2-VL/space_woaudio.mp4 \
|
| 7 |
+
--act_scale_path awq_cache/$MODEL_NAME-smooth-scale.pt --vila-20 \
|
| 8 |
+
--w_bit 4 --q_group_size 128 \
|
| 9 |
+
--run_awq --dump_awq awq_cache/$MODEL_NAME.pt
|
| 10 |
+
|
| 11 |
+
# generate real quantized weights (w4)
|
| 12 |
+
python -m awq.entry --model_path $MODEL_PATH/llm \
|
| 13 |
+
--w_bit 4 --q_group_size 128 \
|
| 14 |
+
--load_awq awq_cache/$MODEL_NAME.pt \
|
| 15 |
+
--q_backend real --dump_quant quant_cache/$MODEL_NAME-w4-g128-awq.pt --vila-20
|
| 16 |
+
|
| 17 |
+
# Run the TinyChat demo:
|
| 18 |
+
python nvila_demo.py --model-path $MODEL_PATH \
|
| 19 |
+
--quant_path quant_cache/$MODEL_NAME-w4-g128-awq.pt \
|
| 20 |
+
--media ../figures/nvila-logo.jpg \
|
| 21 |
+
--act_scale_path awq_cache/$MODEL_NAME-smooth-scale.pt \
|
| 22 |
+
--all --chunk --model_type nvila --vis_image
|
llm-awq/tinychat/serve/README.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
## Gradio demo: VILA with TinyChat
|
| 2 |
+
|
| 3 |
+
We provide scripts for building your own gradio server to run VILA models with TinyChat. Please run the following commands to launch the server.
|
| 4 |
+
|
| 5 |
+
#### Launch a controller
|
| 6 |
+
```bash
|
| 7 |
+
python -m tinychat.serve.controller --host 0.0.0.0 --port 10000
|
| 8 |
+
```
|
| 9 |
+
|
| 10 |
+
#### Launch gradio web server.
|
| 11 |
+
```bash
|
| 12 |
+
python -m tinychat.serve.gradio_web_server --controller http://localhost:10000 --model-list-mode reload --share --auto-pad-image-token
|
| 13 |
+
```
|
| 14 |
+
After launching this script, the web interface will be served on your machine and you can access it with a public URL (or localhost URL).
|
| 15 |
+
|
| 16 |
+
#### Launch a model worker
|
| 17 |
+
|
| 18 |
+
```bash
|
| 19 |
+
python -m tinychat.serve.model_worker_new --host 0.0.0.0 --controller http://localhost:10000 --port 40000 --worker http://localhost:40000 --model-path <path-to-fp16-hf-model> --quant-path <path-to-awq-checkpoint>
|
| 20 |
+
# Please change tinychat.serve.model_worker_new to tinychat.serve.model_worker if you want to serve VILA rather than VILA-1.5
|
| 21 |
+
```
|
| 22 |
+
|
| 23 |
+
Note: You can launch multiple model workers onto the same web server. And please remember to specify different ports for each model worker.
|
| 24 |
+
|
| 25 |
+
### Acknowlegement
|
| 26 |
+
|
| 27 |
+
This demo is inspired by [LLaVA](https://github.com/haotian-liu/LLaVA). We thank LLaVA for providing an elegant way to build the Gradio Web UI.
|
llm-awq/tinychat/serve/controller.py
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
A controller manages distributed workers.
|
| 18 |
+
It sends worker addresses to clients.
|
| 19 |
+
"""
|
| 20 |
+
import argparse
|
| 21 |
+
import asyncio
|
| 22 |
+
import dataclasses
|
| 23 |
+
from enum import Enum, auto
|
| 24 |
+
import json
|
| 25 |
+
import logging
|
| 26 |
+
import time
|
| 27 |
+
from typing import List, Union
|
| 28 |
+
import threading
|
| 29 |
+
|
| 30 |
+
from fastapi import FastAPI, Request
|
| 31 |
+
from fastapi.responses import StreamingResponse
|
| 32 |
+
import numpy as np
|
| 33 |
+
import requests
|
| 34 |
+
import uvicorn
|
| 35 |
+
|
| 36 |
+
from tinychat.utils.constants import CONTROLLER_HEART_BEAT_EXPIRATION
|
| 37 |
+
from tinychat.utils.log_utils import build_logger, server_error_msg
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
logger = build_logger("controller", "controller.log")
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class DispatchMethod(Enum):
|
| 44 |
+
LOTTERY = auto()
|
| 45 |
+
SHORTEST_QUEUE = auto()
|
| 46 |
+
|
| 47 |
+
@classmethod
|
| 48 |
+
def from_str(cls, name):
|
| 49 |
+
if name == "lottery":
|
| 50 |
+
return cls.LOTTERY
|
| 51 |
+
elif name == "shortest_queue":
|
| 52 |
+
return cls.SHORTEST_QUEUE
|
| 53 |
+
else:
|
| 54 |
+
raise ValueError(f"Invalid dispatch method")
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
@dataclasses.dataclass
|
| 58 |
+
class WorkerInfo:
|
| 59 |
+
model_names: List[str]
|
| 60 |
+
speed: int
|
| 61 |
+
queue_length: int
|
| 62 |
+
check_heart_beat: bool
|
| 63 |
+
last_heart_beat: str
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def heart_beat_controller(controller):
|
| 67 |
+
while True:
|
| 68 |
+
time.sleep(CONTROLLER_HEART_BEAT_EXPIRATION)
|
| 69 |
+
controller.remove_stable_workers_by_expiration()
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
class Controller:
|
| 73 |
+
def __init__(self, dispatch_method: str):
|
| 74 |
+
# Dict[str -> WorkerInfo]
|
| 75 |
+
self.worker_info = {}
|
| 76 |
+
self.dispatch_method = DispatchMethod.from_str(dispatch_method)
|
| 77 |
+
|
| 78 |
+
self.heart_beat_thread = threading.Thread(
|
| 79 |
+
target=heart_beat_controller, args=(self,)
|
| 80 |
+
)
|
| 81 |
+
self.heart_beat_thread.start()
|
| 82 |
+
|
| 83 |
+
logger.info("Init controller")
|
| 84 |
+
|
| 85 |
+
def register_worker(
|
| 86 |
+
self, worker_name: str, check_heart_beat: bool, worker_status: dict
|
| 87 |
+
):
|
| 88 |
+
if worker_name not in self.worker_info:
|
| 89 |
+
logger.info(f"Register a new worker: {worker_name}")
|
| 90 |
+
else:
|
| 91 |
+
logger.info(f"Register an existing worker: {worker_name}")
|
| 92 |
+
|
| 93 |
+
if not worker_status:
|
| 94 |
+
worker_status = self.get_worker_status(worker_name)
|
| 95 |
+
if not worker_status:
|
| 96 |
+
return False
|
| 97 |
+
|
| 98 |
+
self.worker_info[worker_name] = WorkerInfo(
|
| 99 |
+
worker_status["model_names"],
|
| 100 |
+
worker_status["speed"],
|
| 101 |
+
worker_status["queue_length"],
|
| 102 |
+
check_heart_beat,
|
| 103 |
+
time.time(),
|
| 104 |
+
)
|
| 105 |
+
|
| 106 |
+
logger.info(f"Register done: {worker_name}, {worker_status}")
|
| 107 |
+
return True
|
| 108 |
+
|
| 109 |
+
def get_worker_status(self, worker_name: str):
|
| 110 |
+
try:
|
| 111 |
+
r = requests.post(worker_name + "/worker_get_status", timeout=5)
|
| 112 |
+
except requests.exceptions.RequestException as e:
|
| 113 |
+
logger.error(f"Get status fails: {worker_name}, {e}")
|
| 114 |
+
return None
|
| 115 |
+
|
| 116 |
+
if r.status_code != 200:
|
| 117 |
+
logger.error(f"Get status fails: {worker_name}, {r}")
|
| 118 |
+
return None
|
| 119 |
+
|
| 120 |
+
return r.json()
|
| 121 |
+
|
| 122 |
+
def remove_worker(self, worker_name: str):
|
| 123 |
+
del self.worker_info[worker_name]
|
| 124 |
+
|
| 125 |
+
def refresh_all_workers(self):
|
| 126 |
+
old_info = dict(self.worker_info)
|
| 127 |
+
self.worker_info = {}
|
| 128 |
+
|
| 129 |
+
for w_name, w_info in old_info.items():
|
| 130 |
+
if not self.register_worker(w_name, w_info.check_heart_beat, None):
|
| 131 |
+
logger.info(f"Remove stale worker: {w_name}")
|
| 132 |
+
|
| 133 |
+
def list_models(self):
|
| 134 |
+
model_names = set()
|
| 135 |
+
|
| 136 |
+
for w_name, w_info in self.worker_info.items():
|
| 137 |
+
model_names.update(w_info.model_names)
|
| 138 |
+
|
| 139 |
+
return list(model_names)
|
| 140 |
+
|
| 141 |
+
def get_worker_address(self, model_name: str):
|
| 142 |
+
if self.dispatch_method == DispatchMethod.LOTTERY:
|
| 143 |
+
worker_names = []
|
| 144 |
+
worker_speeds = []
|
| 145 |
+
for w_name, w_info in self.worker_info.items():
|
| 146 |
+
if model_name in w_info.model_names:
|
| 147 |
+
worker_names.append(w_name)
|
| 148 |
+
worker_speeds.append(w_info.speed)
|
| 149 |
+
worker_speeds = np.array(worker_speeds, dtype=np.float32)
|
| 150 |
+
norm = np.sum(worker_speeds)
|
| 151 |
+
if norm < 1e-4:
|
| 152 |
+
return ""
|
| 153 |
+
worker_speeds = worker_speeds / norm
|
| 154 |
+
if True: # Directly return address
|
| 155 |
+
pt = np.random.choice(np.arange(len(worker_names)), p=worker_speeds)
|
| 156 |
+
worker_name = worker_names[pt]
|
| 157 |
+
return worker_name
|
| 158 |
+
|
| 159 |
+
# Check status before returning
|
| 160 |
+
while True:
|
| 161 |
+
pt = np.random.choice(np.arange(len(worker_names)), p=worker_speeds)
|
| 162 |
+
worker_name = worker_names[pt]
|
| 163 |
+
|
| 164 |
+
if self.get_worker_status(worker_name):
|
| 165 |
+
break
|
| 166 |
+
else:
|
| 167 |
+
self.remove_worker(worker_name)
|
| 168 |
+
worker_speeds[pt] = 0
|
| 169 |
+
norm = np.sum(worker_speeds)
|
| 170 |
+
if norm < 1e-4:
|
| 171 |
+
return ""
|
| 172 |
+
worker_speeds = worker_speeds / norm
|
| 173 |
+
continue
|
| 174 |
+
return worker_name
|
| 175 |
+
elif self.dispatch_method == DispatchMethod.SHORTEST_QUEUE:
|
| 176 |
+
worker_names = []
|
| 177 |
+
worker_qlen = []
|
| 178 |
+
for w_name, w_info in self.worker_info.items():
|
| 179 |
+
if model_name in w_info.model_names:
|
| 180 |
+
worker_names.append(w_name)
|
| 181 |
+
worker_qlen.append(w_info.queue_length / w_info.speed)
|
| 182 |
+
if len(worker_names) == 0:
|
| 183 |
+
return ""
|
| 184 |
+
min_index = np.argmin(worker_qlen)
|
| 185 |
+
w_name = worker_names[min_index]
|
| 186 |
+
self.worker_info[w_name].queue_length += 1
|
| 187 |
+
logger.info(
|
| 188 |
+
f"names: {worker_names}, queue_lens: {worker_qlen}, ret: {w_name}"
|
| 189 |
+
)
|
| 190 |
+
return w_name
|
| 191 |
+
else:
|
| 192 |
+
raise ValueError(f"Invalid dispatch method: {self.dispatch_method}")
|
| 193 |
+
|
| 194 |
+
def receive_heart_beat(self, worker_name: str, queue_length: int):
|
| 195 |
+
if worker_name not in self.worker_info:
|
| 196 |
+
logger.info(f"Receive unknown heart beat. {worker_name}")
|
| 197 |
+
return False
|
| 198 |
+
|
| 199 |
+
self.worker_info[worker_name].queue_length = queue_length
|
| 200 |
+
self.worker_info[worker_name].last_heart_beat = time.time()
|
| 201 |
+
logger.info(f"Receive heart beat. {worker_name}")
|
| 202 |
+
return True
|
| 203 |
+
|
| 204 |
+
def remove_stable_workers_by_expiration(self):
|
| 205 |
+
expire = time.time() - CONTROLLER_HEART_BEAT_EXPIRATION
|
| 206 |
+
to_delete = []
|
| 207 |
+
for worker_name, w_info in self.worker_info.items():
|
| 208 |
+
if w_info.check_heart_beat and w_info.last_heart_beat < expire:
|
| 209 |
+
to_delete.append(worker_name)
|
| 210 |
+
|
| 211 |
+
for worker_name in to_delete:
|
| 212 |
+
self.remove_worker(worker_name)
|
| 213 |
+
|
| 214 |
+
def worker_api_generate_stream(self, params):
|
| 215 |
+
worker_addr = self.get_worker_address(params["model"])
|
| 216 |
+
if not worker_addr:
|
| 217 |
+
logger.info(f"no worker: {params['model']}")
|
| 218 |
+
ret = {
|
| 219 |
+
"text": server_error_msg,
|
| 220 |
+
"error_code": 2,
|
| 221 |
+
}
|
| 222 |
+
yield json.dumps(ret).encode() + b"\0"
|
| 223 |
+
|
| 224 |
+
try:
|
| 225 |
+
response = requests.post(
|
| 226 |
+
worker_addr + "/worker_generate_stream",
|
| 227 |
+
json=params,
|
| 228 |
+
stream=True,
|
| 229 |
+
timeout=5,
|
| 230 |
+
)
|
| 231 |
+
for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"):
|
| 232 |
+
if chunk:
|
| 233 |
+
yield chunk + b"\0"
|
| 234 |
+
except requests.exceptions.RequestException as e:
|
| 235 |
+
logger.info(f"worker timeout: {worker_addr}")
|
| 236 |
+
ret = {
|
| 237 |
+
"text": server_error_msg,
|
| 238 |
+
"error_code": 3,
|
| 239 |
+
}
|
| 240 |
+
yield json.dumps(ret).encode() + b"\0"
|
| 241 |
+
|
| 242 |
+
# Let the controller act as a worker to achieve hierarchical
|
| 243 |
+
# management. This can be used to connect isolated sub networks.
|
| 244 |
+
def worker_api_get_status(self):
|
| 245 |
+
model_names = set()
|
| 246 |
+
speed = 0
|
| 247 |
+
queue_length = 0
|
| 248 |
+
|
| 249 |
+
for w_name in self.worker_info:
|
| 250 |
+
worker_status = self.get_worker_status(w_name)
|
| 251 |
+
if worker_status is not None:
|
| 252 |
+
model_names.update(worker_status["model_names"])
|
| 253 |
+
speed += worker_status["speed"]
|
| 254 |
+
queue_length += worker_status["queue_length"]
|
| 255 |
+
|
| 256 |
+
return {
|
| 257 |
+
"model_names": list(model_names),
|
| 258 |
+
"speed": speed,
|
| 259 |
+
"queue_length": queue_length,
|
| 260 |
+
}
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
app = FastAPI()
|
| 264 |
+
|
| 265 |
+
|
| 266 |
+
@app.post("/register_worker")
|
| 267 |
+
async def register_worker(request: Request):
|
| 268 |
+
data = await request.json()
|
| 269 |
+
controller.register_worker(
|
| 270 |
+
data["worker_name"], data["check_heart_beat"], data.get("worker_status", None)
|
| 271 |
+
)
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
@app.post("/refresh_all_workers")
|
| 275 |
+
async def refresh_all_workers():
|
| 276 |
+
models = controller.refresh_all_workers()
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
@app.post("/list_models")
|
| 280 |
+
async def list_models():
|
| 281 |
+
models = controller.list_models()
|
| 282 |
+
return {"models": models}
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
@app.post("/get_worker_address")
|
| 286 |
+
async def get_worker_address(request: Request):
|
| 287 |
+
data = await request.json()
|
| 288 |
+
addr = controller.get_worker_address(data["model"])
|
| 289 |
+
return {"address": addr}
|
| 290 |
+
|
| 291 |
+
|
| 292 |
+
@app.post("/receive_heart_beat")
|
| 293 |
+
async def receive_heart_beat(request: Request):
|
| 294 |
+
data = await request.json()
|
| 295 |
+
exist = controller.receive_heart_beat(data["worker_name"], data["queue_length"])
|
| 296 |
+
return {"exist": exist}
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
@app.post("/worker_generate_stream")
|
| 300 |
+
async def worker_api_generate_stream(request: Request):
|
| 301 |
+
params = await request.json()
|
| 302 |
+
generator = controller.worker_api_generate_stream(params)
|
| 303 |
+
return StreamingResponse(generator)
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
@app.post("/worker_get_status")
|
| 307 |
+
async def worker_api_get_status(request: Request):
|
| 308 |
+
return controller.worker_api_get_status()
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
if __name__ == "__main__":
|
| 312 |
+
parser = argparse.ArgumentParser()
|
| 313 |
+
parser.add_argument("--host", type=str, default="localhost")
|
| 314 |
+
parser.add_argument("--port", type=int, default=21001)
|
| 315 |
+
parser.add_argument(
|
| 316 |
+
"--dispatch-method",
|
| 317 |
+
type=str,
|
| 318 |
+
choices=["lottery", "shortest_queue"],
|
| 319 |
+
default="shortest_queue",
|
| 320 |
+
)
|
| 321 |
+
args = parser.parse_args()
|
| 322 |
+
logger.info(f"args: {args}")
|
| 323 |
+
|
| 324 |
+
controller = Controller(args.dispatch_method)
|
| 325 |
+
uvicorn.run(app, host=args.host, port=args.port, log_level="info")
|
llm-awq/tinychat/serve/examples/CPR.jpg
ADDED
|
llm-awq/tinychat/serve/examples/icl-logo/adobe.jpg
ADDED
|
llm-awq/tinychat/serve/examples/icl-logo/apple.jpg
ADDED
|
llm-awq/tinychat/serve/examples/icl-logo/google.webp
ADDED
|
llm-awq/tinychat/serve/examples/icl-logo/nvidia.png
ADDED
|
llm-awq/tinychat/serve/gradio_web_server.py
ADDED
|
@@ -0,0 +1,1200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import argparse
|
| 17 |
+
import datetime
|
| 18 |
+
import json
|
| 19 |
+
import os
|
| 20 |
+
import time
|
| 21 |
+
|
| 22 |
+
import gradio as gr
|
| 23 |
+
import requests
|
| 24 |
+
|
| 25 |
+
LOGDIR = "."
|
| 26 |
+
|
| 27 |
+
from tinychat.serve.llava_conv import (
|
| 28 |
+
default_conversation,
|
| 29 |
+
conv_templates,
|
| 30 |
+
get_conversation,
|
| 31 |
+
SeparatorStyle,
|
| 32 |
+
)
|
| 33 |
+
from tinychat.utils.log_utils import (
|
| 34 |
+
build_logger,
|
| 35 |
+
server_error_msg,
|
| 36 |
+
violates_moderation,
|
| 37 |
+
moderation_msg,
|
| 38 |
+
)
|
| 39 |
+
import hashlib
|
| 40 |
+
|
| 41 |
+
IMAGE_BOX_NUM = 3
|
| 42 |
+
BUTTON_LIST_LEN = 2
|
| 43 |
+
|
| 44 |
+
logger = build_logger("gradio_web_server", "gradio_web_server.log")
|
| 45 |
+
|
| 46 |
+
headers = {"User-Agent": "TinyChat AWQ Chatbot"}
|
| 47 |
+
|
| 48 |
+
no_change_btn = gr.Button.update()
|
| 49 |
+
enable_btn = gr.Button.update(interactive=True)
|
| 50 |
+
disable_btn = gr.Button.update(interactive=False)
|
| 51 |
+
|
| 52 |
+
from tinychat.utils.constants import (
|
| 53 |
+
LLAVA_DEFAULT_IMAGE_TOKEN,
|
| 54 |
+
LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER,
|
| 55 |
+
AUTO_FILL_IM_TOKEN_HOLDER,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
# IMAGE_TOKEN_VIS = "**[IMAGE]**"
|
| 59 |
+
IMAGE_TOKEN_VIS = "**\<image\>**"
|
| 60 |
+
|
| 61 |
+
priority = {
|
| 62 |
+
"vicuna-13b": "aaaaaaa",
|
| 63 |
+
"koala-13b": "aaaaaab",
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def get_conv_log_filename():
|
| 68 |
+
t = datetime.datetime.now()
|
| 69 |
+
name = os.path.join(LOGDIR, f"{t.year}-{t.month:02d}-{t.day:02d}-conv.json")
|
| 70 |
+
return name
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def get_model_list():
|
| 74 |
+
ret = requests.post(args.controller_url + "/refresh_all_workers")
|
| 75 |
+
assert ret.status_code == 200
|
| 76 |
+
ret = requests.post(args.controller_url + "/list_models")
|
| 77 |
+
models = ret.json()["models"]
|
| 78 |
+
models.sort(key=lambda x: priority.get(x, x))
|
| 79 |
+
logger.info(f"Models: {models}")
|
| 80 |
+
return models
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
get_window_url_params = """
|
| 84 |
+
function() {
|
| 85 |
+
const params = new URLSearchParams(window.location.search);
|
| 86 |
+
url_params = Object.fromEntries(params);
|
| 87 |
+
console.log(url_params);
|
| 88 |
+
return url_params;
|
| 89 |
+
}
|
| 90 |
+
"""
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def load_demo(url_params, prompt_style_btn, request: gr.Request):
|
| 94 |
+
logger.info(f"load_demo. ip: {request.client.host}. params: {url_params}")
|
| 95 |
+
|
| 96 |
+
dropdown_update = gr.Dropdown.update(visible=True)
|
| 97 |
+
if "model" in url_params:
|
| 98 |
+
model = url_params["model"]
|
| 99 |
+
if model in models:
|
| 100 |
+
dropdown_update = gr.Dropdown.update(value=model, visible=True)
|
| 101 |
+
state = get_conversation(prompt_style_btn)
|
| 102 |
+
# state = default_conversation.copy()
|
| 103 |
+
return state, dropdown_update
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def load_demo_refresh_model_list(prompt_style_btn, request: gr.Request):
|
| 107 |
+
logger.info(f"load_demo. ip: {request.client.host}")
|
| 108 |
+
models = get_model_list()
|
| 109 |
+
state = get_conversation(prompt_style_btn)
|
| 110 |
+
# state = default_conversation.copy()
|
| 111 |
+
dropdown_update = gr.Dropdown.update(
|
| 112 |
+
choices=models, value=models[0] if len(models) > 0 else ""
|
| 113 |
+
)
|
| 114 |
+
return state, dropdown_update
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
# def vote_last_response(state, vote_type, model_selector, request: gr.Request):
|
| 118 |
+
# with open(get_conv_log_filename(), "a") as fout:
|
| 119 |
+
# data = {
|
| 120 |
+
# "tstamp": round(time.time(), 4),
|
| 121 |
+
# "type": vote_type,
|
| 122 |
+
# "model": model_selector,
|
| 123 |
+
# "state": state.dict(),
|
| 124 |
+
# "ip": request.client.host,
|
| 125 |
+
# }
|
| 126 |
+
# fout.write(json.dumps(data) + "\n")
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
# def upvote_last_response(state, model_selector, request: gr.Request):
|
| 130 |
+
# logger.info(f"upvote. ip: {request.client.host}")
|
| 131 |
+
# vote_last_response(state, "upvote", model_selector, request)
|
| 132 |
+
# return ("",) + (disable_btn,) * 3
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
# def downvote_last_response(state, model_selector, request: gr.Request):
|
| 136 |
+
# logger.info(f"downvote. ip: {request.client.host}")
|
| 137 |
+
# vote_last_response(state, "downvote", model_selector, request)
|
| 138 |
+
# return ("",) + (disable_btn,) * 3
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
# def flag_last_response(state, model_selector, request: gr.Request):
|
| 142 |
+
# logger.info(f"flag. ip: {request.client.host}")
|
| 143 |
+
# vote_last_response(state, "flag", model_selector, request)
|
| 144 |
+
# return ("",) + (disable_btn,) * 3
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def regenerate(state, image_process_mode, request: gr.Request):
|
| 148 |
+
logger.info(f"regenerate. ip: {request.client.host}")
|
| 149 |
+
state.messages[-1][-1] = None
|
| 150 |
+
prev_human_msg = state.messages[-2]
|
| 151 |
+
if type(prev_human_msg[1]) in (tuple, list):
|
| 152 |
+
prev_human_msg[1] = (*prev_human_msg[1][:2], image_process_mode)
|
| 153 |
+
state.skip_next = False
|
| 154 |
+
return (state, state.to_gradio_chatbot(), "") + (disable_btn,) * BUTTON_LIST_LEN
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def change_prompt_style(state, prompt_style_btn, request: gr.Request):
|
| 158 |
+
if state.version != prompt_style_btn:
|
| 159 |
+
state = get_conversation(prompt_style_btn)
|
| 160 |
+
return state
|
| 161 |
+
|
| 162 |
+
|
| 163 |
+
def clear_history(prompt_style_btn, request: gr.Request):
|
| 164 |
+
logger.info(f"clear_history. ip: {request.client.host}")
|
| 165 |
+
state = get_conversation(prompt_style_btn)
|
| 166 |
+
return (
|
| 167 |
+
(state, state.to_gradio_chatbot(), "")
|
| 168 |
+
+ (None,) * IMAGE_BOX_NUM
|
| 169 |
+
+ (None,) # Videobox
|
| 170 |
+
+ (disable_btn,) * BUTTON_LIST_LEN
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def clear_text_history(state, prompt_style_btn, request: gr.Request):
|
| 175 |
+
state = get_conversation(prompt_style_btn)
|
| 176 |
+
return (state, state.to_gradio_chatbot())
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def clear_after_click_example_1_video(videobox, textbox):
|
| 180 |
+
imagebox = None
|
| 181 |
+
imagebox_2 = None
|
| 182 |
+
imagebox_3 = None
|
| 183 |
+
state = get_conversation("default")
|
| 184 |
+
prompt_style_btn = "default"
|
| 185 |
+
return (state, imagebox, imagebox_2, imagebox_3, videobox, prompt_style_btn)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def clear_after_click_example_1_image(imagebox, textbox):
|
| 189 |
+
imagebox_2 = None
|
| 190 |
+
imagebox_3 = None
|
| 191 |
+
videobox = None
|
| 192 |
+
state = get_conversation("default")
|
| 193 |
+
prompt_style_btn = "default"
|
| 194 |
+
return (state, imagebox, imagebox_2, imagebox_3, videobox, prompt_style_btn)
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def clear_after_click_example_2_image(imagebox, imagebox_2, textbox):
|
| 198 |
+
imagebox_3 = None
|
| 199 |
+
videobox = None
|
| 200 |
+
state = get_conversation("default")
|
| 201 |
+
prompt_style_btn = "default"
|
| 202 |
+
return (state, imagebox, imagebox_2, imagebox_3, videobox, prompt_style_btn)
|
| 203 |
+
|
| 204 |
+
|
| 205 |
+
def clear_after_click_example_3_image(imagebox, imagebox_2, imagebox_3, textbox):
|
| 206 |
+
videobox = None
|
| 207 |
+
state = get_conversation("default")
|
| 208 |
+
prompt_style_btn = "default"
|
| 209 |
+
return (state, imagebox, imagebox_2, imagebox_3, videobox, prompt_style_btn)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def clear_after_click_example_3_image_icl(imagebox, imagebox_2, imagebox_3, textbox):
|
| 213 |
+
videobox = None
|
| 214 |
+
state = get_conversation("no-sys")
|
| 215 |
+
prompt_style_btn = "no-sys"
|
| 216 |
+
return (state, imagebox, imagebox_2, imagebox_3, videobox, prompt_style_btn)
|
| 217 |
+
|
| 218 |
+
|
| 219 |
+
def add_images(
|
| 220 |
+
state,
|
| 221 |
+
imagebox,
|
| 222 |
+
imagebox_2,
|
| 223 |
+
imagebox_3,
|
| 224 |
+
videobox,
|
| 225 |
+
image_process_mode,
|
| 226 |
+
request: gr.Request,
|
| 227 |
+
):
|
| 228 |
+
if state.image_loaded:
|
| 229 |
+
# return (state,) + (None,) * IMAGE_BOX_NUM
|
| 230 |
+
return state
|
| 231 |
+
|
| 232 |
+
def extract_frames(video_path):
|
| 233 |
+
import cv2
|
| 234 |
+
from PIL import Image
|
| 235 |
+
|
| 236 |
+
vidcap = cv2.VideoCapture(video_path)
|
| 237 |
+
fps = vidcap.get(cv2.CAP_PROP_FPS)
|
| 238 |
+
frame_count = int(vidcap.get(cv2.CAP_PROP_FRAME_COUNT))
|
| 239 |
+
duration = frame_count / fps
|
| 240 |
+
|
| 241 |
+
frame_interval = frame_count // 8
|
| 242 |
+
print(
|
| 243 |
+
"duration:", duration, "frames:", frame_count, "intervals:", frame_interval
|
| 244 |
+
)
|
| 245 |
+
# frame_interval = 10
|
| 246 |
+
|
| 247 |
+
def get_frame(max_frames):
|
| 248 |
+
# frame_id = int(fps * stamp)
|
| 249 |
+
# vidcap.set(cv2.CAP_PROP_POS_FRAMES, frame_id)
|
| 250 |
+
# ret, frame = vidcap.read()
|
| 251 |
+
images = []
|
| 252 |
+
count = 0
|
| 253 |
+
success = True
|
| 254 |
+
while success:
|
| 255 |
+
success, frame = vidcap.read()
|
| 256 |
+
if count % frame_interval == 0:
|
| 257 |
+
img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 258 |
+
im_pil = Image.fromarray(img)
|
| 259 |
+
images.append(im_pil)
|
| 260 |
+
if len(images) == max_frames:
|
| 261 |
+
return images
|
| 262 |
+
|
| 263 |
+
count += 1
|
| 264 |
+
# assert ret, "videocap.read fails!"
|
| 265 |
+
# img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
|
| 266 |
+
# im_pil = Image.fromarray(img)
|
| 267 |
+
# print(f"loading {stamp} success")
|
| 268 |
+
return images
|
| 269 |
+
|
| 270 |
+
# return [get_frame(0), get_frame(stamp1), get_frame(stamp2)]
|
| 271 |
+
# img = get_frame(0)
|
| 272 |
+
# img1 = get_frame(frame_interval * 1)
|
| 273 |
+
# return [img, img1, img, img1, img, img1,]
|
| 274 |
+
return get_frame(8)
|
| 275 |
+
|
| 276 |
+
frames = [
|
| 277 |
+
None,
|
| 278 |
+
]
|
| 279 |
+
if videobox is not None:
|
| 280 |
+
frames = extract_frames(videobox)
|
| 281 |
+
# add frames as regular images
|
| 282 |
+
logger.info(f"Got videobox: {videobox}.")
|
| 283 |
+
|
| 284 |
+
logger.info(f"add_image. ip: {request.client.host}.")
|
| 285 |
+
image_list = [imagebox, imagebox_2, imagebox_3, *frames]
|
| 286 |
+
logger.info(f"image_list: {image_list}")
|
| 287 |
+
|
| 288 |
+
im_count = 0
|
| 289 |
+
for image in image_list:
|
| 290 |
+
if image is not None:
|
| 291 |
+
im_count += 1
|
| 292 |
+
for image in image_list:
|
| 293 |
+
if image is not None:
|
| 294 |
+
if args.auto_pad_image_token or im_count == 1:
|
| 295 |
+
text = (AUTO_FILL_IM_TOKEN_HOLDER, image, image_process_mode)
|
| 296 |
+
else:
|
| 297 |
+
text = ("", image, image_process_mode)
|
| 298 |
+
state.append_message(None, text)
|
| 299 |
+
state.append_message(
|
| 300 |
+
None, None
|
| 301 |
+
) # in order to match the input-output pair for textbox outputs
|
| 302 |
+
# state.append_message(state.roles[0], text)
|
| 303 |
+
# state.append_message(state.roles[1], None)
|
| 304 |
+
# state.skip_next = False
|
| 305 |
+
logger.info(f"im_count {im_count}. ip: {request.client.host}.")
|
| 306 |
+
state.image_loaded = True
|
| 307 |
+
# return (state,) + (None,) * IMAGE_BOX_NUM
|
| 308 |
+
return state
|
| 309 |
+
|
| 310 |
+
|
| 311 |
+
def add_text_only(state, text, request: gr.Request):
|
| 312 |
+
logger.info(f"add_text_only. ip: {request.client.host}. len: {len(text)}")
|
| 313 |
+
|
| 314 |
+
if args.moderate:
|
| 315 |
+
flagged = violates_moderation(text)
|
| 316 |
+
if flagged:
|
| 317 |
+
state.skip_next = True
|
| 318 |
+
return (state, moderation_msg) + (no_change_btn,) * BUTTON_LIST_LEN
|
| 319 |
+
|
| 320 |
+
# This is 1536 characters, rather than tokens
|
| 321 |
+
text = text[:1536] # Hard cut-off
|
| 322 |
+
state.append_message(state.roles[0], text)
|
| 323 |
+
state.append_message(state.roles[1], None)
|
| 324 |
+
state.skip_next = False
|
| 325 |
+
return (state, "") + (disable_btn,) * BUTTON_LIST_LEN
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
def add_text(
|
| 329 |
+
state, text, image, image_process_mode, prompt_style_btn, request: gr.Request
|
| 330 |
+
):
|
| 331 |
+
logger.info(f"add_text. ip: {request.client.host}. len: {len(text)}")
|
| 332 |
+
if len(text) <= 0 and image is None:
|
| 333 |
+
state.skip_next = True
|
| 334 |
+
return (state, state.to_gradio_chatbot(), "", None) + (
|
| 335 |
+
no_change_btn,
|
| 336 |
+
) * BUTTON_LIST_LEN
|
| 337 |
+
if args.moderate:
|
| 338 |
+
flagged = violates_moderation(text)
|
| 339 |
+
if flagged:
|
| 340 |
+
state.skip_next = True
|
| 341 |
+
return (state, state.to_gradio_chatbot(), moderation_msg, None) + (
|
| 342 |
+
no_change_btn,
|
| 343 |
+
) * BUTTON_LIST_LEN
|
| 344 |
+
|
| 345 |
+
text = text[:1536] # Hard cut-off
|
| 346 |
+
if image is not None:
|
| 347 |
+
text = text[:1200] # Hard cut-off for images
|
| 348 |
+
if "<image>" not in text:
|
| 349 |
+
# text = '<Image><image></Image>' + text
|
| 350 |
+
text = text + "\n<image>"
|
| 351 |
+
text = (text, image, image_process_mode)
|
| 352 |
+
if len(state.get_images(return_pil=True)) > 0:
|
| 353 |
+
state = get_conversation(prompt_style_btn)
|
| 354 |
+
# state = default_conversation.copy()
|
| 355 |
+
state.append_message(state.roles[0], text)
|
| 356 |
+
state.append_message(state.roles[1], None)
|
| 357 |
+
state.skip_next = False
|
| 358 |
+
return (state, state.to_gradio_chatbot(), "", None) + (
|
| 359 |
+
disable_btn,
|
| 360 |
+
) * BUTTON_LIST_LEN
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def http_bot(
|
| 364 |
+
state,
|
| 365 |
+
model_selector,
|
| 366 |
+
temperature,
|
| 367 |
+
top_p,
|
| 368 |
+
max_new_tokens,
|
| 369 |
+
prompt_style_btn,
|
| 370 |
+
request: gr.Request,
|
| 371 |
+
):
|
| 372 |
+
logger.info(f"http_bot. ip: {request.client.host}")
|
| 373 |
+
start_tstamp = time.time()
|
| 374 |
+
model_name = model_selector
|
| 375 |
+
|
| 376 |
+
if state.skip_next:
|
| 377 |
+
# This generate call is skipped due to invalid inputs
|
| 378 |
+
yield (state, state.to_gradio_chatbot()) + (no_change_btn,) * BUTTON_LIST_LEN
|
| 379 |
+
return
|
| 380 |
+
if len(state.messages) == state.offset + 2:
|
| 381 |
+
# First round of conversation
|
| 382 |
+
if "llava" in model_name.lower():
|
| 383 |
+
if "llama-2" in model_name.lower():
|
| 384 |
+
template_name = "llava_llama_2"
|
| 385 |
+
elif "v1" in model_name.lower():
|
| 386 |
+
if "mmtag" in model_name.lower():
|
| 387 |
+
template_name = "v1_mmtag"
|
| 388 |
+
elif (
|
| 389 |
+
"plain" in model_name.lower()
|
| 390 |
+
and "finetune" not in model_name.lower()
|
| 391 |
+
):
|
| 392 |
+
template_name = "v1_mmtag"
|
| 393 |
+
else:
|
| 394 |
+
template_name = "llava_v1"
|
| 395 |
+
elif "mpt" in model_name.lower():
|
| 396 |
+
template_name = "mpt"
|
| 397 |
+
else:
|
| 398 |
+
if "mmtag" in model_name.lower():
|
| 399 |
+
template_name = "v0_mmtag"
|
| 400 |
+
elif (
|
| 401 |
+
"plain" in model_name.lower()
|
| 402 |
+
and "finetune" not in model_name.lower()
|
| 403 |
+
):
|
| 404 |
+
template_name = "v0_mmtag"
|
| 405 |
+
else:
|
| 406 |
+
template_name = "llava_v0"
|
| 407 |
+
elif "mpt" in model_name:
|
| 408 |
+
template_name = "mpt_text"
|
| 409 |
+
elif "llama-2" in model_name:
|
| 410 |
+
template_name = "llama_2"
|
| 411 |
+
else:
|
| 412 |
+
template_name = "vicuna_v1"
|
| 413 |
+
if prompt_style_btn == "no-sys":
|
| 414 |
+
new_state = get_conversation(prompt_style_btn)
|
| 415 |
+
else:
|
| 416 |
+
new_state = conv_templates[template_name].copy()
|
| 417 |
+
new_state.append_message(new_state.roles[0], state.messages[-2][1])
|
| 418 |
+
new_state.append_message(new_state.roles[1], None)
|
| 419 |
+
state = new_state
|
| 420 |
+
|
| 421 |
+
# Query worker address
|
| 422 |
+
controller_url = args.controller_url
|
| 423 |
+
ret = requests.post(
|
| 424 |
+
controller_url + "/get_worker_address", json={"model": model_name}
|
| 425 |
+
)
|
| 426 |
+
worker_addr = ret.json()["address"]
|
| 427 |
+
logger.info(f"model_name: {model_name}, worker_addr: {worker_addr}")
|
| 428 |
+
|
| 429 |
+
# No available worker
|
| 430 |
+
if worker_addr == "":
|
| 431 |
+
state.messages[-1][-1] = server_error_msg
|
| 432 |
+
yield (
|
| 433 |
+
state,
|
| 434 |
+
state.to_gradio_chatbot(),
|
| 435 |
+
# disable_btn,
|
| 436 |
+
# disable_btn,
|
| 437 |
+
# disable_btn,
|
| 438 |
+
enable_btn,
|
| 439 |
+
enable_btn,
|
| 440 |
+
)
|
| 441 |
+
return
|
| 442 |
+
|
| 443 |
+
# Construct prompt
|
| 444 |
+
prompt = state.get_prompt()
|
| 445 |
+
|
| 446 |
+
all_images = state.get_images(return_pil=True)
|
| 447 |
+
all_image_hash = [hashlib.md5(image.tobytes()).hexdigest() for image in all_images]
|
| 448 |
+
for image, hash in zip(all_images, all_image_hash):
|
| 449 |
+
t = datetime.datetime.now()
|
| 450 |
+
filename = os.path.join(
|
| 451 |
+
LOGDIR, "serve_images", f"{t.year}-{t.month:02d}-{t.day:02d}", f"{hash}.jpg"
|
| 452 |
+
)
|
| 453 |
+
if not os.path.isfile(filename):
|
| 454 |
+
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
| 455 |
+
image.save(filename)
|
| 456 |
+
|
| 457 |
+
# Make requests
|
| 458 |
+
pload = {
|
| 459 |
+
"model": model_name,
|
| 460 |
+
"prompt": prompt,
|
| 461 |
+
"temperature": float(temperature),
|
| 462 |
+
"top_p": float(top_p),
|
| 463 |
+
"max_new_tokens": min(int(max_new_tokens), 1536),
|
| 464 |
+
"stop": (
|
| 465 |
+
state.sep
|
| 466 |
+
if state.sep_style in [SeparatorStyle.SINGLE, SeparatorStyle.MPT]
|
| 467 |
+
else state.sep2
|
| 468 |
+
),
|
| 469 |
+
"images": f"List of {len(state.get_images())} images: {all_image_hash}",
|
| 470 |
+
}
|
| 471 |
+
|
| 472 |
+
image_num = len(state.get_images())
|
| 473 |
+
if image_num == 0:
|
| 474 |
+
state.messages[-1][
|
| 475 |
+
-1
|
| 476 |
+
] = "**NO INPUT IMAGE RECEIVED BY THE SERVER. PLEASE CHECK YOUR INTERNET CONNECTION AND REFRESH THE PAGE.**"
|
| 477 |
+
yield (
|
| 478 |
+
state,
|
| 479 |
+
state.to_gradio_chatbot(),
|
| 480 |
+
# disable_btn,
|
| 481 |
+
# disable_btn,
|
| 482 |
+
# disable_btn,
|
| 483 |
+
enable_btn,
|
| 484 |
+
enable_btn,
|
| 485 |
+
)
|
| 486 |
+
return
|
| 487 |
+
|
| 488 |
+
count_auto_im_token = prompt.count(AUTO_FILL_IM_TOKEN_HOLDER)
|
| 489 |
+
count_manual_im_token = prompt.count(LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER)
|
| 490 |
+
if (count_auto_im_token == image_num) and (
|
| 491 |
+
count_manual_im_token == 0
|
| 492 |
+
): # Use default system prompt
|
| 493 |
+
prompt = prompt.replace(AUTO_FILL_IM_TOKEN_HOLDER, LLAVA_DEFAULT_IMAGE_TOKEN)
|
| 494 |
+
elif (count_auto_im_token == image_num) and (
|
| 495 |
+
count_manual_im_token == image_num
|
| 496 |
+
): # Use <image> token inserted by user
|
| 497 |
+
prompt = prompt.replace(AUTO_FILL_IM_TOKEN_HOLDER, "")
|
| 498 |
+
prompt = prompt.replace(
|
| 499 |
+
LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, LLAVA_DEFAULT_IMAGE_TOKEN
|
| 500 |
+
)
|
| 501 |
+
elif (count_auto_im_token == 0) and (count_manual_im_token == image_num):
|
| 502 |
+
prompt = prompt.replace(
|
| 503 |
+
LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, LLAVA_DEFAULT_IMAGE_TOKEN
|
| 504 |
+
)
|
| 505 |
+
else:
|
| 506 |
+
state.messages[-1][
|
| 507 |
+
-1
|
| 508 |
+
] = "**IMAGE NUM MISMATCHES IMAGE TOKEN PLACEHOLDER. PLEASE CHECK YOUR INPUT AND REFRESH THE PAGE.**"
|
| 509 |
+
yield (
|
| 510 |
+
state,
|
| 511 |
+
state.to_gradio_chatbot(),
|
| 512 |
+
# disable_btn,
|
| 513 |
+
# disable_btn,
|
| 514 |
+
# disable_btn,
|
| 515 |
+
enable_btn,
|
| 516 |
+
enable_btn,
|
| 517 |
+
)
|
| 518 |
+
return
|
| 519 |
+
|
| 520 |
+
pload["prompt"] = prompt
|
| 521 |
+
logger.info(f"==== request ====\n{pload}")
|
| 522 |
+
pload["images"] = state.get_images()
|
| 523 |
+
|
| 524 |
+
state.messages[-1][-1] = "▌"
|
| 525 |
+
ret = state.to_gradio_chatbot()
|
| 526 |
+
ret[0][0] = ret[0][0].replace(LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, IMAGE_TOKEN_VIS)
|
| 527 |
+
yield (state, ret) + (disable_btn,) * BUTTON_LIST_LEN
|
| 528 |
+
|
| 529 |
+
try:
|
| 530 |
+
# Stream output
|
| 531 |
+
response = requests.post(
|
| 532 |
+
worker_addr + "/worker_generate_stream",
|
| 533 |
+
headers=headers,
|
| 534 |
+
json=pload,
|
| 535 |
+
stream=True,
|
| 536 |
+
timeout=10,
|
| 537 |
+
)
|
| 538 |
+
for chunk in response.iter_lines(decode_unicode=False, delimiter=b"\0"):
|
| 539 |
+
if chunk:
|
| 540 |
+
data = json.loads(chunk.decode())
|
| 541 |
+
if data["error_code"] == 0:
|
| 542 |
+
output = data["text"][len(prompt) :].strip()
|
| 543 |
+
state.messages[-1][-1] = output + "▌"
|
| 544 |
+
ret = state.to_gradio_chatbot()
|
| 545 |
+
ret[0][0] = ret[0][0].replace(
|
| 546 |
+
LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, IMAGE_TOKEN_VIS
|
| 547 |
+
)
|
| 548 |
+
yield (state, ret) + (disable_btn,) * BUTTON_LIST_LEN
|
| 549 |
+
else:
|
| 550 |
+
output = data["text"] + f" (error_code: {data['error_code']})"
|
| 551 |
+
state.messages[-1][-1] = output
|
| 552 |
+
ret = state.to_gradio_chatbot()
|
| 553 |
+
ret[0][0] = ret[0][0].replace(
|
| 554 |
+
LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, IMAGE_TOKEN_VIS
|
| 555 |
+
)
|
| 556 |
+
yield (state, ret) + (
|
| 557 |
+
# disable_btn,
|
| 558 |
+
# disable_btn,
|
| 559 |
+
# disable_btn,
|
| 560 |
+
enable_btn,
|
| 561 |
+
enable_btn,
|
| 562 |
+
)
|
| 563 |
+
return
|
| 564 |
+
time.sleep(0.03)
|
| 565 |
+
except requests.exceptions.RequestException as e:
|
| 566 |
+
state.messages[-1][-1] = server_error_msg
|
| 567 |
+
yield (state, state.to_gradio_chatbot()) + (
|
| 568 |
+
# disable_btn,
|
| 569 |
+
# disable_btn,
|
| 570 |
+
# disable_btn,
|
| 571 |
+
enable_btn,
|
| 572 |
+
enable_btn,
|
| 573 |
+
)
|
| 574 |
+
return
|
| 575 |
+
|
| 576 |
+
state.messages[-1][-1] = state.messages[-1][-1][:-1]
|
| 577 |
+
ret = state.to_gradio_chatbot()
|
| 578 |
+
ret[0][0] = ret[0][0].replace(LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER, IMAGE_TOKEN_VIS)
|
| 579 |
+
yield (state, ret) + (enable_btn,) * BUTTON_LIST_LEN
|
| 580 |
+
|
| 581 |
+
finish_tstamp = time.time()
|
| 582 |
+
logger.info(f"{output}")
|
| 583 |
+
|
| 584 |
+
with open(get_conv_log_filename(), "a") as fout:
|
| 585 |
+
data = {
|
| 586 |
+
"tstamp": round(finish_tstamp, 4),
|
| 587 |
+
"type": "chat",
|
| 588 |
+
"model": model_name,
|
| 589 |
+
"start": round(start_tstamp, 4),
|
| 590 |
+
"finish": round(finish_tstamp, 4),
|
| 591 |
+
"state": state.dict(),
|
| 592 |
+
"images": all_image_hash,
|
| 593 |
+
"ip": request.client.host,
|
| 594 |
+
}
|
| 595 |
+
fout.write(json.dumps(data) + "\n")
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
title_markdown = """
|
| 599 |
+
# VILA: On Pre-training for Visual Language Models
|
| 600 |
+
[\[Paper\]](https://arxiv.org/abs/2312.07533) [\[Github\]](https://github.com/NVlabs/VILA)
|
| 601 |
+
### Powered by [TinyChat](https://github.com/mit-han-lab/llm-awq/tree/main/tinychat) with 4-bit [AWQ](https://arxiv.org/abs/2306.00978).
|
| 602 |
+
"""
|
| 603 |
+
|
| 604 |
+
tos_markdown = """
|
| 605 |
+
### Terms of Use
|
| 606 |
+
By using this service, users are required to agree to the following terms:
|
| 607 |
+
The service is a research preview intended for non-commercial use only. It only provides limited safety measures and may generate offensive content. It must not be used for any illegal, harmful, violent, racist, or sexual purposes. The service may collect user dialogue data for future research.
|
| 608 |
+
Please click the "Flag" button if you get any inappropriate answer! We will collect those to keep improving our moderator.
|
| 609 |
+
For an optimal experience, please use desktop computers for this demo, as mobile devices may compromise its quality.
|
| 610 |
+
"""
|
| 611 |
+
|
| 612 |
+
|
| 613 |
+
learn_more_markdown = """
|
| 614 |
+
### License
|
| 615 |
+
The service is a research preview intended for non-commercial use only, subject to the model [License](https://github.com/facebookresearch/llama/blob/main/MODEL_CARD.md) of LLaMA, [Terms of Use](https://openai.com/policies/terms-of-use) of the data generated by OpenAI, and [Privacy Practices](https://chrome.google.com/webstore/detail/sharegpt-share-your-chatg/daiacboceoaocpibfodeljbdfacokfjb) of ShareGPT. Please contact us if you find any potential violation.
|
| 616 |
+
"""
|
| 617 |
+
|
| 618 |
+
ack_markdown = """
|
| 619 |
+
### Acknowledgement
|
| 620 |
+
This demo is inspired by [LLaVA](https://github.com/haotian-liu/LLaVA). We thank LLaVA for providing an elegant way to build the Gradio Web UI.
|
| 621 |
+
"""
|
| 622 |
+
|
| 623 |
+
block_css = """
|
| 624 |
+
|
| 625 |
+
#buttons button {
|
| 626 |
+
min-width: min(120px,100%);
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
"""
|
| 630 |
+
|
| 631 |
+
|
| 632 |
+
def build_demo(embed_mode):
|
| 633 |
+
textbox = gr.Textbox(
|
| 634 |
+
show_label=False, placeholder="Enter text and press ENTER", container=False
|
| 635 |
+
)
|
| 636 |
+
with gr.Blocks(
|
| 637 |
+
title="VILA on TinyChat", theme=gr.themes.Default(), css=block_css
|
| 638 |
+
) as demo:
|
| 639 |
+
state = gr.State()
|
| 640 |
+
|
| 641 |
+
if not embed_mode:
|
| 642 |
+
gr.Markdown(title_markdown)
|
| 643 |
+
|
| 644 |
+
with gr.Row():
|
| 645 |
+
with gr.Column(scale=8):
|
| 646 |
+
with gr.Row():
|
| 647 |
+
imagebox = gr.Image(type="pil")
|
| 648 |
+
imagebox_2 = gr.Image(type="pil")
|
| 649 |
+
imagebox_3 = gr.Image(type="pil")
|
| 650 |
+
videobox = gr.Video(label="1 video = 8 frames")
|
| 651 |
+
image_process_mode = gr.Radio(
|
| 652 |
+
["Crop", "Resize", "Pad", "Default"],
|
| 653 |
+
value="Default",
|
| 654 |
+
label="Preprocess for non-square image",
|
| 655 |
+
visible=False,
|
| 656 |
+
)
|
| 657 |
+
# imagebox_out = gr.Image(height=150)
|
| 658 |
+
with gr.Row():
|
| 659 |
+
with gr.Column(scale=5):
|
| 660 |
+
textbox.render()
|
| 661 |
+
with gr.Column(scale=1, min_width=100):
|
| 662 |
+
submit_btn = gr.Button(value="Send", variant="primary")
|
| 663 |
+
with gr.Column(scale=1, min_width=100):
|
| 664 |
+
clear_btn = gr.Button(
|
| 665 |
+
value="🗑️ Clear", variant="primary", interactive=False
|
| 666 |
+
)
|
| 667 |
+
with gr.Column(scale=1, min_width=100):
|
| 668 |
+
regenerate_btn = gr.Button(
|
| 669 |
+
value="🔄 Retry", variant="primary", interactive=False
|
| 670 |
+
)
|
| 671 |
+
with gr.Row():
|
| 672 |
+
gr.Markdown(
|
| 673 |
+
"### *** Before changing the current images, uploading new images or switching the prompt style, please click the clear button."
|
| 674 |
+
)
|
| 675 |
+
chatbot = gr.Chatbot(
|
| 676 |
+
elem_id="chatbot", label="TinyChat Assistant", height=550
|
| 677 |
+
)
|
| 678 |
+
|
| 679 |
+
with gr.Column(scale=4):
|
| 680 |
+
with gr.Row(equal_height=True):
|
| 681 |
+
with gr.Column(scale=1, min_width=50):
|
| 682 |
+
model_selector = gr.Dropdown(
|
| 683 |
+
choices=models,
|
| 684 |
+
value=models[0] if len(models) > 0 else "",
|
| 685 |
+
label="Model",
|
| 686 |
+
interactive=True,
|
| 687 |
+
show_label=True,
|
| 688 |
+
container=False,
|
| 689 |
+
)
|
| 690 |
+
with gr.Column(scale=1, min_width=50):
|
| 691 |
+
prompt_style_btn = gr.Radio(
|
| 692 |
+
["default", "no-sys"],
|
| 693 |
+
label="Prompt style",
|
| 694 |
+
value="default",
|
| 695 |
+
interactive=True,
|
| 696 |
+
)
|
| 697 |
+
|
| 698 |
+
# with gr.Row():
|
| 699 |
+
# with gr.Column(scale=1, min_width=50):
|
| 700 |
+
# im_submit_btn = gr.Button(value="Add image", variant="primary")
|
| 701 |
+
# with gr.Column(scale=1, min_width=50):
|
| 702 |
+
# submit_btn_1 = gr.Button(value="Send", variant="primary")
|
| 703 |
+
|
| 704 |
+
cur_dir = os.path.dirname(os.path.abspath(__file__))
|
| 705 |
+
with gr.Row(equal_height=True):
|
| 706 |
+
gr.Examples(
|
| 707 |
+
examples=[
|
| 708 |
+
[
|
| 709 |
+
f"{cur_dir}/examples/video/qZDF__7LNKc.4.mp4",
|
| 710 |
+
"Elaborate on the visual and narrative elements of the video in detail.",
|
| 711 |
+
],
|
| 712 |
+
],
|
| 713 |
+
label="Video Example",
|
| 714 |
+
inputs=[videobox, textbox],
|
| 715 |
+
fn=clear_after_click_example_1_video,
|
| 716 |
+
outputs=[
|
| 717 |
+
state,
|
| 718 |
+
imagebox,
|
| 719 |
+
imagebox_2,
|
| 720 |
+
imagebox_3,
|
| 721 |
+
videobox,
|
| 722 |
+
prompt_style_btn,
|
| 723 |
+
],
|
| 724 |
+
run_on_click=True,
|
| 725 |
+
)
|
| 726 |
+
with gr.Row(equal_height=True):
|
| 727 |
+
with gr.Column(scale=1, min_width=50):
|
| 728 |
+
gr.Examples(
|
| 729 |
+
examples=[
|
| 730 |
+
[
|
| 731 |
+
f"{cur_dir}/examples/pedestrain.png",
|
| 732 |
+
"<image> What is the person in the center of the image doing?",
|
| 733 |
+
],
|
| 734 |
+
],
|
| 735 |
+
label="Image Example 1",
|
| 736 |
+
inputs=[imagebox, textbox],
|
| 737 |
+
fn=clear_after_click_example_1_image,
|
| 738 |
+
outputs=[
|
| 739 |
+
state,
|
| 740 |
+
imagebox,
|
| 741 |
+
imagebox_2,
|
| 742 |
+
imagebox_3,
|
| 743 |
+
videobox,
|
| 744 |
+
prompt_style_btn,
|
| 745 |
+
],
|
| 746 |
+
run_on_click=True,
|
| 747 |
+
)
|
| 748 |
+
with gr.Column(scale=1, min_width=50):
|
| 749 |
+
gr.Examples(
|
| 750 |
+
examples=[
|
| 751 |
+
[
|
| 752 |
+
f"{cur_dir}/examples/car_repair.png",
|
| 753 |
+
"<image> What is the brand of the silver car in the image?",
|
| 754 |
+
],
|
| 755 |
+
],
|
| 756 |
+
label="Image Example 2",
|
| 757 |
+
inputs=[imagebox, textbox],
|
| 758 |
+
fn=clear_after_click_example_1_image,
|
| 759 |
+
outputs=[
|
| 760 |
+
state,
|
| 761 |
+
imagebox,
|
| 762 |
+
imagebox_2,
|
| 763 |
+
imagebox_3,
|
| 764 |
+
videobox,
|
| 765 |
+
prompt_style_btn,
|
| 766 |
+
],
|
| 767 |
+
run_on_click=True,
|
| 768 |
+
)
|
| 769 |
+
with gr.Row(equal_height=True):
|
| 770 |
+
with gr.Column(scale=1, min_width=50):
|
| 771 |
+
gr.Examples(
|
| 772 |
+
examples=[
|
| 773 |
+
[
|
| 774 |
+
f"{cur_dir}/examples/CPR.jpg",
|
| 775 |
+
"<image> What are the people doing in this image?",
|
| 776 |
+
],
|
| 777 |
+
],
|
| 778 |
+
label="Image Example 3",
|
| 779 |
+
inputs=[imagebox, textbox],
|
| 780 |
+
fn=clear_after_click_example_1_image,
|
| 781 |
+
outputs=[
|
| 782 |
+
state,
|
| 783 |
+
imagebox,
|
| 784 |
+
imagebox_2,
|
| 785 |
+
imagebox_3,
|
| 786 |
+
videobox,
|
| 787 |
+
prompt_style_btn,
|
| 788 |
+
],
|
| 789 |
+
run_on_click=True,
|
| 790 |
+
)
|
| 791 |
+
with gr.Column(scale=1, min_width=50):
|
| 792 |
+
gr.Examples(
|
| 793 |
+
examples=[
|
| 794 |
+
[
|
| 795 |
+
f"{cur_dir}/examples/Wall_fissure.png",
|
| 796 |
+
"<image> What are the likely service needed for this building?",
|
| 797 |
+
],
|
| 798 |
+
],
|
| 799 |
+
label="Image Example 4",
|
| 800 |
+
inputs=[imagebox, textbox],
|
| 801 |
+
fn=clear_after_click_example_1_image,
|
| 802 |
+
outputs=[
|
| 803 |
+
state,
|
| 804 |
+
imagebox,
|
| 805 |
+
imagebox_2,
|
| 806 |
+
imagebox_3,
|
| 807 |
+
videobox,
|
| 808 |
+
prompt_style_btn,
|
| 809 |
+
],
|
| 810 |
+
run_on_click=True,
|
| 811 |
+
)
|
| 812 |
+
|
| 813 |
+
with gr.Row(equal_height=True):
|
| 814 |
+
with gr.Column(scale=1, min_width=50):
|
| 815 |
+
gr.Examples(
|
| 816 |
+
examples=[
|
| 817 |
+
[
|
| 818 |
+
f"{cur_dir}/examples/animal_blocking.png",
|
| 819 |
+
"<image> What is unusual in this image?",
|
| 820 |
+
],
|
| 821 |
+
],
|
| 822 |
+
label="Image Example 5",
|
| 823 |
+
inputs=[imagebox, textbox],
|
| 824 |
+
fn=clear_after_click_example_1_image,
|
| 825 |
+
outputs=[
|
| 826 |
+
state,
|
| 827 |
+
imagebox,
|
| 828 |
+
imagebox_2,
|
| 829 |
+
imagebox_3,
|
| 830 |
+
videobox,
|
| 831 |
+
prompt_style_btn,
|
| 832 |
+
],
|
| 833 |
+
run_on_click=True,
|
| 834 |
+
)
|
| 835 |
+
with gr.Column(scale=1, min_width=50):
|
| 836 |
+
gr.Examples(
|
| 837 |
+
examples=[
|
| 838 |
+
[
|
| 839 |
+
f"{cur_dir}/examples/windmill_people.png",
|
| 840 |
+
"<image> Can you describe what is happening?",
|
| 841 |
+
],
|
| 842 |
+
],
|
| 843 |
+
label="Image Example 6",
|
| 844 |
+
inputs=[imagebox, textbox],
|
| 845 |
+
fn=clear_after_click_example_1_image,
|
| 846 |
+
outputs=[
|
| 847 |
+
state,
|
| 848 |
+
imagebox,
|
| 849 |
+
imagebox_2,
|
| 850 |
+
imagebox_3,
|
| 851 |
+
videobox,
|
| 852 |
+
prompt_style_btn,
|
| 853 |
+
],
|
| 854 |
+
run_on_click=True,
|
| 855 |
+
)
|
| 856 |
+
|
| 857 |
+
gr.Examples(
|
| 858 |
+
examples=[
|
| 859 |
+
[
|
| 860 |
+
f"{cur_dir}/examples/climate_change/climate_change_1.png",
|
| 861 |
+
f"{cur_dir}/examples/climate_change/climate_change_2.png",
|
| 862 |
+
"<image> <image> What is the implication of temperature based on this image?",
|
| 863 |
+
],
|
| 864 |
+
],
|
| 865 |
+
inputs=[imagebox, imagebox_2, textbox],
|
| 866 |
+
label="Multi-image Example 1",
|
| 867 |
+
fn=clear_after_click_example_2_image,
|
| 868 |
+
outputs=[
|
| 869 |
+
state,
|
| 870 |
+
imagebox,
|
| 871 |
+
imagebox_2,
|
| 872 |
+
imagebox_3,
|
| 873 |
+
videobox,
|
| 874 |
+
prompt_style_btn,
|
| 875 |
+
],
|
| 876 |
+
run_on_click=True,
|
| 877 |
+
)
|
| 878 |
+
|
| 879 |
+
gr.Examples(
|
| 880 |
+
examples=[
|
| 881 |
+
[
|
| 882 |
+
f"{cur_dir}/examples/palms/palm1.png",
|
| 883 |
+
f"{cur_dir}/examples/palms/palm2.png",
|
| 884 |
+
f"{cur_dir}/examples/palms/palm3.png",
|
| 885 |
+
"8:15am: <image> 12:45pm: <image> 16:00pm: <image> When did I have lunch and what did I eat for lunch?",
|
| 886 |
+
],
|
| 887 |
+
],
|
| 888 |
+
inputs=[imagebox, imagebox_2, imagebox_3, textbox],
|
| 889 |
+
label="Multi-image Example 2",
|
| 890 |
+
fn=clear_after_click_example_3_image,
|
| 891 |
+
outputs=[
|
| 892 |
+
state,
|
| 893 |
+
imagebox,
|
| 894 |
+
imagebox_2,
|
| 895 |
+
imagebox_3,
|
| 896 |
+
videobox,
|
| 897 |
+
prompt_style_btn,
|
| 898 |
+
],
|
| 899 |
+
run_on_click=True,
|
| 900 |
+
)
|
| 901 |
+
|
| 902 |
+
gr.Examples(
|
| 903 |
+
examples=[
|
| 904 |
+
[
|
| 905 |
+
f"{cur_dir}/examples/golf/Golfman1.png",
|
| 906 |
+
f"{cur_dir}/examples/golf/Golfman2.png",
|
| 907 |
+
f"{cur_dir}/examples/golf/Golfman3.png",
|
| 908 |
+
"<image> <image> <image> What happens to the man after hitting the ball? And why does it happen?",
|
| 909 |
+
],
|
| 910 |
+
],
|
| 911 |
+
inputs=[imagebox, imagebox_2, imagebox_3, textbox],
|
| 912 |
+
label="Multi-image Example 3",
|
| 913 |
+
fn=clear_after_click_example_3_image,
|
| 914 |
+
outputs=[
|
| 915 |
+
state,
|
| 916 |
+
imagebox,
|
| 917 |
+
imagebox_2,
|
| 918 |
+
imagebox_3,
|
| 919 |
+
videobox,
|
| 920 |
+
prompt_style_btn,
|
| 921 |
+
],
|
| 922 |
+
run_on_click=True,
|
| 923 |
+
)
|
| 924 |
+
|
| 925 |
+
gr.Examples(
|
| 926 |
+
examples=[
|
| 927 |
+
[
|
| 928 |
+
f"{cur_dir}/examples/icl-logo/google.webp",
|
| 929 |
+
f"{cur_dir}/examples/icl-logo/apple.jpg",
|
| 930 |
+
f"{cur_dir}/examples/icl-logo/nvidia.png",
|
| 931 |
+
"<image> is famous for its search engine. <image> is famous for Mac and iPhone. <image> ",
|
| 932 |
+
],
|
| 933 |
+
],
|
| 934 |
+
inputs=[imagebox, imagebox_2, imagebox_3, textbox],
|
| 935 |
+
label="In-context Learning Example 1 (Please switch the prompt style to 'no-sys')",
|
| 936 |
+
fn=clear_after_click_example_3_image_icl,
|
| 937 |
+
outputs=[
|
| 938 |
+
state,
|
| 939 |
+
imagebox,
|
| 940 |
+
imagebox_2,
|
| 941 |
+
imagebox_3,
|
| 942 |
+
videobox,
|
| 943 |
+
prompt_style_btn,
|
| 944 |
+
],
|
| 945 |
+
run_on_click=True,
|
| 946 |
+
)
|
| 947 |
+
|
| 948 |
+
gr.Examples(
|
| 949 |
+
examples=[
|
| 950 |
+
[
|
| 951 |
+
f"{cur_dir}/examples/icl-building/csail_building.jpeg",
|
| 952 |
+
f"{cur_dir}/examples/icl-building/Toronto_Tower.jpeg",
|
| 953 |
+
f"{cur_dir}/examples/icl-building/Golden_State_Bridge.jpeg",
|
| 954 |
+
"<image> Boston. <image> Toronto. <image> ",
|
| 955 |
+
],
|
| 956 |
+
],
|
| 957 |
+
inputs=[imagebox, imagebox_2, imagebox_3, textbox],
|
| 958 |
+
label="In-context Learning Example 2 (Please switch the prompt style to 'no-sys')",
|
| 959 |
+
fn=clear_after_click_example_3_image_icl,
|
| 960 |
+
outputs=[
|
| 961 |
+
state,
|
| 962 |
+
imagebox,
|
| 963 |
+
imagebox_2,
|
| 964 |
+
imagebox_3,
|
| 965 |
+
videobox,
|
| 966 |
+
prompt_style_btn,
|
| 967 |
+
],
|
| 968 |
+
run_on_click=True,
|
| 969 |
+
)
|
| 970 |
+
|
| 971 |
+
gr.Examples(
|
| 972 |
+
examples=[
|
| 973 |
+
[
|
| 974 |
+
f"{cur_dir}/examples/arts/sunflowers.jpg",
|
| 975 |
+
f"{cur_dir}/examples/arts/the_persistence_of_memory.png",
|
| 976 |
+
f"{cur_dir}/examples/arts/impression_sunrise.png",
|
| 977 |
+
"<image> Vincent Van Gogh. <image> Salvador Dalí. <image>",
|
| 978 |
+
],
|
| 979 |
+
],
|
| 980 |
+
inputs=[imagebox, imagebox_2, imagebox_3, textbox],
|
| 981 |
+
label="In-context Learning Example 3 (Please switch the prompt style to 'no-sys')",
|
| 982 |
+
fn=clear_after_click_example_3_image_icl,
|
| 983 |
+
outputs=[
|
| 984 |
+
state,
|
| 985 |
+
imagebox,
|
| 986 |
+
imagebox_2,
|
| 987 |
+
imagebox_3,
|
| 988 |
+
videobox,
|
| 989 |
+
prompt_style_btn,
|
| 990 |
+
],
|
| 991 |
+
run_on_click=True,
|
| 992 |
+
)
|
| 993 |
+
|
| 994 |
+
with gr.Accordion("Parameters", open=False) as parameter_row:
|
| 995 |
+
temperature = gr.Slider(
|
| 996 |
+
minimum=0.0,
|
| 997 |
+
maximum=1.0,
|
| 998 |
+
value=0.2,
|
| 999 |
+
step=0.1,
|
| 1000 |
+
interactive=True,
|
| 1001 |
+
label="Temperature",
|
| 1002 |
+
)
|
| 1003 |
+
top_p = gr.Slider(
|
| 1004 |
+
minimum=0.0,
|
| 1005 |
+
maximum=1.0,
|
| 1006 |
+
value=1.0,
|
| 1007 |
+
step=0.1,
|
| 1008 |
+
interactive=True,
|
| 1009 |
+
label="Top P",
|
| 1010 |
+
)
|
| 1011 |
+
max_output_tokens = gr.Slider(
|
| 1012 |
+
minimum=0,
|
| 1013 |
+
maximum=1024,
|
| 1014 |
+
value=512,
|
| 1015 |
+
step=64,
|
| 1016 |
+
interactive=True,
|
| 1017 |
+
label="Max output tokens",
|
| 1018 |
+
)
|
| 1019 |
+
|
| 1020 |
+
# with gr.Row(elem_id="buttons") as button_row:
|
| 1021 |
+
# upvote_btn = gr.Button(value="👍 Upvote", interactive=False)
|
| 1022 |
+
# downvote_btn = gr.Button(value="👎 Downvote", interactive=False)
|
| 1023 |
+
# flag_btn = gr.Button(value="⚠️ Flag", interactive=False)
|
| 1024 |
+
# stop_btn = gr.Button(value="⏹️ Stop Generation", interactive=False)
|
| 1025 |
+
# regenerate_btn = gr.Button(value="🔄 Regenerate", interactive=False)
|
| 1026 |
+
|
| 1027 |
+
if not embed_mode:
|
| 1028 |
+
gr.Markdown(tos_markdown)
|
| 1029 |
+
gr.Markdown(learn_more_markdown)
|
| 1030 |
+
gr.Markdown(ack_markdown)
|
| 1031 |
+
url_params = gr.JSON(visible=False)
|
| 1032 |
+
|
| 1033 |
+
# Register listeners
|
| 1034 |
+
# btn_list = [upvote_btn, downvote_btn, flag_btn, regenerate_btn, clear_btn]
|
| 1035 |
+
btn_list = [regenerate_btn, clear_btn]
|
| 1036 |
+
|
| 1037 |
+
# upvote_btn.click(
|
| 1038 |
+
# upvote_last_response,
|
| 1039 |
+
# [state, model_selector],
|
| 1040 |
+
# [textbox, upvote_btn, downvote_btn, flag_btn],
|
| 1041 |
+
# queue=False,
|
| 1042 |
+
# )
|
| 1043 |
+
# downvote_btn.click(
|
| 1044 |
+
# downvote_last_response,
|
| 1045 |
+
# [state, model_selector],
|
| 1046 |
+
# [textbox, upvote_btn, downvote_btn, flag_btn],
|
| 1047 |
+
# queue=False,
|
| 1048 |
+
# )
|
| 1049 |
+
# flag_btn.click(
|
| 1050 |
+
# flag_last_response,
|
| 1051 |
+
# [state, model_selector],
|
| 1052 |
+
# [textbox, upvote_btn, downvote_btn, flag_btn],
|
| 1053 |
+
# queue=False,
|
| 1054 |
+
# )
|
| 1055 |
+
|
| 1056 |
+
regenerate_btn.click(
|
| 1057 |
+
regenerate,
|
| 1058 |
+
[state, image_process_mode],
|
| 1059 |
+
[state, chatbot, textbox] + btn_list,
|
| 1060 |
+
queue=False,
|
| 1061 |
+
).then(
|
| 1062 |
+
http_bot,
|
| 1063 |
+
[
|
| 1064 |
+
state,
|
| 1065 |
+
model_selector,
|
| 1066 |
+
temperature,
|
| 1067 |
+
top_p,
|
| 1068 |
+
max_output_tokens,
|
| 1069 |
+
prompt_style_btn,
|
| 1070 |
+
],
|
| 1071 |
+
[state, chatbot] + btn_list,
|
| 1072 |
+
)
|
| 1073 |
+
|
| 1074 |
+
prompt_style_btn.change(
|
| 1075 |
+
change_prompt_style, [state, prompt_style_btn], [state], queue=False
|
| 1076 |
+
)
|
| 1077 |
+
|
| 1078 |
+
clear_btn.click(
|
| 1079 |
+
clear_history,
|
| 1080 |
+
[prompt_style_btn],
|
| 1081 |
+
[state, chatbot, textbox, imagebox, imagebox_2, imagebox_3, videobox]
|
| 1082 |
+
+ btn_list,
|
| 1083 |
+
queue=False,
|
| 1084 |
+
)
|
| 1085 |
+
|
| 1086 |
+
# textbox.submit(
|
| 1087 |
+
# add_text,
|
| 1088 |
+
# [state, textbox, imagebox, image_process_mode],
|
| 1089 |
+
# [state, chatbot, textbox, imagebox] + btn_list,
|
| 1090 |
+
# queue=False
|
| 1091 |
+
# ).then(
|
| 1092 |
+
# http_bot,
|
| 1093 |
+
# [state, model_selector, temperature, top_p, max_output_tokens],
|
| 1094 |
+
# [state, chatbot] + btn_list
|
| 1095 |
+
# )
|
| 1096 |
+
|
| 1097 |
+
# im_submit_btn.click(
|
| 1098 |
+
# mirror,
|
| 1099 |
+
# inputs=[imagebox],
|
| 1100 |
+
# outputs=[imagebox_out]
|
| 1101 |
+
# ).then(
|
| 1102 |
+
# add_image,
|
| 1103 |
+
# [state, imagebox, image_process_mode],
|
| 1104 |
+
# [state, imagebox] + btn_list,
|
| 1105 |
+
# queue=False
|
| 1106 |
+
# )
|
| 1107 |
+
|
| 1108 |
+
textbox.submit(
|
| 1109 |
+
clear_text_history, [state, prompt_style_btn], [state, chatbot], queue=False
|
| 1110 |
+
).then(
|
| 1111 |
+
add_images,
|
| 1112 |
+
[state, imagebox, imagebox_2, imagebox_3, videobox, image_process_mode],
|
| 1113 |
+
[state],
|
| 1114 |
+
queue=False,
|
| 1115 |
+
).then(
|
| 1116 |
+
add_text_only, [state, textbox], [state, textbox] + btn_list, queue=False
|
| 1117 |
+
).then(
|
| 1118 |
+
http_bot,
|
| 1119 |
+
[
|
| 1120 |
+
state,
|
| 1121 |
+
model_selector,
|
| 1122 |
+
temperature,
|
| 1123 |
+
top_p,
|
| 1124 |
+
max_output_tokens,
|
| 1125 |
+
prompt_style_btn,
|
| 1126 |
+
],
|
| 1127 |
+
[state, chatbot] + btn_list,
|
| 1128 |
+
)
|
| 1129 |
+
|
| 1130 |
+
submit_btn.click(
|
| 1131 |
+
clear_text_history, [state, prompt_style_btn], [state, chatbot], queue=False
|
| 1132 |
+
).then(
|
| 1133 |
+
add_images,
|
| 1134 |
+
[state, imagebox, imagebox_2, imagebox_3, videobox, image_process_mode],
|
| 1135 |
+
[state],
|
| 1136 |
+
queue=False,
|
| 1137 |
+
).then(
|
| 1138 |
+
add_text_only, [state, textbox], [state, textbox] + btn_list, queue=False
|
| 1139 |
+
).then(
|
| 1140 |
+
http_bot,
|
| 1141 |
+
[
|
| 1142 |
+
state,
|
| 1143 |
+
model_selector,
|
| 1144 |
+
temperature,
|
| 1145 |
+
top_p,
|
| 1146 |
+
max_output_tokens,
|
| 1147 |
+
prompt_style_btn,
|
| 1148 |
+
],
|
| 1149 |
+
[state, chatbot] + btn_list,
|
| 1150 |
+
)
|
| 1151 |
+
|
| 1152 |
+
if args.model_list_mode == "once":
|
| 1153 |
+
demo.load(
|
| 1154 |
+
load_demo,
|
| 1155 |
+
[url_params, prompt_style_btn],
|
| 1156 |
+
[state, model_selector],
|
| 1157 |
+
_js=get_window_url_params,
|
| 1158 |
+
queue=False,
|
| 1159 |
+
)
|
| 1160 |
+
elif args.model_list_mode == "reload":
|
| 1161 |
+
demo.load(
|
| 1162 |
+
load_demo_refresh_model_list,
|
| 1163 |
+
[prompt_style_btn],
|
| 1164 |
+
[state, model_selector],
|
| 1165 |
+
queue=False,
|
| 1166 |
+
)
|
| 1167 |
+
else:
|
| 1168 |
+
raise ValueError(f"Unknown model list mode: {args.model_list_mode}")
|
| 1169 |
+
|
| 1170 |
+
return demo
|
| 1171 |
+
|
| 1172 |
+
|
| 1173 |
+
if __name__ == "__main__":
|
| 1174 |
+
parser = argparse.ArgumentParser()
|
| 1175 |
+
parser.add_argument("--host", type=str, default="0.0.0.0")
|
| 1176 |
+
parser.add_argument("--port", type=int)
|
| 1177 |
+
parser.add_argument("--controller-url", type=str, default="http://localhost:21001")
|
| 1178 |
+
parser.add_argument("--concurrency-count", type=int, default=10)
|
| 1179 |
+
parser.add_argument(
|
| 1180 |
+
"--model-list-mode", type=str, default="once", choices=["once", "reload"]
|
| 1181 |
+
)
|
| 1182 |
+
parser.add_argument("--share", action="store_true")
|
| 1183 |
+
parser.add_argument("--moderate", action="store_true")
|
| 1184 |
+
parser.add_argument("--embed", action="store_true")
|
| 1185 |
+
parser.add_argument(
|
| 1186 |
+
"--auto-pad-image-token",
|
| 1187 |
+
action="store_true",
|
| 1188 |
+
help="Automatically pad <image> token to the before of the prompt if no user inputs.",
|
| 1189 |
+
)
|
| 1190 |
+
# NOTE: For single image input, we still auto pad <image> token even if the --auto-pad-image-token is False
|
| 1191 |
+
args = parser.parse_args()
|
| 1192 |
+
logger.info(f"args: {args}")
|
| 1193 |
+
|
| 1194 |
+
models = get_model_list()
|
| 1195 |
+
|
| 1196 |
+
logger.info(args)
|
| 1197 |
+
demo = build_demo(args.embed)
|
| 1198 |
+
demo.queue(concurrency_count=args.concurrency_count, api_open=False).launch(
|
| 1199 |
+
server_name=args.host, server_port=args.port, share=args.share
|
| 1200 |
+
)
|
llm-awq/tinychat/serve/llava_conv.py
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
import dataclasses
|
| 17 |
+
from enum import auto, Enum
|
| 18 |
+
from typing import List, Tuple
|
| 19 |
+
|
| 20 |
+
from tinychat.utils.constants import AUTO_FILL_IM_TOKEN_HOLDER
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class SeparatorStyle(Enum):
|
| 24 |
+
"""Different separator style."""
|
| 25 |
+
|
| 26 |
+
SINGLE = auto()
|
| 27 |
+
TWO = auto()
|
| 28 |
+
MPT = auto()
|
| 29 |
+
PLAIN = auto()
|
| 30 |
+
LLAMA_2 = auto()
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
@dataclasses.dataclass
|
| 34 |
+
class Conversation:
|
| 35 |
+
"""A class that keeps all conversation history."""
|
| 36 |
+
|
| 37 |
+
system: str
|
| 38 |
+
roles: List[str]
|
| 39 |
+
messages: List[List[str]]
|
| 40 |
+
offset: int
|
| 41 |
+
sep_style: SeparatorStyle = SeparatorStyle.SINGLE
|
| 42 |
+
sep: str = "###"
|
| 43 |
+
sep2: str = None
|
| 44 |
+
version: str = "Unknown"
|
| 45 |
+
|
| 46 |
+
skip_next: bool = False
|
| 47 |
+
image_loaded: bool = False
|
| 48 |
+
|
| 49 |
+
def get_prompt(self):
|
| 50 |
+
messages = self.messages
|
| 51 |
+
if len(messages) > 0 and type(messages[0][1]) is tuple:
|
| 52 |
+
messages = self.messages.copy()
|
| 53 |
+
init_role, init_msg = messages[0].copy()
|
| 54 |
+
# init_msg = init_msg[0].replace(AUTO_FILL_IM_TOKEN_HOLDER, "").strip()
|
| 55 |
+
init_msg = init_msg[0].replace("", "").strip()
|
| 56 |
+
if "mmtag" in self.version:
|
| 57 |
+
messages[0] = (init_role, init_msg)
|
| 58 |
+
messages.insert(0, (self.roles[0], "<Image><image></Image>"))
|
| 59 |
+
messages.insert(1, (self.roles[1], "Received."))
|
| 60 |
+
else:
|
| 61 |
+
# messages[0] = (init_role, AUTO_FILL_IM_TOKEN_HOLDER + init_msg)
|
| 62 |
+
messages[0] = (init_role, "" + init_msg)
|
| 63 |
+
|
| 64 |
+
if self.sep_style == SeparatorStyle.SINGLE:
|
| 65 |
+
ret = self.system + self.sep
|
| 66 |
+
for role, message in messages:
|
| 67 |
+
if role:
|
| 68 |
+
if message:
|
| 69 |
+
if type(message) is tuple:
|
| 70 |
+
message, _, _ = message
|
| 71 |
+
ret += role + ": " + message + self.sep
|
| 72 |
+
else:
|
| 73 |
+
ret += role + ":"
|
| 74 |
+
else:
|
| 75 |
+
if message:
|
| 76 |
+
if type(message) is tuple:
|
| 77 |
+
message, _, _ = message
|
| 78 |
+
ret += message
|
| 79 |
+
elif self.sep_style == SeparatorStyle.TWO:
|
| 80 |
+
seps = [self.sep, self.sep2]
|
| 81 |
+
ret = self.system + seps[0]
|
| 82 |
+
for i, (role, message) in enumerate(messages):
|
| 83 |
+
if role:
|
| 84 |
+
if message:
|
| 85 |
+
if type(message) is tuple:
|
| 86 |
+
message, _, _ = message
|
| 87 |
+
ret += role + ": " + message + seps[i % 2]
|
| 88 |
+
else:
|
| 89 |
+
ret += role + ":"
|
| 90 |
+
else:
|
| 91 |
+
if message:
|
| 92 |
+
if type(message) is tuple:
|
| 93 |
+
message, _, _ = message
|
| 94 |
+
ret += message + seps[i % 2]
|
| 95 |
+
elif self.sep_style == SeparatorStyle.MPT:
|
| 96 |
+
ret = self.system + self.sep
|
| 97 |
+
for role, message in messages:
|
| 98 |
+
if message:
|
| 99 |
+
if type(message) is tuple:
|
| 100 |
+
message, _, _ = message
|
| 101 |
+
ret += role + message + self.sep
|
| 102 |
+
else:
|
| 103 |
+
ret += role
|
| 104 |
+
elif self.sep_style == SeparatorStyle.LLAMA_2:
|
| 105 |
+
wrap_sys = lambda msg: f"<<SYS>>\n{msg}\n<</SYS>>\n\n"
|
| 106 |
+
wrap_inst = lambda msg: f"[INST] {msg} [/INST]"
|
| 107 |
+
ret = ""
|
| 108 |
+
|
| 109 |
+
for i, (role, message) in enumerate(messages):
|
| 110 |
+
if i == 0:
|
| 111 |
+
assert message, "first message should not be none"
|
| 112 |
+
assert role == self.roles[0], "first message should come from user"
|
| 113 |
+
if message:
|
| 114 |
+
if type(message) is tuple:
|
| 115 |
+
message, _, _ = message
|
| 116 |
+
if i == 0:
|
| 117 |
+
message = wrap_sys(self.system) + message
|
| 118 |
+
if i % 2 == 0:
|
| 119 |
+
message = wrap_inst(message)
|
| 120 |
+
ret += self.sep + message
|
| 121 |
+
else:
|
| 122 |
+
ret += " " + message + " " + self.sep2
|
| 123 |
+
else:
|
| 124 |
+
ret += ""
|
| 125 |
+
ret = ret.lstrip(self.sep)
|
| 126 |
+
elif self.sep_style == SeparatorStyle.PLAIN:
|
| 127 |
+
seps = [self.sep, self.sep2]
|
| 128 |
+
ret = self.system
|
| 129 |
+
for i, (role, message) in enumerate(messages):
|
| 130 |
+
if message:
|
| 131 |
+
if type(message) is tuple:
|
| 132 |
+
message, _, _ = message
|
| 133 |
+
ret += message + seps[i % 2]
|
| 134 |
+
else:
|
| 135 |
+
ret += ""
|
| 136 |
+
else:
|
| 137 |
+
raise ValueError(f"Invalid style: {self.sep_style}")
|
| 138 |
+
|
| 139 |
+
return ret
|
| 140 |
+
|
| 141 |
+
def append_message(self, role, message):
|
| 142 |
+
self.messages.append([role, message])
|
| 143 |
+
|
| 144 |
+
def get_images(self, return_pil=False):
|
| 145 |
+
images = []
|
| 146 |
+
for i, (role, msg) in enumerate(self.messages[self.offset :]):
|
| 147 |
+
if i % 2 == 0:
|
| 148 |
+
if type(msg) is tuple:
|
| 149 |
+
import base64
|
| 150 |
+
from io import BytesIO
|
| 151 |
+
from PIL import Image
|
| 152 |
+
|
| 153 |
+
msg, image, image_process_mode = msg
|
| 154 |
+
if image_process_mode == "Pad":
|
| 155 |
+
|
| 156 |
+
def expand2square(pil_img, background_color=(122, 116, 104)):
|
| 157 |
+
width, height = pil_img.size
|
| 158 |
+
if width == height:
|
| 159 |
+
return pil_img
|
| 160 |
+
elif width > height:
|
| 161 |
+
result = Image.new(
|
| 162 |
+
pil_img.mode, (width, width), background_color
|
| 163 |
+
)
|
| 164 |
+
result.paste(pil_img, (0, (width - height) // 2))
|
| 165 |
+
return result
|
| 166 |
+
else:
|
| 167 |
+
result = Image.new(
|
| 168 |
+
pil_img.mode, (height, height), background_color
|
| 169 |
+
)
|
| 170 |
+
result.paste(pil_img, ((height - width) // 2, 0))
|
| 171 |
+
return result
|
| 172 |
+
|
| 173 |
+
image = expand2square(image)
|
| 174 |
+
elif image_process_mode in ["Default", "Crop"]:
|
| 175 |
+
pass
|
| 176 |
+
elif image_process_mode == "Resize":
|
| 177 |
+
image = image.resize((336, 336))
|
| 178 |
+
else:
|
| 179 |
+
raise ValueError(
|
| 180 |
+
f"Invalid image_process_mode: {image_process_mode}"
|
| 181 |
+
)
|
| 182 |
+
max_hw, min_hw = max(image.size), min(image.size)
|
| 183 |
+
aspect_ratio = max_hw / min_hw
|
| 184 |
+
max_len, min_len = 800, 400
|
| 185 |
+
shortest_edge = int(min(max_len / aspect_ratio, min_len, min_hw))
|
| 186 |
+
longest_edge = int(shortest_edge * aspect_ratio)
|
| 187 |
+
W, H = image.size
|
| 188 |
+
if longest_edge != max(image.size):
|
| 189 |
+
if H > W:
|
| 190 |
+
H, W = longest_edge, shortest_edge
|
| 191 |
+
else:
|
| 192 |
+
H, W = shortest_edge, longest_edge
|
| 193 |
+
image = image.resize((W, H))
|
| 194 |
+
if return_pil:
|
| 195 |
+
images.append(image)
|
| 196 |
+
else:
|
| 197 |
+
buffered = BytesIO()
|
| 198 |
+
image.save(buffered, format="PNG")
|
| 199 |
+
img_b64_str = base64.b64encode(buffered.getvalue()).decode()
|
| 200 |
+
images.append(img_b64_str)
|
| 201 |
+
return images
|
| 202 |
+
|
| 203 |
+
def to_gradio_chatbot(self):
|
| 204 |
+
ret = []
|
| 205 |
+
# count the figures to skip visualizing them in the text box.
|
| 206 |
+
cur_num_fig = 0
|
| 207 |
+
for i, (role, msg) in enumerate(self.messages[self.offset :]):
|
| 208 |
+
if i % 2 == 0:
|
| 209 |
+
if type(msg) is tuple:
|
| 210 |
+
# Skip the visualization of image in the chatbox
|
| 211 |
+
cur_num_fig += 1
|
| 212 |
+
continue
|
| 213 |
+
# import base64
|
| 214 |
+
# from io import BytesIO
|
| 215 |
+
# msg, image, image_process_mode = msg
|
| 216 |
+
# max_hw, min_hw = max(image.size), min(image.size)
|
| 217 |
+
# aspect_ratio = max_hw / min_hw
|
| 218 |
+
# max_len, min_len = 800, 400
|
| 219 |
+
# shortest_edge = int(min(max_len / aspect_ratio, min_len, min_hw))
|
| 220 |
+
# longest_edge = int(shortest_edge * aspect_ratio)
|
| 221 |
+
# W, H = image.size
|
| 222 |
+
# if H > W:
|
| 223 |
+
# H, W = longest_edge, shortest_edge
|
| 224 |
+
# else:
|
| 225 |
+
# H, W = shortest_edge, longest_edge
|
| 226 |
+
# image = image.resize((W, H))
|
| 227 |
+
# buffered = BytesIO()
|
| 228 |
+
# image.save(buffered, format="JPEG")
|
| 229 |
+
# img_b64_str = base64.b64encode(buffered.getvalue()).decode()
|
| 230 |
+
# img_str = f'<img src="data:image/png;base64,{img_b64_str}" alt="user upload image" />'
|
| 231 |
+
# msg = img_str + msg.replace('<image>', '').strip()
|
| 232 |
+
# ret.append([msg[0], None])
|
| 233 |
+
else:
|
| 234 |
+
ret.append([msg, None])
|
| 235 |
+
else:
|
| 236 |
+
if cur_num_fig > 0:
|
| 237 |
+
cur_num_fig -= 1
|
| 238 |
+
continue
|
| 239 |
+
ret[-1][-1] = msg
|
| 240 |
+
return ret
|
| 241 |
+
|
| 242 |
+
def copy(self):
|
| 243 |
+
return Conversation(
|
| 244 |
+
system=self.system,
|
| 245 |
+
roles=self.roles,
|
| 246 |
+
messages=[[x, y] for x, y in self.messages],
|
| 247 |
+
offset=self.offset,
|
| 248 |
+
sep_style=self.sep_style,
|
| 249 |
+
sep=self.sep,
|
| 250 |
+
sep2=self.sep2,
|
| 251 |
+
version=self.version,
|
| 252 |
+
image_loaded=self.image_loaded,
|
| 253 |
+
)
|
| 254 |
+
|
| 255 |
+
def dict(self):
|
| 256 |
+
if len(self.get_images()) > 0:
|
| 257 |
+
return {
|
| 258 |
+
"system": self.system,
|
| 259 |
+
"roles": self.roles,
|
| 260 |
+
"messages": [
|
| 261 |
+
[x, y[0] if type(y) is tuple else y] for x, y in self.messages
|
| 262 |
+
],
|
| 263 |
+
"offset": self.offset,
|
| 264 |
+
"sep": self.sep,
|
| 265 |
+
"sep2": self.sep2,
|
| 266 |
+
}
|
| 267 |
+
return {
|
| 268 |
+
"system": self.system,
|
| 269 |
+
"roles": self.roles,
|
| 270 |
+
"messages": self.messages,
|
| 271 |
+
"offset": self.offset,
|
| 272 |
+
"sep": self.sep,
|
| 273 |
+
"sep2": self.sep2,
|
| 274 |
+
}
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
conv_vicuna_v0 = Conversation(
|
| 278 |
+
system="A chat between a curious human and an artificial intelligence assistant. "
|
| 279 |
+
"The assistant gives helpful, detailed, and polite answers to the human's questions.",
|
| 280 |
+
roles=("Human", "Assistant"),
|
| 281 |
+
messages=(
|
| 282 |
+
(
|
| 283 |
+
"Human",
|
| 284 |
+
"What are the key differences between renewable and non-renewable energy sources?",
|
| 285 |
+
),
|
| 286 |
+
(
|
| 287 |
+
"Assistant",
|
| 288 |
+
"Renewable energy sources are those that can be replenished naturally in a relatively "
|
| 289 |
+
"short amount of time, such as solar, wind, hydro, geothermal, and biomass. "
|
| 290 |
+
"Non-renewable energy sources, on the other hand, are finite and will eventually be "
|
| 291 |
+
"depleted, such as coal, oil, and natural gas. Here are some key differences between "
|
| 292 |
+
"renewable and non-renewable energy sources:\n"
|
| 293 |
+
"1. Availability: Renewable energy sources are virtually inexhaustible, while non-renewable "
|
| 294 |
+
"energy sources are finite and will eventually run out.\n"
|
| 295 |
+
"2. Environmental impact: Renewable energy sources have a much lower environmental impact "
|
| 296 |
+
"than non-renewable sources, which can lead to air and water pollution, greenhouse gas emissions, "
|
| 297 |
+
"and other negative effects.\n"
|
| 298 |
+
"3. Cost: Renewable energy sources can be more expensive to initially set up, but they typically "
|
| 299 |
+
"have lower operational costs than non-renewable sources.\n"
|
| 300 |
+
"4. Reliability: Renewable energy sources are often more reliable and can be used in more remote "
|
| 301 |
+
"locations than non-renewable sources.\n"
|
| 302 |
+
"5. Flexibility: Renewable energy sources are often more flexible and can be adapted to different "
|
| 303 |
+
"situations and needs, while non-renewable sources are more rigid and inflexible.\n"
|
| 304 |
+
"6. Sustainability: Renewable energy sources are more sustainable over the long term, while "
|
| 305 |
+
"non-renewable sources are not, and their depletion can lead to economic and social instability.\n",
|
| 306 |
+
),
|
| 307 |
+
),
|
| 308 |
+
offset=2,
|
| 309 |
+
sep_style=SeparatorStyle.SINGLE,
|
| 310 |
+
sep="###",
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
empty_conv = Conversation(
|
| 314 |
+
system="",
|
| 315 |
+
roles=("", ""),
|
| 316 |
+
version="no-sys",
|
| 317 |
+
messages=(),
|
| 318 |
+
offset=0,
|
| 319 |
+
sep_style=SeparatorStyle.TWO,
|
| 320 |
+
sep="",
|
| 321 |
+
sep2="</s>",
|
| 322 |
+
)
|
| 323 |
+
|
| 324 |
+
conv_vicuna_v1 = Conversation(
|
| 325 |
+
system="A chat between a curious user and an artificial intelligence assistant. "
|
| 326 |
+
"The assistant gives helpful, detailed, and polite answers to the user's questions.",
|
| 327 |
+
roles=("USER", "ASSISTANT"),
|
| 328 |
+
version="default",
|
| 329 |
+
messages=(),
|
| 330 |
+
offset=0,
|
| 331 |
+
sep_style=SeparatorStyle.TWO,
|
| 332 |
+
sep=" ",
|
| 333 |
+
sep2="</s>",
|
| 334 |
+
)
|
| 335 |
+
|
| 336 |
+
conv_llama_2 = Conversation(
|
| 337 |
+
system="""You are a helpful, respectful and honest assistant. Always answer as helpfully as possible, while being safe. Your answers should not include any harmful, unethical, racist, sexist, toxic, dangerous, or illegal content. Please ensure that your responses are socially unbiased and positive in nature.
|
| 338 |
+
|
| 339 |
+
If a question does not make any sense, or is not factually coherent, explain why instead of answering something not correct. If you don't know the answer to a question, please don't share false information.""",
|
| 340 |
+
roles=("USER", "ASSISTANT"),
|
| 341 |
+
version="llama_v2",
|
| 342 |
+
messages=(),
|
| 343 |
+
offset=0,
|
| 344 |
+
sep_style=SeparatorStyle.LLAMA_2,
|
| 345 |
+
sep="<s>",
|
| 346 |
+
sep2="</s>",
|
| 347 |
+
)
|
| 348 |
+
|
| 349 |
+
conv_llava_llama_2 = Conversation(
|
| 350 |
+
system="You are a helpful language and vision assistant. "
|
| 351 |
+
"You are able to understand the visual content that the user provides, "
|
| 352 |
+
"and assist the user with a variety of tasks using natural language.",
|
| 353 |
+
roles=("USER", "ASSISTANT"),
|
| 354 |
+
version="llama_v2",
|
| 355 |
+
messages=(),
|
| 356 |
+
offset=0,
|
| 357 |
+
sep_style=SeparatorStyle.LLAMA_2,
|
| 358 |
+
sep="<s>",
|
| 359 |
+
sep2="</s>",
|
| 360 |
+
)
|
| 361 |
+
|
| 362 |
+
conv_mpt = Conversation(
|
| 363 |
+
system="""<|im_start|>system
|
| 364 |
+
A conversation between a user and an LLM-based AI assistant. The assistant gives helpful and honest answers.""",
|
| 365 |
+
roles=("<|im_start|>user\n", "<|im_start|>assistant\n"),
|
| 366 |
+
version="mpt",
|
| 367 |
+
messages=(),
|
| 368 |
+
offset=0,
|
| 369 |
+
sep_style=SeparatorStyle.MPT,
|
| 370 |
+
sep="<|im_end|>",
|
| 371 |
+
)
|
| 372 |
+
|
| 373 |
+
conv_llava_plain = Conversation(
|
| 374 |
+
system="",
|
| 375 |
+
roles=("", ""),
|
| 376 |
+
messages=(),
|
| 377 |
+
offset=0,
|
| 378 |
+
sep_style=SeparatorStyle.PLAIN,
|
| 379 |
+
sep="\n",
|
| 380 |
+
)
|
| 381 |
+
|
| 382 |
+
conv_llava_v0 = Conversation(
|
| 383 |
+
system="A chat between a curious human and an artificial intelligence assistant. "
|
| 384 |
+
"The assistant gives helpful, detailed, and polite answers to the human's questions.",
|
| 385 |
+
roles=("Human", "Assistant"),
|
| 386 |
+
messages=(),
|
| 387 |
+
offset=0,
|
| 388 |
+
sep_style=SeparatorStyle.SINGLE,
|
| 389 |
+
sep="###",
|
| 390 |
+
)
|
| 391 |
+
|
| 392 |
+
conv_llava_v0_mmtag = Conversation(
|
| 393 |
+
system="A chat between a curious user and an artificial intelligence assistant. "
|
| 394 |
+
"The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language."
|
| 395 |
+
"The visual content will be provided with the following format: <Image>visual content</Image>.",
|
| 396 |
+
roles=("Human", "Assistant"),
|
| 397 |
+
messages=(),
|
| 398 |
+
offset=0,
|
| 399 |
+
sep_style=SeparatorStyle.SINGLE,
|
| 400 |
+
sep="###",
|
| 401 |
+
version="v0_mmtag",
|
| 402 |
+
)
|
| 403 |
+
|
| 404 |
+
conv_llava_v1 = Conversation(
|
| 405 |
+
system="A chat between a curious human and an artificial intelligence assistant. "
|
| 406 |
+
"The assistant gives helpful, detailed, and polite answers to the human's questions.",
|
| 407 |
+
roles=("USER", "ASSISTANT"),
|
| 408 |
+
version="v1",
|
| 409 |
+
messages=(),
|
| 410 |
+
offset=0,
|
| 411 |
+
sep_style=SeparatorStyle.TWO,
|
| 412 |
+
sep=" ",
|
| 413 |
+
sep2="</s>",
|
| 414 |
+
)
|
| 415 |
+
|
| 416 |
+
conv_llava_v1_mmtag = Conversation(
|
| 417 |
+
system="A chat between a curious user and an artificial intelligence assistant. "
|
| 418 |
+
"The assistant is able to understand the visual content that the user provides, and assist the user with a variety of tasks using natural language."
|
| 419 |
+
"The visual content will be provided with the following format: <Image>visual content</Image>.",
|
| 420 |
+
roles=("USER", "ASSISTANT"),
|
| 421 |
+
messages=(),
|
| 422 |
+
offset=0,
|
| 423 |
+
sep_style=SeparatorStyle.TWO,
|
| 424 |
+
sep=" ",
|
| 425 |
+
sep2="</s>",
|
| 426 |
+
version="v1_mmtag",
|
| 427 |
+
)
|
| 428 |
+
|
| 429 |
+
default_conversation = conv_vicuna_v1
|
| 430 |
+
conv_templates = {
|
| 431 |
+
"no-sys": empty_conv,
|
| 432 |
+
"default": conv_vicuna_v1,
|
| 433 |
+
"v0": conv_vicuna_v0,
|
| 434 |
+
"v1": conv_vicuna_v1,
|
| 435 |
+
"vicuna_v1": conv_vicuna_v1,
|
| 436 |
+
"llama_2": conv_llama_2,
|
| 437 |
+
"plain": conv_llava_plain,
|
| 438 |
+
"v0_plain": conv_llava_plain,
|
| 439 |
+
"llava_v0": conv_llava_v0,
|
| 440 |
+
"v0_mmtag": conv_llava_v0_mmtag,
|
| 441 |
+
"llava_v1": conv_llava_v1,
|
| 442 |
+
"v1_mmtag": conv_llava_v1_mmtag,
|
| 443 |
+
"llava_llama_2": conv_llava_llama_2,
|
| 444 |
+
"mpt": conv_mpt,
|
| 445 |
+
}
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def get_conversation(version: str):
|
| 449 |
+
conv = conv_templates.get(version, conv_vicuna_v1).copy()
|
| 450 |
+
return conv
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
if __name__ == "__main__":
|
| 454 |
+
print(default_conversation.get_prompt())
|
llm-awq/tinychat/serve/model_worker.py
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Modified from https://github.com/haotian-liu/LLaVA
|
| 2 |
+
# Copyright 2023 Haotian Liu
|
| 3 |
+
#
|
| 4 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 5 |
+
# you may not use this file except in compliance with the License.
|
| 6 |
+
# You may obtain a copy of the License at
|
| 7 |
+
#
|
| 8 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 9 |
+
#
|
| 10 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 11 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 12 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 13 |
+
# See the License for the specific language governing permissions and
|
| 14 |
+
# limitations under the License.
|
| 15 |
+
|
| 16 |
+
"""
|
| 17 |
+
A model worker executes the model.
|
| 18 |
+
"""
|
| 19 |
+
import argparse
|
| 20 |
+
import asyncio
|
| 21 |
+
import json
|
| 22 |
+
import time
|
| 23 |
+
import threading
|
| 24 |
+
import uuid
|
| 25 |
+
|
| 26 |
+
from fastapi import FastAPI, Request, BackgroundTasks
|
| 27 |
+
from fastapi.responses import StreamingResponse
|
| 28 |
+
import requests
|
| 29 |
+
import torch
|
| 30 |
+
import uvicorn
|
| 31 |
+
from functools import partial
|
| 32 |
+
from tqdm import tqdm
|
| 33 |
+
|
| 34 |
+
import tinychat.utils.constants
|
| 35 |
+
from tinychat.utils.constants import (
|
| 36 |
+
WORKER_HEART_BEAT_INTERVAL,
|
| 37 |
+
LLAVA_DEFAULT_IMAGE_TOKEN_IDX,
|
| 38 |
+
LLAVA_DEFAULT_IMAGE_TOKEN,
|
| 39 |
+
LLAVA_DEFAULT_IM_START_TOKEN,
|
| 40 |
+
LLAVA_DEFAULT_IM_END_TOKEN,
|
| 41 |
+
)
|
| 42 |
+
from tinychat.utils.log_utils import (
|
| 43 |
+
build_logger,
|
| 44 |
+
server_error_msg,
|
| 45 |
+
pretty_print_semaphore,
|
| 46 |
+
)
|
| 47 |
+
from tinychat.stream_generators.llava_stream_gen import tokenizer_image_token
|
| 48 |
+
from tinychat.utils.llava_image_processing import process_images, load_image_from_base64
|
| 49 |
+
from tinychat.models.llava_llama import LlavaLlamaForCausalLM
|
| 50 |
+
from tinychat.stream_generators.llava_stream_gen import LlavaStreamGenerator
|
| 51 |
+
from tinychat.utils.prompt_templates import (
|
| 52 |
+
get_prompter,
|
| 53 |
+
get_stop_token_ids,
|
| 54 |
+
get_image_token,
|
| 55 |
+
)
|
| 56 |
+
from tinychat.utils.conversation_utils import gen_params
|
| 57 |
+
|
| 58 |
+
from transformers import AutoConfig, AutoTokenizer
|
| 59 |
+
from accelerate import load_checkpoint_and_dispatch
|
| 60 |
+
|
| 61 |
+
# import os
|
| 62 |
+
# os.environ["CUDA_VISIBLE_DEVICES"] = "0"
|
| 63 |
+
|
| 64 |
+
GB = 1 << 30
|
| 65 |
+
|
| 66 |
+
worker_id = str(uuid.uuid4())[:6]
|
| 67 |
+
logger = build_logger("model_worker", f"model_worker_{worker_id}.log")
|
| 68 |
+
global_counter = 0
|
| 69 |
+
|
| 70 |
+
model_semaphore = None
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def heart_beat_worker(controller):
|
| 74 |
+
while True:
|
| 75 |
+
time.sleep(WORKER_HEART_BEAT_INTERVAL)
|
| 76 |
+
controller.send_heart_beat()
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def skip(*args, **kwargs):
|
| 80 |
+
pass
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
class ModelWorker:
|
| 84 |
+
def __init__(
|
| 85 |
+
self,
|
| 86 |
+
controller_addr,
|
| 87 |
+
worker_addr,
|
| 88 |
+
worker_id,
|
| 89 |
+
no_register,
|
| 90 |
+
model_type,
|
| 91 |
+
model_path,
|
| 92 |
+
model_name,
|
| 93 |
+
quant_path,
|
| 94 |
+
precision,
|
| 95 |
+
device,
|
| 96 |
+
):
|
| 97 |
+
self.controller_addr = controller_addr
|
| 98 |
+
self.worker_addr = worker_addr
|
| 99 |
+
self.worker_id = worker_id
|
| 100 |
+
self.model_type = model_type
|
| 101 |
+
self.model_path = model_path
|
| 102 |
+
if model_path.endswith("/"):
|
| 103 |
+
model_path = model_path[:-1]
|
| 104 |
+
if model_name is None:
|
| 105 |
+
model_paths = model_path.split("/")
|
| 106 |
+
if model_paths[-1].startswith("checkpoint-"):
|
| 107 |
+
self.model_name = model_paths[-2] + "_" + model_paths[-1]
|
| 108 |
+
else:
|
| 109 |
+
self.model_name = model_paths[-1]
|
| 110 |
+
else:
|
| 111 |
+
self.model_name = model_name
|
| 112 |
+
if precision == "W4A16":
|
| 113 |
+
self.model_name = self.model_name + "-4bit-AWQ"
|
| 114 |
+
self.device = device
|
| 115 |
+
|
| 116 |
+
# Load TinyChat model
|
| 117 |
+
logger.info(f"Loading the model {self.model_name} on worker {worker_id} ...")
|
| 118 |
+
|
| 119 |
+
setattr(torch.nn.Linear, "reset_parameters", lambda self: None)
|
| 120 |
+
setattr(torch.nn.LayerNorm, "reset_parameters", lambda self: None)
|
| 121 |
+
torch.nn.init.kaiming_uniform_ = skip
|
| 122 |
+
torch.nn.init.kaiming_normal_ = skip
|
| 123 |
+
torch.nn.init.uniform_ = skip
|
| 124 |
+
torch.nn.init.normal_ = skip
|
| 125 |
+
|
| 126 |
+
self.tokenizer = AutoTokenizer.from_pretrained(model_path, use_fast=False)
|
| 127 |
+
tinychat.utils.constants.LLAVA_DEFAULT_IMAGE_PATCH_TOKEN_IDX = (
|
| 128 |
+
self.tokenizer.convert_tokens_to_ids(
|
| 129 |
+
[tinychat.utils.constants.LLAVA_DEFAULT_IMAGE_PATCH_TOKEN]
|
| 130 |
+
)[0]
|
| 131 |
+
)
|
| 132 |
+
config = AutoConfig.from_pretrained(args.model_path, trust_remote_code=True)
|
| 133 |
+
config.min_max_range_path = args.model_path + "/emb_min_max.pt"
|
| 134 |
+
model = LlavaLlamaForCausalLM(config, args.device).half()
|
| 135 |
+
vision_tower = model.get_model().vision_tower
|
| 136 |
+
if not vision_tower.is_loaded:
|
| 137 |
+
vision_tower.load_model()
|
| 138 |
+
vision_tower = vision_tower.half()
|
| 139 |
+
self.image_processor = vision_tower.image_processor
|
| 140 |
+
|
| 141 |
+
if precision == "W16A16":
|
| 142 |
+
pbar = tqdm(range(1))
|
| 143 |
+
pbar.set_description("Loading checkpoint shards")
|
| 144 |
+
for i in pbar:
|
| 145 |
+
model = load_checkpoint_and_dispatch(
|
| 146 |
+
model,
|
| 147 |
+
model_path,
|
| 148 |
+
no_split_module_classes=[
|
| 149 |
+
"OPTDecoderLayer",
|
| 150 |
+
"LlamaDecoderLayer",
|
| 151 |
+
"BloomBlock",
|
| 152 |
+
"MPTBlock",
|
| 153 |
+
"DecoderLayer",
|
| 154 |
+
"CLIPEncoderLayer",
|
| 155 |
+
],
|
| 156 |
+
).to(device)
|
| 157 |
+
elif precision == "W4A16":
|
| 158 |
+
from tinychat.utils.load_quant import load_awq_model
|
| 159 |
+
|
| 160 |
+
model = load_awq_model(model, quant_path, 4, 128, device)
|
| 161 |
+
from tinychat.modules import (
|
| 162 |
+
make_quant_norm,
|
| 163 |
+
make_quant_attn,
|
| 164 |
+
)
|
| 165 |
+
|
| 166 |
+
make_quant_attn(model, device)
|
| 167 |
+
make_quant_norm(model)
|
| 168 |
+
model = model.to(device)
|
| 169 |
+
else:
|
| 170 |
+
raise NotImplementedError(f"Precision {precision} is not supported.")
|
| 171 |
+
|
| 172 |
+
self.model = model
|
| 173 |
+
self.is_multimodal = (
|
| 174 |
+
"llava" in self.model_name.lower() or "vila" in self.model_name.lower()
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
if not no_register:
|
| 178 |
+
self.register_to_controller()
|
| 179 |
+
self.heart_beat_thread = threading.Thread(
|
| 180 |
+
target=heart_beat_worker, args=(self,)
|
| 181 |
+
)
|
| 182 |
+
self.heart_beat_thread.start()
|
| 183 |
+
|
| 184 |
+
def register_to_controller(self):
|
| 185 |
+
logger.info("Register to controller")
|
| 186 |
+
|
| 187 |
+
url = self.controller_addr + "/register_worker"
|
| 188 |
+
data = {
|
| 189 |
+
"worker_name": self.worker_addr,
|
| 190 |
+
"check_heart_beat": True,
|
| 191 |
+
"worker_status": self.get_status(),
|
| 192 |
+
}
|
| 193 |
+
r = requests.post(url, json=data)
|
| 194 |
+
assert r.status_code == 200
|
| 195 |
+
|
| 196 |
+
def send_heart_beat(self):
|
| 197 |
+
logger.info(
|
| 198 |
+
f"Send heart beat. Models: {[self.model_name]}. "
|
| 199 |
+
f"Semaphore: {pretty_print_semaphore(model_semaphore)}. "
|
| 200 |
+
f"global_counter: {global_counter}"
|
| 201 |
+
)
|
| 202 |
+
|
| 203 |
+
url = self.controller_addr + "/receive_heart_beat"
|
| 204 |
+
|
| 205 |
+
while True:
|
| 206 |
+
try:
|
| 207 |
+
ret = requests.post(
|
| 208 |
+
url,
|
| 209 |
+
json={
|
| 210 |
+
"worker_name": self.worker_addr,
|
| 211 |
+
"queue_length": self.get_queue_length(),
|
| 212 |
+
},
|
| 213 |
+
timeout=5,
|
| 214 |
+
)
|
| 215 |
+
exist = ret.json()["exist"]
|
| 216 |
+
break
|
| 217 |
+
except requests.exceptions.RequestException as e:
|
| 218 |
+
logger.error(f"heart beat error: {e}")
|
| 219 |
+
time.sleep(5)
|
| 220 |
+
|
| 221 |
+
if not exist:
|
| 222 |
+
self.register_to_controller()
|
| 223 |
+
|
| 224 |
+
def get_queue_length(self):
|
| 225 |
+
if model_semaphore is None:
|
| 226 |
+
return 0
|
| 227 |
+
else:
|
| 228 |
+
return (
|
| 229 |
+
args.limit_model_concurrency
|
| 230 |
+
- model_semaphore._value
|
| 231 |
+
+ (
|
| 232 |
+
len(model_semaphore._waiters)
|
| 233 |
+
if model_semaphore._waiters is not None
|
| 234 |
+
else 0
|
| 235 |
+
)
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
def get_status(self):
|
| 239 |
+
return {
|
| 240 |
+
"model_names": [self.model_name],
|
| 241 |
+
"speed": 1,
|
| 242 |
+
"queue_length": self.get_queue_length(),
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
@torch.inference_mode()
|
| 246 |
+
def generate_stream(self, params):
|
| 247 |
+
tokenizer, model, image_processor = (
|
| 248 |
+
self.tokenizer,
|
| 249 |
+
self.model,
|
| 250 |
+
self.image_processor,
|
| 251 |
+
)
|
| 252 |
+
|
| 253 |
+
prompt = params["prompt"]
|
| 254 |
+
ori_prompt = prompt
|
| 255 |
+
images = params.get("images", None)
|
| 256 |
+
if images is not None and len(images) > 0 and self.is_multimodal:
|
| 257 |
+
if len(images) > 0:
|
| 258 |
+
if len(images) != prompt.count(LLAVA_DEFAULT_IMAGE_TOKEN):
|
| 259 |
+
raise ValueError(
|
| 260 |
+
"Number of images does not match number of <image> tokens in prompt"
|
| 261 |
+
)
|
| 262 |
+
|
| 263 |
+
images = [load_image_from_base64(image) for image in images]
|
| 264 |
+
images = process_images(images, image_processor, model.config)
|
| 265 |
+
|
| 266 |
+
if type(images) is list:
|
| 267 |
+
images = [
|
| 268 |
+
image.to(model.device, dtype=torch.float16) for image in images
|
| 269 |
+
]
|
| 270 |
+
else:
|
| 271 |
+
images = images.to(model.device, dtype=torch.float16)
|
| 272 |
+
|
| 273 |
+
replace_token = LLAVA_DEFAULT_IMAGE_TOKEN
|
| 274 |
+
if getattr(model.config, "mm_use_im_start_end", False):
|
| 275 |
+
replace_token = (
|
| 276 |
+
LLAVA_DEFAULT_IM_START_TOKEN
|
| 277 |
+
+ replace_token
|
| 278 |
+
+ LLAVA_DEFAULT_IM_END_TOKEN
|
| 279 |
+
)
|
| 280 |
+
prompt = prompt.replace(LLAVA_DEFAULT_IMAGE_TOKEN, replace_token)
|
| 281 |
+
else:
|
| 282 |
+
images = None
|
| 283 |
+
else:
|
| 284 |
+
images = None
|
| 285 |
+
|
| 286 |
+
gen_params.temp = float(params.get("temperature", 1.0))
|
| 287 |
+
gen_params.top_p = float(params.get("top_p", 1.0))
|
| 288 |
+
gen_params.n_predict = min(int(params.get("max_new_tokens", 256)), 1024)
|
| 289 |
+
|
| 290 |
+
stream_generator = LlavaStreamGenerator
|
| 291 |
+
stop_token_ids = get_stop_token_ids(self.model_type, self.model_path)
|
| 292 |
+
image_token = get_image_token(model, self.model_path)
|
| 293 |
+
image_token_holder = (
|
| 294 |
+
tinychat.utils.constants.LLAVA_DEFAULT_IM_TOKEN_PLACE_HOLDER
|
| 295 |
+
)
|
| 296 |
+
prompt = prompt.replace(image_token_holder, image_token)
|
| 297 |
+
|
| 298 |
+
# print("=" * 50)
|
| 299 |
+
# print(prompt)
|
| 300 |
+
# print('=' * 50)
|
| 301 |
+
output_stream = stream_generator(
|
| 302 |
+
model,
|
| 303 |
+
tokenizer,
|
| 304 |
+
prompt,
|
| 305 |
+
gen_params,
|
| 306 |
+
device=model.device,
|
| 307 |
+
stop_token_ids=stop_token_ids,
|
| 308 |
+
image_tensor=images,
|
| 309 |
+
)
|
| 310 |
+
|
| 311 |
+
generated_text = ori_prompt
|
| 312 |
+
pre = 0
|
| 313 |
+
for outputs in output_stream:
|
| 314 |
+
output_text = outputs["text"]
|
| 315 |
+
output_text = output_text.strip().split(" ")
|
| 316 |
+
now = len(output_text) - 1
|
| 317 |
+
if now > pre:
|
| 318 |
+
generated_text += " ".join(output_text[pre:now]) + " "
|
| 319 |
+
yield json.dumps(
|
| 320 |
+
{"text": generated_text, "error_code": 0}
|
| 321 |
+
).encode() + b"\0"
|
| 322 |
+
pre = now
|
| 323 |
+
generated_text += " ".join(output_text[pre:])
|
| 324 |
+
yield json.dumps({"text": generated_text, "error_code": 0}).encode() + b"\0"
|
| 325 |
+
|
| 326 |
+
def generate_stream_gate(self, params):
|
| 327 |
+
try:
|
| 328 |
+
for x in self.generate_stream(params):
|
| 329 |
+
yield x
|
| 330 |
+
except ValueError as e:
|
| 331 |
+
print("Caught ValueError:", e)
|
| 332 |
+
ret = {
|
| 333 |
+
"text": server_error_msg,
|
| 334 |
+
"error_code": 1,
|
| 335 |
+
}
|
| 336 |
+
yield json.dumps(ret).encode() + b"\0"
|
| 337 |
+
except torch.cuda.CudaError as e:
|
| 338 |
+
print("Caught torch.cuda.CudaError:", e)
|
| 339 |
+
ret = {
|
| 340 |
+
"text": server_error_msg,
|
| 341 |
+
"error_code": 1,
|
| 342 |
+
}
|
| 343 |
+
yield json.dumps(ret).encode() + b"\0"
|
| 344 |
+
except Exception as e:
|
| 345 |
+
print("Caught Unknown Error", e)
|
| 346 |
+
ret = {
|
| 347 |
+
"text": server_error_msg,
|
| 348 |
+
"error_code": 1,
|
| 349 |
+
}
|
| 350 |
+
yield json.dumps(ret).encode() + b"\0"
|
| 351 |
+
|
| 352 |
+
|
| 353 |
+
app = FastAPI()
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
def release_model_semaphore(fn=None):
|
| 357 |
+
model_semaphore.release()
|
| 358 |
+
if fn is not None:
|
| 359 |
+
fn()
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
@app.post("/worker_generate_stream")
|
| 363 |
+
async def generate_stream(request: Request):
|
| 364 |
+
global model_semaphore, global_counter
|
| 365 |
+
global_counter += 1
|
| 366 |
+
params = await request.json()
|
| 367 |
+
|
| 368 |
+
if model_semaphore is None:
|
| 369 |
+
model_semaphore = asyncio.Semaphore(args.limit_model_concurrency)
|
| 370 |
+
await model_semaphore.acquire()
|
| 371 |
+
worker.send_heart_beat()
|
| 372 |
+
generator = worker.generate_stream_gate(params)
|
| 373 |
+
background_tasks = BackgroundTasks()
|
| 374 |
+
background_tasks.add_task(
|
| 375 |
+
partial(release_model_semaphore, fn=worker.send_heart_beat)
|
| 376 |
+
)
|
| 377 |
+
return StreamingResponse(generator, background=background_tasks)
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
@app.post("/worker_get_status")
|
| 381 |
+
async def get_status(request: Request):
|
| 382 |
+
return worker.get_status()
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
if __name__ == "__main__":
|
| 386 |
+
parser = argparse.ArgumentParser()
|
| 387 |
+
parser.add_argument("--host", type=str, default="localhost")
|
| 388 |
+
parser.add_argument("--port", type=int, default=21002)
|
| 389 |
+
parser.add_argument("--worker-address", type=str, default="http://localhost:21002")
|
| 390 |
+
parser.add_argument(
|
| 391 |
+
"--controller-address", type=str, default="http://localhost:21001"
|
| 392 |
+
)
|
| 393 |
+
parser.add_argument(
|
| 394 |
+
"--model-type",
|
| 395 |
+
type=str,
|
| 396 |
+
default="LLaMa",
|
| 397 |
+
help="type of the (base) language model",
|
| 398 |
+
)
|
| 399 |
+
parser.add_argument("--model-path", type=str, default="facebook/opt-350m")
|
| 400 |
+
parser.add_argument("--model-name", type=str)
|
| 401 |
+
parser.add_argument("--quant-path", type=str, default=None)
|
| 402 |
+
parser.add_argument("--precision", type=str, default="W4A16")
|
| 403 |
+
parser.add_argument("--device", type=str, default="cuda")
|
| 404 |
+
parser.add_argument(
|
| 405 |
+
"--multi-modal",
|
| 406 |
+
action="store_true",
|
| 407 |
+
help="Multimodal mode is automatically detected with model name, please make sure `llava` is included in the model path.",
|
| 408 |
+
)
|
| 409 |
+
parser.add_argument("--limit-model-concurrency", type=int, default=5)
|
| 410 |
+
parser.add_argument("--stream-interval", type=int, default=1)
|
| 411 |
+
parser.add_argument("--no-register", action="store_true")
|
| 412 |
+
|
| 413 |
+
args = parser.parse_args()
|
| 414 |
+
logger.info(f"args: {args}")
|
| 415 |
+
|
| 416 |
+
if args.multi_modal:
|
| 417 |
+
logger.warning(
|
| 418 |
+
"Multimodal mode is automatically detected with model name, please make sure `llava` is included in the model path."
|
| 419 |
+
)
|
| 420 |
+
|
| 421 |
+
worker = ModelWorker(
|
| 422 |
+
args.controller_address,
|
| 423 |
+
args.worker_address,
|
| 424 |
+
worker_id,
|
| 425 |
+
args.no_register,
|
| 426 |
+
args.model_type,
|
| 427 |
+
args.model_path,
|
| 428 |
+
args.model_name,
|
| 429 |
+
args.quant_path,
|
| 430 |
+
args.precision,
|
| 431 |
+
args.device,
|
| 432 |
+
)
|
| 433 |
+
uvicorn.run(app, host=args.host, port=args.port, log_level="info")
|