Florence-2 是微軟於 2024 年 6 月釋出的一個基礎視覺語言模型。該模型極具吸引力,因為它尺寸很小 (0.2B 及 0.7B) 且在各種計算機視覺和視覺語言任務上表現出色。
Florence 開箱即用支援多種型別的任務,包括: 看圖說話、目標檢測、OCR 等等。雖然覆蓋面很廣,但仍有可能你的任務或領域不在此列,也有可能你希望針對自己的任務更好地控制模型輸出。此時,你就需要微調了!
本文,我們展示了一個在 DocVQA 上微調 Florence 的示例。儘管原文宣稱 Florence 2 支援視覺問答 (VQA) 任務,但最終釋出的模型並未包含 VQA 功能。因此,我們正好拿這個任務練練手,看看我們能做點什麼!
預訓練細節與模型架構
Florence-2 架構
無論執行什麼樣的計算機視覺任務,Florence-2 都會將其建模為序列到序列的任務。Florence-2 以影像和文字作為輸入,並輸出文字。模型結構比較簡單: 用 DaViT 視覺編碼器將影像轉換為視覺嵌入,並用 BERT 將文字提示轉換為文字和位置嵌入; 然後,生成的嵌入由標準編碼器 - 解碼器 transformer 架構進行處理,最終生成文字和位置詞元。Florence-2 的優勢並非源自其架構,而是源自海量的預訓練資料集。作者指出,市面上領先的計算機視覺資料集通常所含資訊有限 - WIT 僅有圖文對,SA-1B 僅有影像及相關分割掩碼。因此,他們決定構建一個新的 FLD-5B 資料集,其中的每個影像都包含最廣泛的資訊 - 目標框、掩碼、描述文字及標籤。在建立資料集時,很大程度採用了自動化的過程,作者使用現成的專門任務模型,並用一組啟發式規則及質檢過程來清理所獲得的結果。最終生成的用於預訓練 Florence-2 模型的新資料集中包含了 1.26 億張影像、超過 50 億個標註。
VQA 上的原始效能
我們嘗試了各種方法來微調模型以使其適配 VQA (視覺問答) 任務的響應方式。迄今為止,我們發現最有效方法將其建模為影像區域描述任務,儘管其並不完全等同於 VQA 任務。看圖說話任務雖然可以輸出影像的描述性資訊,但其不允許直接輸入問題。
我們還測試了幾個“不支援”的提示,例如 “<VQA>”、“<vqa>” 以及 “<Visual question answering>”。不幸的是,這些嘗試的產生的結果都不可用。
微調後在 DocVQA 上的效能
我們使用 DocVQA 資料集的標準指標 Levenshtein 相似度 來測量效能。微調前,模型在驗證集上的輸出與標註的相似度為 0,因為模型輸出與標註差異不小。對訓練集進行 7 個 epoch 的微調後,驗證集上的相似度得分提高到了 57.0。
我們建立了一個 🤗 空間 以演示微調後的模型。雖然該模型在 DocVQA 上表現良好,但在一般文件理解方面還有改進的空間。但我們仍然認為,它成功地完成了任務,展示了 Florence-2 對下游任務進行微調的潛力。我們建議大家使用 The Cauldron 資料集對 Florence-2 進行微調,大家可以在 我們的 GitHub 頁面 上找到必要的程式碼。
下圖給出了微調前後的推理結果對比。你還可以至 此處 親自試用模型。
微調細節
由原文我們可以知道,基礎模型在預訓練時使用的 batch size 為 2048,大模型在預訓練時使用的 batch size 為 3072。另外原文還說: 與凍結影像編碼器相比,使用未凍結的影像編碼器進行微調能帶來效能改進。
我們在低資源的情況下進行了多組實驗,以探索模型如何在更受限的條件下進行微調。我們凍結了視覺編碼器,並在 Colab 的分別使用單張 A100 GPU (batch size 6) 、單張 T4 (batch size 1) 順利完成微調。
與此同時,我們還對更多資源的情況進行了實驗,以 batch size 64 對整個模型進行了微調。在配備 8 張 H100 GPU 的叢集上該訓練過程花費了 70 分鐘。你可以在 這裡 找到我們訓得的模型。
我們都發現 1e-6
的小學習率適合上述所有訓練情形。如果學習率變大,模型將很快過擬合。
遛程式碼
如果你想復現我們的結果,可以在 此處 找到我們的 Colab 微調筆記本。下面,我們遛一遍在 DocVQA 上微調 Florence-2-base-ft 模型。
我們從安裝依賴項開始。
!pip install -q datasets flash_attn timm einops
接著,從 Hugging Face Hub 載入 DocVQA 資料集。
import torch
from datasets import load_dataset
data = load_dataset("HuggingFaceM4/DocumentVQA")
我們可以使用 transformers 庫中的 AutoModelForCausalLM
和 AutoProcessor
類來載入模型和處理器,並設 trust_remote_code=True
,因為該模型尚未原生整合到 transformers 中,因此需要使用自定義程式碼。我們還會凍結視覺編碼器,以降低微調成本。
from transformers import AutoModelForCausalLM, AutoProcessor
import torch
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = AutoModelForCausalLM.from_pretrained(
"microsoft/Florence-2-base-ft",
trust_remote_code=True,
revision='refs/pr/6'
).to(device)
processor = AutoProcessor.from_pretrained("microsoft/Florence-2-base-ft",
trust_remote_code=True, revision='refs/pr/6')
for param in model.vision_tower.parameters():
param.is_trainable = False
現在開始微調模型!我們構建一個訓練 PyTorch 資料集,併為資料集中的每個問題新增 \<DocVQA\>
字首。
import torch from torch.utils.data import Dataset
class DocVQADataset(Dataset):
def __init__(self, data):
self.data = data
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
example = self.data[idx]
question = "<DocVQA>" + example['question']
first_answer = example['answers'][0]
image = example['image'].convert("RGB")
return question, first_answer, image
接著,構建資料整理器,從資料集樣本構建訓練 batch,以用於訓練。在 40GB 記憶體的 A100 中,batch size 可設至 6。如果你在 T4 上進行訓練,batch size 就只能是 1。
import os
from torch.utils.data import DataLoader
from tqdm import tqdm
from transformers import AdamW, get_scheduler
def collate_fn(batch):
questions, answers, images = zip(*batch)
inputs = processor(text=list(questions), images=list(images), return_tensors="pt", padding=True).to(device)
return inputs, answers
train_dataset = DocVQADataset(data['train'])
val_dataset = DocVQADataset(data['validation'])
batch_size = 6
num_workers = 0
train_loader = DataLoader(train_dataset, batch_size=batch_size,
collate_fn=collate_fn, num_workers=num_workers, shuffle=True)
val_loader = DataLoader(val_dataset, batch_size=batch_size,
collate_fn=collate_fn, num_workers=num_workers)
開始訓練模型:
epochs = 7
optimizer = AdamW(model.parameters(), lr=1e-6)
num_training_steps = epochs * len(train_loader)
lr_scheduler = get_scheduler(name="linear", optimizer=optimizer,
num_warmup_steps=0, num_training_steps=num_training_steps,)
for epoch in range(epochs):
model.train()
train_loss = 0
i = -1
for inputs, answers in tqdm(train_loader, desc=f"Training Epoch {epoch + 1}/{epochs}"):
i += 1
input_ids = inputs["input_ids"]
pixel_values = inputs["pixel_values"]
labels = processor.tokenizer(text=answers, return_tensors="pt", padding=True, return_token_type_ids=False).input_ids.to(device)
outputs = model(input_ids=input_ids, pixel_values=pixel_values, labels=labels)
loss = outputs.loss
loss.backward()
optimizer.step()
lr_scheduler.step()
optimizer.zero_grad()
train_loss += loss.item()
avg_train_loss = train_loss / len(train_loader)
print(f"Average Training Loss: {avg_train_loss}")
model.eval()
val_loss = 0
with torch.no_grad():
for batch in tqdm(val_loader, desc=f"Validation Epoch {epoch + 1}/{epochs}"):
inputs, answers = batch
input_ids = inputs["input_ids"]
pixel_values = inputs["pixel_values"]
labels = processor.tokenizer(text=answers, return_tensors="pt", padding=True, return_token_type_ids=False).input_ids.to(device)
outputs = model(input_ids=input_ids, pixel_values=pixel_values, labels=labels)
loss = outputs.loss
val_loss += loss.item()
print(val_loss / len(val_loader))
你可以分別對模型和處理器呼叫 save_pretrained()
以儲存它們。微調後的模型在 此處,你還可以在 此處 找到其演示。
總結
本文,我們展示瞭如何有效地針對自定義資料集微調 Florence-2,以在短時間內在全新任務上取得令人眼前一亮的效能。對於那些希望在裝置上或在生產環境中經濟高效地部署小模型的人來說,該做法特別有價值。我們鼓勵開源社群利用這個微調教程,探索 Florence-2 在各種新任務中的巨大潛力!我們迫不及待地想在 🤗 Hub 上看到你的模型!
有用資源
- 視覺語言模型詳解
- 微調 Colab
- 微調 Github 程式碼庫
- Florence-2 推理 Notebook
- Florence-2 DocVQA 演示
- Florence-2 演示
我們感謝 Pedro Cuenca 對本文的審閱。
英文原文: https://hf.co/blog/finetune-florence2
原文作者: Andres Marafioti,Merve Noyan,Piotr Skalski
譯者: Matrix Yao (姚偉峰),英特爾深度學習工程師,工作方向為 transformer-family 模型在各模態資料上的應用及大規模模型的訓練推理。