多模態(tài)模型(VLM)部署方法拋磚引玉
共 38283字,需瀏覽 77分鐘
·
2024-07-10 22:00
極市導(dǎo)讀
文章詳細討論了幾種多模態(tài)模型,如LLaVA、InternLM-XComposer2、QWen-VL等,并解釋了它們的架構(gòu)和訓(xùn)練流程。以及介紹了幾種多模態(tài)模型的部署框架,包括TensorRT-LLM、lmdeploy和vLLM,并討論了它們對VLM模型的支持程度。 >>加入極市CV技術(shù)交流群,走在計算機視覺的最前沿
去年年初LLM剛起步的時候,大模型的部署方案還不是很成熟,如今僅僅過了一年多,LLM部署方案已經(jīng)遍地都是了。
而多模態(tài)模型相比大語言模型來說,發(fā)展的還沒有很“特別”成熟,不過由于兩者結(jié)構(gòu)很相似,LLMs的經(jīng)驗還是可以很好地利用到VLMs中。
本篇文章中提到的多模態(tài)指的是視覺多模態(tài),即VLM(Vision Language Models)。
以下用一張圖展示下簡單多模態(tài)模型的運行流程:
-
Text Embeddings即文本輸入,就是常見LLM中的輸入; -
而Multomode projector則是多模態(tài)模型額外一個模態(tài)的輸入,這里指的是視覺輸入信息,當然是轉(zhuǎn)換維度之后的;
將這個轉(zhuǎn)換維度之后的視覺特征和Text Embeddings執(zhí)行concat操作合并起來,輸入decoder中(例如llama)就完成推理流程了;
Multomode projector負責(zé)將原始的圖像特征轉(zhuǎn)換下維度,輸出轉(zhuǎn)換后的圖像特征;所以有個中文叫投射層,這個名字有點抽象,理解就行,其實就是個mlp層,轉(zhuǎn)換下視覺特征的維度
引入VLM
VLM就是視覺encoder加上語言decoder,這么說可能有點抽象,我們先簡單回顧下transformer的基本結(jié)構(gòu):
由編碼器和解碼器組成,最開始的transformer主要是處理機器翻譯任務(wù),結(jié)構(gòu)是encoder+decoder。除了這個結(jié)構(gòu),還有一些其他結(jié)構(gòu)適用于各種任務(wù),比如
-
Encoder-only models:適用于需要理解輸入的任務(wù),例如句子分類和命名實體識別。 -
Decoder-only models:適用于生成性任務(wù),如文本生成。 -
Encoder-decoder models or sequence-to-sequence models:適用于需要輸入的生成性任務(wù),例如翻譯或摘要。
我們常見的llama屬于Decoder-only models,只有decoder層;bert屬于encoder-only;T5屬于encoder-decoder。llama不多說了,BERT 是一種僅包含編碼器的模型,它通過學(xué)習(xí)雙向的上下文來更好地理解語言的深層含義。
BERT 通常用于理解任務(wù),如情感分析、命名實體識別、問題回答等。T5 模型包括編碼器和解碼器,適用于同時需要理解和生成語言的任務(wù)。T5 被設(shè)計來處理各種“文本到文本”的任務(wù),比如機器翻譯、文摘生成、文本分類等。這種結(jié)構(gòu)允許模型首先通過編碼器理解輸入文本,然后通過解碼器生成相應(yīng)的輸出文本?;A(chǔ)知識就過到這里,我們先說enc-dec結(jié)構(gòu)。
Encoder-Decoder (Enc-Dec)
注意,Enc-Dec模型需要區(qū)別于多模態(tài)模型,目前TensorRT-LLM官方支持的enc-dec模型如下:
-
T5[1] -
T5v1.1[2] and Flan-T5[3] -
mT5[4] -
BART[5] -
mBART[6] -
FairSeq NMT[7] -
ByT5[8]
第一個就是T5也就是上述提到的編碼器+解碼器結(jié)構(gòu)。需要注意這些模型都是基于Transformer架構(gòu)的自然語言處理(NLP)模型,區(qū)別于多模態(tài)模型,這些模型主要是處理文本,算是單模態(tài)。
VLM or multimodal
而多模態(tài)除了本文,就帶上了圖像:
VILA[9]是nvidia推出的一個視覺語言模型,上圖很清楚地介紹了其推理和訓(xùn)練流程。我知道的一些多模態(tài)模型有(這些模型來自lmdeploy官方github介紹):
-
LLaVA(1.5,1.6) (7B-34B) -
InternLM-XComposer2 (7B, 4khd-7B) -
QWen-VL (7B) -
DeepSeek-VL (7B) -
InternVL-Chat (v1.1-v1.5) -
MiniGeminiLlama (7B) -
CogVLM-Chat (17B) -
CogVLM2-Chat (19B) -
MiniCPM-Llama3-V-2_5
這里我只簡單介紹下llava,畢竟llava是較早的做多模態(tài)的模型,之后很多多模態(tài)的架構(gòu)和llava基本都差不多。剩下的多模態(tài)模型大家可以自行查閱,結(jié)構(gòu)基本都類似。
LLAVA
LLaVA\(2304\)[10]作為VLMs的代表性工作,號稱只需要幾個小時的訓(xùn)練,即可讓一個LLM轉(zhuǎn)變?yōu)閂LM:
訓(xùn)練方式比較簡單,主要操作是將視覺圖像視作一種“外語”(相比于之前純nlp,圖像可以當做額外輸入的外語),利用vision-encoder和projection[11]將“圖像翻譯成文本信號”,并微調(diào)LLM從而可以適應(yīng)圖像任務(wù),我們簡單看下其推理代碼:
其中processor就是圖像預(yù)處理,處理后得到的input為:inputs.pixel_values,其shape是torch.Size([1, 3, 336, 336])。然后進入generate階段:
第一步(stage-1)是執(zhí)行encoder部分:
-
input_ids(torch.Size([1, 17])) -> input_embeds(torch.Size([1, 17, 4096])) -
pixel_values(torch.Size([1, 3, 336, 336]))經(jīng)過視覺模型 輸出視覺特征(torch.Size([1, 576, 1024])) -
視覺特征經(jīng)過投影層(mlp)輸出最終的圖像特征(torch.Size([1, 576, 4096]))
第二步(stage-2)是執(zhí)行文字embed和圖像embed合并過程,也就是合并inputs_embeds + image_features,最終得到的inputs_embeds維度為torch.Size([1, 592, 4096]),具體在llava中是pre_prompt + image + post_prompt一起輸入進來,其中image的特征替換prompt中的標記,對應(yīng)的token id為32000。
而這個_merge_input_ids_with_image_features函數(shù)的作用主要是將image_features和inputs_embeds合并起來:在合并完兩者之后,其余部分就和普通的decoder基本一致了。整體運行流程如下:
其他多模態(tài)模型拼輸入的方式和llava類似,比如InternVLChat[12],輸入的prompt是這樣的:
轉(zhuǎn)換為token_ids如下,其圖像特征占位,img_context_token_id是92546:
上述prompt中的<IMG_CONTEXT>之后會被實際的圖像特征填上,目前只是占個位子。運行過程中的維度變化:
-
pixel_values.shape torch.Size([1, 3, 224, 224]) -> vision model -> torch.Size([1, 64, 2048]) -
input_ids torch.Size([1, 293]) -> language model embed -> torch.Size([1, 293, 2048])
核心代碼如下,需要注意這里是先占位再填(input_embeds[selected]=...)的形式,不是llava中concat的形式,最終實現(xiàn)效果是一樣的。
最終也是將合并后的embeds送入language-decoder中。
包含cross-attention的多模態(tài)
上述介紹的多模態(tài)模型都是文本輸入和圖像輸入轉(zhuǎn)換為embeds合并后,輸入language decoder中,這個decoder可以是常見的decoder-only models,比如llama。
還有些特殊的多模態(tài)模型的decoder部分會有cross-attention結(jié)構(gòu),比如meta推出的nougat[13]。其視覺特征不會和input_ids合并,而是單獨輸入decoder,在decoder中和文本特征進行cross attention:
這種結(jié)構(gòu)相對稍微復(fù)雜一些。
部署方案
部署方案的話,我們參考主流開源(TensorRT-LLM不完全開源)框架來窺探下。雖然一些大廠有自己的LLM部署方案,不過技術(shù)其實也多是“借鑒”,大同小異。這幾個LLM框架應(yīng)該是用的比較多的:
-
TensorRT-LLM -
vLLM -
lmdeploy
目前這三個框架都支持VLM模型,只不過支持程度不同,我們先看trt-llm。
TensorRT-LLM
trt-llm中多模態(tài)部分在Multimodal示例中,如果是跑demo或者實際中使用trt-llm的python session跑的話,直接看官方的example即可:
如果我們更進一步,想要部署在生產(chǎn)環(huán)境,也是可以搞的。如果我們想要使用triton inference server部署的話,TensorRT-LLM對多模態(tài)模型的支持限于將cv-encoder和decoder分離開,搞成兩個模型服務(wù)。
即通過ensemble或者tensorrt_llm_bls(python backend)的方式串起來,整體運行流程和在普通模型中是一致的(先輸入image和text,然后兩者經(jīng)過tokenizer轉(zhuǎn)換為token id,最終拼接和encoder輸出特征圖一并傳入decoder中)。
視覺端
視覺端模型轉(zhuǎn)換和普通CV模型一致,可以通過onnx的方式或者直接通過trt-python-api搭建。
以llava舉例子,在TensorRT-LLM中,首先把視覺模型(encoder)使用Wrapper類套一層,其中:
-
self.vision_tower是視覺模型, -
multi_modal_projector是投影層,將特征維度轉(zhuǎn)換為和input_embeds相匹配的維度:
轉(zhuǎn)換好之后,模型信息如下:
[I] ==== TensorRT Engine ====
Name: Unnamed Network 0 | Explicit Batch Engine
---- 1 Engine Input(s) ----
{input [dtype=float16, shape=(-1, 3, 336, 336)]}
---- 1 Engine Output(s) ----
{output [dtype=float16, shape=(-1, 576, 4096)]}
---- Memory ----
Device Memory: 56644608 bytes
---- 1 Profile(s) (2 Tensor(s) Each) ----
- Profile: 0
Tensor: input (Input), Index: 0 | Shapes: min=(1, 3, 336, 336), opt=(2, 3, 336, 336), max=(4, 3, 336, 336)
Tensor: output (Output), Index: 1 | Shape: (-1, 576, 4096)
---- 398 Layer(s) ----
語言端
decoder端需要額外加入一個合并input_ids 和prompt_table_data的embedding層(這里稱為PromptTuningEmbedding),其余的和普通llama一致:
通過設(shè)置 use_prompt_tuning來確定該decoder是否需要額外的 prompt_tuning 輸入:
class LLaMAModel(Module):
def __init__(self,
num_layers,
num_heads,
...# 省略參數(shù)
use_prompt_tuning: bool = False, # !!!
enable_pos_shift=False,
dense_context_fmha=False,
max_lora_rank=None):
super().__init__()
self.mapping = mapping
self.use_prompt_tuning = use_prompt_tuning
EmbeddingCls = PromptTuningEmbedding if use_prompt_tuning else Embedding
其中PromptTuningEmbedding的forward代碼如下,這個使用trt-python-api搭出來的layer主要作用就是將input_ids和視覺特征prompt_embedding_table進行embed并且concat,和上述一開始提到的concat流程大差不差:
# PromptTuningEmbedding
def forward(self, tokens, prompt_embedding_table, tasks, task_vocab_size):
# do not use ">=" because internally the layer works with floating points
prompt_tokens_mask = tokens > (self.vocab_size - 1)
# clip tokens in the [0, vocab_size) range
normal_tokens = where(prompt_tokens_mask, self.vocab_size - 1, tokens)
normal_embeddings = embedding(normal_tokens, self.weight.value,
self.tp_size, self.tp_group,
self.sharding_dim, self.tp_rank)
# put virtual tokens in the [0, max_prompt_vocab_size) range
prompt_tokens = where(prompt_tokens_mask, tokens - self.vocab_size, 0)
# add offsets to match the concatenated embedding tables
tasks = tasks * task_vocab_size
# tasks: [batch_size, seq_len]
# prompt_tokens: [batch_size, seq_len]
prompt_tokens = prompt_tokens + tasks
prompt_embeddings = embedding(prompt_tokens, prompt_embedding_table)
# prompt_tokens_mask: [batch_size, seq_len] -> [batch_size, seq_len, 1]
# combine the correct sources of embedding: normal/prompt
return where(unsqueeze(prompt_tokens_mask, -1), prompt_embeddings,
normal_embeddings)
服務(wù)整合
服務(wù)整合其實很容易,只不過官方一開始并沒有給出示例,只有在最近才在tutorial中給出實際例子:
-
https://github.com/triton-inference-server/tutorials/blob/main/Popular\_Models\_Guide/Llava1.5/llava\_trtllm\_guide.md[14]
其實我們可以很早發(fā)現(xiàn)tensorrt_llm/config.pbtxt中已經(jīng)包含了這兩個輸入:
-
prompt_embedding_table -
prompt_vocab_size
意味著在trt-llm的executor中包裝著decoder-engine,可以接受這兩個輸入。而trt-llm提供的executor,在triton-trt-llm-backend去通過調(diào)用這個API實現(xiàn)inflight batching:
# inflight_batcher_llm/tensorrt_llm/config.pbtxt
{
name: "prompt_embedding_table"
data_type: TYPE_FP16
dims: [ -1, -1 ]
optional: true
allow_ragged_batch: true
},
{
name: "prompt_vocab_size"
data_type: TYPE_INT32
dims: [ 1 ]
reshape: { shape: [ ] }
optional: true
},
而之前介紹的llava的decoder結(jié)構(gòu),則可以看到有prompt_embedding_table和prompt_vocab_size這兩個輸入:
[03/04/2024-03:23:39] [TRT-LLM] [I] Engine:name=Unnamed Network 0, refittable=False, num_layers=495, device_memory_size=1291851264, nb_profiles=1
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=input_ids, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=position_ids, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=prompt_embedding_table, mode=TensorIOMode.INPUT, shape=(-1, 4096), dtype=DataType.HALF, tformat=Row major linear FP16 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=tasks, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=prompt_vocab_size, mode=TensorIOMode.INPUT, shape=(1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=last_token_ids, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=kv_cache_block_offsets, mode=TensorIOMode.INPUT, shape=(-1, 2, -1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_kv_cache_block_offsets, mode=TensorIOMode.INPUT, shape=(-1, 2, -1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_kv_cache_pool_pointers, mode=TensorIOMode.INPUT, shape=(2,), dtype=DataType.INT64, tformat=Row major linear INT8 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=sequence_length, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_request_types, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_past_key_value_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=context_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_context_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_max_attention_window_sizes, mode=TensorIOMode.INPUT, shape=(32,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=host_sink_token_length, mode=TensorIOMode.INPUT, shape=(1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=cache_indirection, mode=TensorIOMode.INPUT, shape=(-1, 1, -1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[03/04/2024-03:23:39] [TRT-LLM] [I] Tensor:name=logits, mode=TensorIOMode.OUTPUT, shape=(-1, 32064), dtype=DataType.FLOAT, tformat=Row major linear FP32 format (kLINEAR)
這是普通decoder-only server中的服務(wù)文件組織:
而最終整體的目錄文件,我們再多一個encoder文件夾就行,具體我們可以在preprocessing中調(diào)用encoder,然后去處理prompt合并,最終通過prompt_embedding_table和prompt_vocab_size送入tensorrt_llm中即可。不過需要注意,trt-llm不支持cross attention這種多模態(tài)的inflight batching,因為在config.pbtxt沒有暴露出相關(guān)的接口,cross attention這種多模態(tài)不需要prompt_embedding_table和prompt_vocab_size,而是需要類似于encoder_output這種原始的圖像特征輸入,以下是官方nougat模型decoder部分轉(zhuǎn)為trt的結(jié)構(gòu):
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=input_ids, mode=TensorIOMode.INPUT, shape=(-1, 1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=position_ids, mode=TensorIOMode.INPUT, shape=(-1, 1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=encoder_input_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=encoder_max_input_length, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=encoder_output, mode=TensorIOMode.INPUT, shape=(-1, -1, 1024), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=host_past_key_value_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=sequence_length, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=context_lengths, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=host_request_types, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=last_token_ids, mode=TensorIOMode.INPUT, shape=(-1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cache_indirection, mode=TensorIOMode.INPUT, shape=(-1, 1, -1), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=host_max_attention_window_sizes, mode=TensorIOMode.INPUT, shape=(10,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=host_sink_token_length, mode=TensorIOMode.INPUT, shape=(1,), dtype=DataType.INT32, tformat=Row major linear INT32 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_0, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_0, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_1, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_1, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_2, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_2, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_3, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_3, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_4, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_4, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_5, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_5, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_6, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_6, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_7, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_7, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_8, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_8, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=past_key_value_9, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_past_key_value_9, mode=TensorIOMode.INPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_kv_cache_gen, mode=TensorIOMode.INPUT, shape=(1,), dtype=DataType.BOOL, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_0, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_0, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_1, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_1, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_2, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_2, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_3, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_3, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_4, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_4, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_5, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_5, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_6, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_6, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_7, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_7, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_8, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_8, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=present_key_value_9, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=cross_present_key_value_9, mode=TensorIOMode.OUTPUT, shape=(-1, 2, 16, -1, 64), dtype=DataType.BF16, tformat=Row major linear INT8 format (kLINEAR)
[04/23/2024-03:56:36] [TRT-LLM] [I] Tensor:name=logits, mode=TensorIOMode.OUTPUT, shape=(-1, 50000), dtype=DataType.FLOAT, tformat=Row major linear FP32 format (kLINEAR)
lmdeploy
lmdeploy對多模態(tài)的支持方式也在預(yù)料之中,decoder使用自家的turbomind或者pytorch engine去跑,然后cv-encoder端復(fù)用原始transformers庫中的代碼去跑,整理流程和trt-llm中的相似。
vLLM
vllm對多模態(tài)模型的支持尚可,官方展示的不是很多,但實際上支持了不少(https://github.com/vllm-project/vllm/issues/4194[15]);而且鑒于vllm的易接入性,自己增加模型還是比較簡單的:
另外,vLLM近期也在修改相關(guān)VLM的架構(gòu),正在進行重構(gòu)[16],以及vllm對vlm的一些后續(xù)優(yōu)化:
-
Make VLMs work with chunked prefill -
Unify tokenizer & multi-modal processor (so that we can leverage AutoProcessor from transformers) -
Prefix caching for images -
Streaming inputs of multi-modal data
簡單測試了llava,測試性能在同樣fp16的情況下性能不如trt-llm,原因表現(xiàn)可以參考在llama上的對比。
優(yōu)化點TODO
優(yōu)化點其實有很多,不過占大頭的就是量化[17]。
量化
因為多模態(tài)的decoder部分就是普通的decode模型,我們可以復(fù)用現(xiàn)有的成熟的量化技術(shù)量化decoder部分就可以拿到很大的收益。整個pipeline當中視覺encoder部分的耗時占比一般都很小5%左右,encoder部分可以按照我們平常的小模型的方式去優(yōu)化即可,量化也可以上。需要注意的就是量化方法,大模型量化方法很多,需要選對。
The AWQ quantization algorithm is suitable for multi-modal applications since AWQ does not require backpropagation or reconstruction, while GPTQ does. Thus, it has better generalization ability to new modalities and does not overfit to a specific calibration set. We only quantized the language part of the model as it dominates the model size and inference latency. The visual part takes less than 4% of the latency. AWQ outperforms existing methods (RTN, GPTQ) under zero-shot and various few-shot settings, demonstrating the generality of different modalities and in-context learning workloads.
Runtime
還有cv-encoder需要和decoder最好放到一個runtime當中,要不然會有一些冗余的顯存拷貝,不過這部分對吞吐影響不大,主要是latency。trt-llm中對enc-dec結(jié)構(gòu)已經(jīng)做了這樣的優(yōu)化,在trt-llm-0607版本中將這倆放到了同一個runtime中,這里指的是Executor,可以看到多出了一個encoder的model path:
可以看到triton-llm-backend中多了一個encoder_model_path的輸入,這里將encoder和decoder放到同一個runtime中了:
還有一些其他的優(yōu)化空間,這里暫時不談了,后續(xù)有新的結(jié)論了再補充。
這里的討論還不是很全很細,算是拋磚引玉,之后有更新也會再發(fā)篇文章單獨介紹。各位讀者如果有比較好的方法或者建議也歡迎留言,我們一起討論。
參考
-
https://github.com/triton-inference-server/tutorials/pull/100[18] -
https://huggingface.co/blog/vlms[19] -
https://github.com/InternLM/lmdeploy/issues/1309[20] -
https://developer.nvidia.com/blog/visual-language-intelligence-and-edge-ai-2-0/[21]
T5: https://huggingface.co/docs/transformers/main/en/model_doc/t5
[2]T5v1.1: https://huggingface.co/docs/transformers/model_doc/t5v1.1
[3]Flan-T5: https://huggingface.co/docs/transformers/model_doc/flan-t5
[4]mT5: https://huggingface.co/docs/transformers/model_doc/mt5
[5]BART: https://huggingface.co/docs/transformers/model_doc/bart
[6]mBART: https://huggingface.co/docs/transformers/model_doc/mbart
[7]FairSeq NMT: https://pytorch.org/hub/pytorch_fairseq_translation/
[8]ByT5: https://huggingface.co/docs/transformers/main/en/model_doc/byt5
[9]VILA: https://developer.nvidia.com/blog/visual-language-models-on-nvidia-hardware-with-vila/
[10]LLaVA(2304): https://link.zhihu.com/?target=https%3A//arxiv.org/abs/2304.08485
[11]projection: https://www.zhihu.com/search?q=projection&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22article%22%2C%22sourceId%22%3A%22702811733%22%7D
[12]InternVLChat: https://huggingface.co/OpenGVLab/InternVL-Chat-V1-5
[13]nougat: https://github.com/facebookresearch/nougat
[14]https://github.com/triton-inference-server/tutorials/blob/main/Popular_Models_Guide/Llava1.5/llava_trtllm_guide.md: https://github.com/triton-inference-server/tutorials/blob/main/Popular_Models_Guide/Llava1.5/llava_trtllm_guide.md
[15]https://github.com/vllm-project/vllm/issues/4194: https://github.com/vllm-project/vllm/issues/4194
[16]重構(gòu): https://github.com/vllm-project/vllm/issues/4194
[17]undefined: undefined
[18]https://github.com/triton-inference-server/tutorials/pull/100: https://github.com/triton-inference-server/tutorials/pull/100
[19]https://huggingface.co/blog/vlms: https://huggingface.co/blog/vlms
[20]https://github.com/InternLM/lmdeploy/issues/1309: https://github.com/InternLM/lmdeploy/issues/1309
[21]https://developer.nvidia.com/blog/visual-language-intelligence-and-edge-ai-2-0/: https://developer.nvidia.com/blog/visual-language-intelligence-and-edge-ai-2-0/
公眾號后臺回復(fù)“數(shù)據(jù)集”獲取100+深度學(xué)習(xí)各方向資源整理
極市干貨
點擊閱讀原文進入CV社區(qū)
收獲更多技術(shù)干貨
