聊聊VLM架構以及訓練後的一些實驗和思考

MLNLP社群是國內外知名的機器學習與自然語言處理社群,受眾覆蓋國內外NLP碩博生、高校老師以及企業研究人員。
社群的願景是促進國內外自然語言處理,機器學習學術界、產業界和廣大愛好者之間的交流和進步,特別是初學者同學們的進步。
來源 | 知乎
作者|Jason

總覽

多模態大模型(Vision Large Models)主要是能感知多模態輸入(目前主要是圖片和影片)併產出語言輸出(也有直接的多模態輸出架構)。目前常見的VLM架構有兩種,
  • • Type A: Visual Encoder -> Cross Modality Connector -> LLM
  • • Type B: VQ-VAE -> Transformer
第一種能夠利用到單模態訓練時候的語言能力,具有計算資源需求少,且能達到較好效果的程度;缺點是基本只能語言模態輸出,無法輸出多模態內容;
第二種能夠直接把圖片模態和文字模態進行tokenization,在decode的過程中可以直接產出多模態輸出,但是VQ-VAE的訓練難度比較大,基本需要從頭訓練,成本較高,目前處於更前期的探索階段。

主要難點

目前,Typa A的主要難點是
  • • 如何處理不同解析度的圖片;因為大部分採用了Vision Encoder架構的模型使用的Vision Encoder都是ViT,ViT能處理的解析度是224×224。
  • • 如何在一次生成中引入更多的圖片;因為該架構會使用ViT的最後一層作為feature,大小一般是576或者768。這個feature的長度會給後面LLM模組帶來較大的壓力。
  • • 如何對影片進行建模;影片同時有稠密幀資料和temporal feature,如何能達到平衡是一個難題。

常規解法

處理解析度

現在的多模態模型的technical report都會花較長的篇幅介紹自己如何對圖片進行操作,下面介紹幾種典型操作。
  • • 對圖片進行downsampling或者upsampling;
  • • scale then padding。
MiniCPM-V:根據ViT預訓練時候的解析度,選擇一個最佳的比例給原圖進行切片,把所有切片加上縮圖平鋪輸入到SigLip。如果是多圖的話,每張圖會分別形成一個list。實際訓練的時候,每張圖會用換行符隔開。Resampler的意思很直白,就是用cross attention模組,再對SigLip的特徵進行壓縮,提取出來壓縮後的特徵。

LLaVA onevision的圖片處理策略:
  • • ①單張圖片,切分成a\times b大小的slices,每個slice都是SigLip能處理的大小,外加上一張resize後的整體的縮圖。
  • • ②多張圖片,每張圖片直接按照SigLip的解析度resize,然後把多圖直接給SigLip處理。
  • • ③影片,SigLip解析度resize之後,再經過BiLinear Interpolation,得到更小長度的embedding,拼接後輸入到LLM中。
其實PiL庫的resize function預設就是BiLinear Interpolation,作者就是直接resize了。

Qwen2-VL :認為上述兩個方法都需要把圖片解析度適配到預訓練的CLIP模型的解析度,在高解析度圖片,尤其是OCR的場景表現較差,因此他們需要針對解析度動態調整一張圖片佔用的tokens數量。所以他們對ViT的結構進行了魔改,把原來的ViT的positional embedding拿走換了個2D RoPE作為positional encoding的方法,同時加了一層MLP把22個對映成了1個token,已達到壓縮圖片的目的。
這個做法看上去比較合理,但天下沒有免費午餐,這樣原來訓練好ViT模型也喪失了一定的能力。後續訓練也需要對原ViT模型有一定重新訓練。但比較有意思的是,有其他文章1: Molmo & PixMo.驗證了Qwen2-VL在所有學術Benchmark上表現比較好,但是在human eval的時候表現非常不一致。

2D RoPE的程式碼實現:
 def forward(self, x, position_ids):   if "dynamic" in self.rope_type:       self._dynamic_frequency_update(position_ids, device=x.device)   # Core RoPE block. In contrast to other models, Qwen2_VL has different position ids for thw grids   # So we expand the inv_freq to shape (3, ...)   inv_freq_expanded = self.inv_freq[None, None, :, None].float().expand(3, position_ids.shape[1], -1, 1)   position_ids_expanded = position_ids[:, :, None, :].float()  # shape (3, bs, 1, positions)   # Force float32 (see https://github.com/huggingface/transformers/pull/29285)   device_type = x.device.type   device_type = device_type if isinstance(device_type, str) and device_type != "mps" else "cpu"   with torch.autocast(device_type=device_type, enabled=False):       freqs = (inv_freq_expanded.float() @ position_ids_expanded.float()).transpose(2, 3)       emb = torch.cat((freqs, freqs), dim=-1)       cos = emb.cos()       sin = emb.sin()   # Advanced RoPE types (e.g. yarn) apply a post-processing scaling factor, equivalent to scaling attention   cos = cos * self.attention_scaling   sin = sin * self.attention_scaling   return cos.to(dtype=x.dtype), sin.to(dtype=x.dtype)

效果如何

先說結論,從使用角度來說,如果用來在自有任務上做SFT,

MiniCPM-V > LLaVA onevision > Qwen2 VL

這幾個模型都是基於Qwen2的LM,所以這個結論比較起來也有一定說服力。但是,這個只是我們自己訓練得到的結論,實際上會根據任務要求和資料型別的不同,結果會有區別。個人建議,如果需要早期確定一個模型是否在某個任務上能夠取得還不錯的效果,可以自己構建一個較小的,資料分佈和預訓練資料有一定交叉,且和自有任務有一定交叉的驗證集(實在不行就試試模型的caption能力)。每個模型訓練之前拿來跑下這個Benchmark,比直接相信技術報告裡面的結果要好一些。

為什麼需要處理解析度

現在業界普遍相信,對於偏向OCR的任務,需要高解析度的輸入,而其他任務並不一定需要高解析度圖片。從模型整體效果來看,有一篇不錯的文章可供參考。
LLaVA-next的2024.6月的blog[1]中,作者在projector這條技術路徑上進行了探索,對於Type-A模型來說,影響模型能力的是什麼,先說結論
  • • 提升LLM的能力比提升Image Encoder和提升解析度都更有用;
  • • Vision Encoder的大小和解析度提升都對結果有提升,但是提升解析度對效果的提升更多;
  • • 訓練時要引入一個訓練階段,這個階段需要全引數訓練+高質量資料
其中結論2對解析度的ablation表格:

可以看到,在相同Vision Encoder下,解析度的提升對效果都是有正向影響的。但細看這個表格,並非所有Benchmark都有提升,比如ChartQA和DocVQA的提升巨大,而MME,MMMU等甚至還有下降。前者任務型別更偏向OCR,而後者則是包含了推理和影像描述等任務的。所以callback一下開頭說的,如果你的任務是OCR為主,那麼先無腦提升解析度再說;如果和OCR不相關,解析度的影響可能不大。

視覺表徵(Vision Representations)

不同解析度的圖片對映到解析度就是Vision Encoder預訓練的解析度,Vision Encoder會把圖片對映到到一個一維長度的Image representation。一般來說,大家會使用到Vision Transformers架構的模型的最後一層拿掉CLS token。ViT的架構可以參考我的另一篇筆記,簡單recap一下,就是在下圖的MLP Head之前的一層layer。

對於Type-A模型,MiniCPM和LLaVA使用的是 SigLIP SoViT-400m/14,Qwen2-VL使用的是DFN,這些模型都是基於CLIP的方法訓練得到的ViT模型,只是訓練的loss不同或者基於的初始資料集不同。這裡需要重點關注,不同架構是將ViT的Output輸出給LLM的。
  • • MiniCPM-V:基於Resampler的方法;
  • • LLaVA和Qwen2-VL:基於projector的方法;
Resampler:利用Cross attention模組對原representation進行resample,提取更短長度的特徵,這種方法好處是resample後的特徵長度比較好定製,可以用較少的token表徵一個slice或者圖片。一般來說,這個Resampler會把Query設定為可學習的引數,Key和value是經過ViT模型的原特徵。
以MiniCPM-V的Resampler程式碼為例,看forward函式
 class Resampler(nn.Module):     """     A 2D perceiver-resampler network with one cross attention layers by        given learnable queries and 2d sincos pos_emb     Outputs:         A tensor with the shape of (batch_size, num_queries, embed_dim)     """     ...     def __init__(self,):       # 初始化為可訓練引數       self.query = nn.Parameter(torch.zeros(self.num_queries, embed_dim))     def forward(self, x, tgt_sizes=None):         pos_embed = ...         x = self.kv_proj(x)  # B * L * D         x = self.ln_kv(x).permute(1, 0, 2)  # L * B * D         q = self.ln_q(self.query)  # Q * D         out = self.attn(             self._repeat(q, bs),  # Q * B * D query             x + pos_embed,  # L * B * D +  L * B * D key             x, value             key_padding_mask=key_padding_mask)[0]         #  out: Q * B * D         x = out.permute(1, 0, 2)  # B * Q * D         x = self.ln_post(x)         x = x @ self.proj         return x
基於projector的方法:以LLaVA為例,LLaVA用一個兩層的MLP直接將representation投射成長度h的n維的embedding。然後直接和文字的input embedding拼接在一起後進行LM的後續Transformers block。

效果如何

先說結論,這個和任務強相關,如果對於每張圖片的解析度要求不高,但是對圖片數量(多圖推理任務)有要求的,那麼Resampler模型效果好;否則效果差不多。LLaVA的projector方法勝在結構簡單,訓練方便。實際上LLaVA在提出這個架構的時候,就明確表達了自己想做Data-centric,用最快的方法驗證構造的資料是否有效。但從可定製化角度,更復雜任務的角度看,一個簡單的projector還是略顯力不從心。

訓練過程

先說結論:
  • • 實際下游任務微調中,資料量不多的情況下,SFT只訓練LLM效果是最好的。其他微調方法效果都會更差
  • • 適配下游應用時,大部分試圖創新模型結構或訓練方法來提升效果的努力都是徒勞的(搞大模型的人太多了,你能想到的方法早就被人試過無數遍了,這些方法之所以沒出現在論文裡,是因為這些方法沒用),除非你訓練資料非常多,且訓練過程的把控很好
  • • 模型效果提升沒有Silver Bullet,全是搞資料和煉丹的苦工
模態對齊簡單而言就是讓訓練好的LLM能夠認識Image features,並賦予該feature特定的含義。比較直白的想法是,既然ViT和LLM都是訓練好的,那麼他們的Output就本來具有特定的意義,因此,對齊的過程往往就是單獨訓練對齊模組,比如Resampler或者projector。
現在的多模態訓練一般會分成幾個階段,第一階段用來Warm-up視覺模組和文字模組的connector,對於MiniCPM就是Resampler,對於LLaVA來說就是connector,以MiniCPM的訓練舉例

預訓練

Connector Warm-up :不改變預訓練的ViT的解析度(預設224*224),選用Image caption資料庫,僅訓練connector,其他引數都凍結。各家可能會採用不同的策略,比如MiniCPM會使用Image caption和OCR兩個資料訓練,下面是一些訓練的資料sample(from LAION-COCO)。這類資料的特點是caption較短,解析度較低,資料量非常大。
解析度提升:第二階段會把訓練圖片的解析度提升到448*448,並只訓練Vision Encoder,其他所有模組凍結。用到的資料和第一階段相同。
encoding策略訓練:MiniCPM使用了一些特殊方法來處理高解析度圖片(LLaVA和其他模型也是類似的),比如pooling或者adaptive Slicing+pooling等,這些高解析度圖片處理方法會改變ViT預訓練模型感知圖片的特徵,因此這裡需要針對該方法對Vision Encoder+Connector進行訓練,並凍結LLM的引數。

SFT

基本上就是傳統的SFT階段,會使用QA或者具體的下游資料對模型進行指令微調,MiniCPM這裡在訓練的時候把所有引數都開啟進行微調了。如果我們看LLaVA,他們用的策略也是類似的,都是開啟全參微調。但我在實際訓練過程中,全參微調的效果不如只訓練LLM,應該主要是資料量和資料分佈的原因,分佈和原資料有差別的話,效果可能不如只訓練LLM.

RLHF

幾乎所有開源的多模態模型都在Alignment tuning這一步取得了效果,並且會在減少幻覺這個Benchmark上大書特書。目前正在計劃做這一步的訓練,會在有結果的時候分享出來。
總結下,
  • • 現在ViT+LLM的訓練過程普遍包含Image caption的預訓練,解析度提升訓練,高分圖片或多圖encoding方法的訓練,SFT和DPO幾個流程。
  • • 實際做SFT的話,資料量小隻訓練LLM就行了。
下面提出幾個問題,大家可以關注我看後續問題的結果(我也在學習)
  • • 每個階段的衡量標準是什麼?如果全都訓練完看端到端的效果,流程是否太長了?
  • • 每個階段訓練完的消融實驗有沒有?為什麼訓練過程要這麼設計?哪些手法是特別有效果,哪些手法的增量微乎其微?
  • • 訓練資料都是百K,M的量級起跳,訓練的時候有哪些資料處理的細節?有沒有科學手段衡量自己的應用資料集應該如何構建和清洗?

每個階段的衡量標準

這裡至少有兩個問題
  • • 該訓練階段目標達成如何?
  • • 該階段訓練目標如何和下游任務的目標對齊?
比如,第一階段是訓練projector,使用Image caption資料,那麼projector對齊的越好,對Benchmark的提升就越大?projector對齊到什麼程度,對下游任務的影響就比較小了?

關於VLM SFT訓練後的一些實驗和思考

SFT本身並不算有技術難度,可以簡單到兩個流程:
  • • ① 組織好任務需要的資料
  • • ② 開幾個實驗進行超參的探索
實際上第二步甚至也可以不做,SFT的腳本里已經預設了一些超參,直接跑下訓練就可以了。那麼問題來了,一次訓練完了,有一個初始效果了,後續如何繼續最佳化?
還是要從業務角度出發,發現存在的問題。先說我們任務對於VLM的要求是,
  • • 回覆有一定多樣性,業務需要一次生成很多候選集,即一次生成結果中需要存在多樣性;並且在Input相同的情況下,需要給出多樣性更高的回覆,即多次生成需要存在多樣性。
  • • 回覆的質量要夠高,需要在離線和人工評估的Benchmark上有持續的漲點。
這裡說一個暴論:如果我們的任務是服務於應用,不要輕易改模型結構,如果是分類問題,可以嘗試改下最後一層或者加一層,可能效果還不錯。原因也很簡單,做一個好的基礎大模型是一門系統工程,需要標註資源,資料資源,訓練資源和人力資源的投入,除非能論證價值並調動很大團隊做,否則大機率是無用功。模型結構的改動必然會導致LLM已經Align過的部分改變,效果大機率是下降的。
所以這裡初始也是最有效的方法就是想辦法構造更多更高質量訓練資料。
回到VLM的要求,針對相同Input也要求回覆多樣性的場景,有幾種方法。

純inference方法:

RAG:做一個帶Recall的系統,一次生成的每個給不同的prompt,回覆自帶多樣性,但要模型有較強的in-context learning的能力;而且RAG的內容會潛在影響模型訓練好的回覆質量。
High temperature:用的多了會發現這個最大的問題就是回覆和我們想象的多樣性差別較大,整體來說還在一個框架內進行回覆。
Dynamic temperature:算是一個折中的方案,增加前幾個token的temperature,後面token的temperature是不固定的。

訓練的方法

我們的思考非常直白,就是增加相同Input下面生成tokens的多樣性,Next token prediction的公式是: ,如果我們能讓模型在相同Input下看到不同的token,是否就天然讓生成帶有多樣性?
這裡我們利用了閉源的VLM對相同Input構造了n條完全不同的回覆,並保證這n條回覆的每條的質量都是達標,甚至更高的。並用這些資料對模型進行了訓練。但不知為何,模型突然開始拒絕回覆!

經過思考,我們初步確定了兩個假設
  • • 構造資料的Input資料是來線上的真實分佈,所以圖片的解析度的多樣性非常高,考慮到VLM預訓練時的解析度都是相對一致且逐步提升,直接動態解析度訓練可能引起問題;
  • • 給相同Input的不同回覆的資料導致模型引數corrupt,乾脆不知道怎麼回答了。所以我們設計了一個實驗,
實驗1:把相同Input的5條回覆縮小到1條,如果還是回覆有問題,就確定至少和多個回覆無關;
結果我們發現模型還是存在拒絕回覆的情況,那麼可以確認問題至少不是假設2引起的。
為了驗證假設1,我們可以設計另兩個實驗
實驗2:把所有輸入圖片的解析度都統一;
實驗3:給不同解析度的圖片進行歸併,進行分階段訓練,例如限定224\times224,336\times336,448\times448,這幾個區間,把一個sample中的多圖找中位數進行解析度統一,先訓練低解析度組,再訓練高解析度組。
我在做這兩個實驗之前,順手起了另一個任務,用相同資料換了個backbone,從MiniCPM-v換成了LLaVA-onevision,然後效果突然就變好了。不僅沒有拒絕回答的情況,而且按照預想的結果,給出了多樣性非常高的回覆。
這讓我們陷入了思考,為什麼backbone對模型的影響這麼大,到底哪裡對我們之前的認知產生了衝擊,產生了這樣的結果。

引用連結

[1] LLaVA-next的2024.6月的blog: https://llava-vl.github.io/blog/2024-05-25-llava-next-ablations/

技術交流群邀請函

△長按新增小助手
掃描二維碼新增小助手微信
請備註:姓名-學校/公司-研究方向
(如:小張-哈工大-對話系統)
即可申請加入自然語言處理/Pytorch等技術交流群

關於我們

MLNLP 社群是由國內外機器學習與自然語言處理學者聯合構建的民間學術社群,目前已經發展為國內外知名的機器學習與自然語言處理社群,旨在促進機器學習,自然語言處理學術界、產業界和廣大愛好者之間的進步。
社群可以為相關從業者的深造、就業及研究等方面提供開放交流平臺。歡迎大家關注和加入我們。


相關文章