微調 Florence-2 - 微軟的尖端視覺語言模型
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 資料集,其中包含每張影像的廣泛資訊——邊界框、掩碼、標題和接地。資料集建立過程大部分是自動化的。作者使用現成的任務特定模型和一套啟發式方法和質量檢查來清理獲得的結果。最終得到一個新資料集,包含1.26億張影像的50億多條標註,該資料集用於預訓練 Florence-2 模型。
VQA上的原始效能
我們嘗試了各種方法來調整模型以適應 VQA(視覺問答)響應。我們發現最有效的方法是區域到描述的提示,儘管它與 VQA 任務並不完全一致。影像標註提供了影像的描述性資訊,但不支援直接的問答輸入。我們還測試了幾種“不受支援”的提示,例如“
微調後在 DocVQA 上的效能
我們使用 DocVQA 資料集的標準指標 Levenshtein 相似度來衡量效能。在微調之前,模型預測與驗證集上的真實標籤之間的相似度為0,因為輸出與真實標籤不接近。在用訓練集微調七個 epoch 後,驗證集上的相似度得分提高到57.0。我們建立了一個 🤗 空間來演示微調後的模型。儘管該模型在 DocVQA 方面表現良好,但在通用文件理解方面仍有改進空間。然而,它成功地完成了任務,展示了 Florence-2 在下游任務中進行微調的潛力。為了開發一個出色的 VQA 模型,我們建議使用 The Cauldron 進一步微調 Florence-2。我們已經在 我們的 GitHub 頁面上提供了必要的程式碼。
為了提供一個具體的例子,下面我們提供了微調前後兩個推理結果。您也可以在這裡試用該模型。
微調前後
微調細節
在預訓練中,作者對基礎模型使用了 2048 的批次大小,對大型模型使用了 3072 的批次大小。他們還描述了與凍結影像編碼器相比,解凍影像編碼器進行微調時效能的提升。
我們使用低得多的資源配置進行了實驗,以探索模型在更受限制的微調環境中的能力。我們在 Colab 的單個 A100 GPU 上凍結了視覺編碼器並使用了 6 的批次大小,或者在 T4 上使用了 1 的批次大小。同時,我們還進行了一項使用更多資源的實驗,以 64 的批次大小微調整個模型。此訓練過程在配備 8 個 H100 GPU 的叢集上花費了 70 分鐘。此訓練好的模型可在此處找到。
在每種情況下,我們都發現 1e-6 的小學習率對訓練有益。如果學習率較大,模型會很快過擬合訓練集。
程式碼演練
如果您想繼續學習,可以在此處找到我們的 Colab 微調筆記本,其中包含在 DocVQA 資料集上的檢查點。讓我們從安裝依賴項開始。
!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 資料集,在該資料集中,我們將向資料集中的每個問題新增一個
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
我們現在將構建資料整理器,它從資料集樣本構建訓練批次,然後開始訓練。在具有 40GB 記憶體的 A100 上,我們可以放入 6 個示例。如果您在 T4 上訓練,可以使用 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 上看到您的模型!
有用資源
我們感謝 Pedro Cuenca 對本部落格文章的評論。