chen459664 commited on
Commit
11690a5
·
verified ·
1 Parent(s): 075eaa3

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. llm-awq/scripts/DeepSeek_R1_Distill_example.sh +25 -0
  2. llm-awq/scripts/llava_example.sh +12 -0
  3. llm-awq/scripts/nvila_example.sh +12 -0
  4. llm-awq/scripts/vila15_example.sh +14 -0
  5. llm-awq/scripts/vila_example.sh +12 -0
  6. llm-awq/tinychat/README.md +526 -0
  7. llm-awq/tinychat/internvl_demo.py +270 -0
  8. llm-awq/tinychat/models/__init__.py +10 -0
  9. llm-awq/tinychat/models/falcon.py +304 -0
  10. llm-awq/tinychat/models/internvl/configuration_internvl.py +204 -0
  11. llm-awq/tinychat/models/internvl/conversation.py +391 -0
  12. llm-awq/tinychat/models/internvl/internvit.py +425 -0
  13. llm-awq/tinychat/models/internvl/media.py +113 -0
  14. llm-awq/tinychat/models/internvl3.py +383 -0
  15. llm-awq/tinychat/models/llama.py +413 -0
  16. llm-awq/tinychat/models/llava_base/llava_arch.py +412 -0
  17. llm-awq/tinychat/models/llava_base/multimodal_encoder/builder.py +21 -0
  18. llm-awq/tinychat/models/llava_base/multimodal_encoder/clip_encoder.py +97 -0
  19. llm-awq/tinychat/models/llava_base/multimodal_projector/builder.py +72 -0
  20. llm-awq/tinychat/models/llava_llama.py +282 -0
  21. llm-awq/tinychat/models/mpt.py +304 -0
  22. llm-awq/tinychat/models/nvila/builder.py +291 -0
  23. llm-awq/tinychat/models/nvila/configuration_llava.py +89 -0
  24. llm-awq/tinychat/models/nvila/llava_arch.py +909 -0
  25. llm-awq/tinychat/models/nvila_qwen2.py +157 -0
  26. llm-awq/tinychat/models/qwen2.py +511 -0
  27. llm-awq/tinychat/models/vila_llama.py +109 -0
  28. llm-awq/tinychat/modules/__init__.py +9 -0
  29. llm-awq/tinychat/modules/fused_attn.py +634 -0
  30. llm-awq/tinychat/modules/fused_internencoder.py +237 -0
  31. llm-awq/tinychat/modules/fused_mlp.py +101 -0
  32. llm-awq/tinychat/modules/fused_norm.py +46 -0
  33. llm-awq/tinychat/modules/fused_siglipdecoder.py +282 -0
  34. llm-awq/tinychat/modules/fused_vision_attn.py +272 -0
  35. llm-awq/tinychat/nvila_benchmark.py +163 -0
  36. llm-awq/tinychat/nvila_demo.py +272 -0
  37. llm-awq/tinychat/offline-weight-repacker.py +157 -0
  38. llm-awq/tinychat/scripts/internvl_demo.sh +18 -0
  39. llm-awq/tinychat/scripts/llama2_demo.sh +31 -0
  40. llm-awq/tinychat/scripts/nvila_demo.sh +22 -0
  41. llm-awq/tinychat/serve/README.md +27 -0
  42. llm-awq/tinychat/serve/controller.py +325 -0
  43. llm-awq/tinychat/serve/examples/CPR.jpg +0 -0
  44. llm-awq/tinychat/serve/examples/icl-logo/adobe.jpg +0 -0
  45. llm-awq/tinychat/serve/examples/icl-logo/apple.jpg +0 -0
  46. llm-awq/tinychat/serve/examples/icl-logo/google.webp +0 -0
  47. llm-awq/tinychat/serve/examples/icl-logo/nvidia.png +0 -0
  48. llm-awq/tinychat/serve/gradio_web_server.py +1200 -0
  49. llm-awq/tinychat/serve/llava_conv.py +454 -0
  50. 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
+ ![TinyChat with LLaMA-3-8b on RTX 4090: W4A16 is 2.7x faster than FP16](./figures/4090_example_new.gif)
43
+
44
+ * TinyChat with LLaMA-3-8b on Jetson Orin (2.9x faster than FP16):
45
+
46
+ ![TinyChat with LLaMA-3-8b on Jetson Orin: W4A16 is 2.9x faster than FP16](./figures/orin_example_new.gif)
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
+ ![TinyChat with NVILA on 4090 single image](./figures/4090_nvila_single.gif)
53
+
54
+ * TinyChat with NVILA-8B on RTX 4090 (multi-image inputs):
55
+
56
+ ![TinyChat with NVILA on 4090 multiple images](./figures/4090_nvila_multi.gif)
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")