解鎖多模態(tài)大語言模型:從原理到實(shí)戰(zhàn),一文全掌握! 原創(chuàng)
引言:人工智能領(lǐng)域的新焦點(diǎn)
在人工智能飛速發(fā)展的大環(huán)境下,近年來備受矚目的一個(gè)領(lǐng)域便是多模態(tài)大語言模型(MLLMs)。這些具有變革性的工具不僅能夠處理和生成文本,還能處理圖像、音頻和視頻等其他形式的數(shù)據(jù)。通過借助機(jī)器學(xué)習(xí)和深度學(xué)習(xí)算法的強(qiáng)大力量,多模態(tài)大語言模型能夠捕捉人類在多種模態(tài)下交流的細(xì)微差別,為自然語言處理、計(jì)算機(jī)視覺和多媒體分析等領(lǐng)域的應(yīng)用開辟了新的可能性。
隨著我們不斷拓展多模態(tài)大語言模型的能力邊界,研究這些模型在結(jié)合視覺和聽覺信息后生成新穎文本內(nèi)容的潛力變得愈發(fā)重要。在一個(gè)由人工智能驅(qū)動(dòng)的 “多模態(tài)世界” 中,系統(tǒng)能夠無縫整合多種形式的數(shù)據(jù)以創(chuàng)造新的意義和價(jià)值,這已不再是科幻小說中的情節(jié)。
事實(shí)上,正如近期的研究和應(yīng)用所展示的那樣,多模態(tài)大語言模型在生成連貫、特定于上下文且富有細(xì)微差別的文本方面已經(jīng)展現(xiàn)出了卓越的能力。例如,一個(gè)在圖像和字幕數(shù)據(jù)集上訓(xùn)練的多模態(tài)模型,能夠生成與新圖像語義準(zhǔn)確且在風(fēng)格上與原始字幕一致的新穎描述。
作為 “讓我們……” 系列的一部分,我們旨在通過進(jìn)行一項(xiàng)實(shí)驗(yàn),進(jìn)一步探索多模態(tài)大語言模型的潛力,該實(shí)驗(yàn)旨在突破結(jié)合視覺和語言信息時(shí)的可能性極限。本研究有兩個(gè)具體目標(biāo):一是掌握多模態(tài)大語言模型的基礎(chǔ)知識(shí);二是通過實(shí)驗(yàn)來展示多模態(tài)大語言模型的能力。
多模態(tài)大語言模型(MLLM)
多模態(tài)大語言模型(MLLM)是一種人工智能(AI)模型,它可以處理和生成文本,以及其他形式的數(shù)據(jù),比如:
- 圖像:多模態(tài)大語言模型能夠理解圖像的內(nèi)容,包括其中的物體、場(chǎng)景和動(dòng)作。
- 音頻:它可以分析音頻記錄,包括語音、音樂和各種聲音。
- 視頻:能夠處理視頻內(nèi)容,涵蓋視覺和聽覺信息。
換句話說,多模態(tài)大語言模型是一種在多樣化的數(shù)據(jù)類型上進(jìn)行訓(xùn)練的大語言模型,這使得它能夠理解和生成與這些不同模態(tài)(或形式)的數(shù)據(jù)相關(guān)的文本。
多模態(tài)大語言模型旨在處理多模態(tài)數(shù)據(jù)的復(fù)雜性,這可能包括:
- 語義:理解多種語言中單詞、短語和句子的含義。
- 句法:分析語言的結(jié)構(gòu),包括語法和句子結(jié)構(gòu)。
- 視覺:識(shí)別圖像和視頻中的物體、場(chǎng)景和動(dòng)作。
- 聽覺:分析語音、音樂和聲音。
多模態(tài)大語言模型(MLLMs)基于幾個(gè)關(guān)鍵組件構(gòu)建:
- 模態(tài)編碼器:這個(gè)組件將圖像、音頻和視頻等輸入數(shù)據(jù)轉(zhuǎn)換為特征表示。例如ViT、CLIP和HuBERT等。
- 輸入投影器:該組件將編碼器的特征與大語言模型的文本輸入連接起來。方法包括線性、多層感知器(MLP)和交叉注意力。
- 大語言模型(LLMs):在多模態(tài)大語言模型中會(huì)使用像GPT、LLaMA和Flan-T5這樣的大語言模型架構(gòu)。
- 輸出投影器:此組件將語言模型的特征映射到圖像、視頻或音頻等數(shù)據(jù)類型的特征。例如MLP和Tiny Transformer。
- 模態(tài)生成器:該組件使用特征表示生成所需的數(shù)據(jù)。例如用于圖像的Stable Diffusion、用于視頻的Zeroscope和用于音頻的AudioLDM。
自GPT-4發(fā)布以來,由于其多模態(tài)示例,對(duì)多模態(tài)語言模型(MLLMs)的研究激增。
多模態(tài)大語言模型的優(yōu)點(diǎn)包括:
- 更好地理解人類交流:通過處理多種形式的數(shù)據(jù),多模態(tài)大語言模型可以更好地理解人類在不同模態(tài)下的交流方式。
- 增強(qiáng)的多模態(tài)生成能力:多模態(tài)大語言模型可以生成更細(xì)致入微且特定于上下文的文本,同時(shí)考慮到場(chǎng)景或情況的視覺和聽覺方面。
- 更好地表示語言:多模態(tài)大語言模型能夠捕捉現(xiàn)實(shí)世界場(chǎng)景中語言使用的復(fù)雜性,在這些場(chǎng)景中通常存在多種形式的數(shù)據(jù)。
總之,多模態(tài)大語言模型是一種強(qiáng)大的人工智能工具,它可以處理、生成和理解文本以及其他形式的數(shù)據(jù),如圖像、音頻和視頻。
代碼實(shí)現(xiàn)
BLIP-2模型架構(gòu)將問題和圖像作為輸入進(jìn)行處理,其輸出是基于問題和圖像上下文的答案。BLIP-2由以下組件組成:
- 圖像編碼器:使用CLIP ViT(視覺變換器)從輸入圖像中提取視覺特征。
- 輸入投影器:Q-Former負(fù)責(zé)將問題和圖像特征投影為適合大語言模型的統(tǒng)一表示。
- 大語言模型(LLMs):我們使用Flan-T5/OPT作為其語言模型核心,它根據(jù)問題和圖像的組合上下文生成最終答案。
BLIP-2是一種高效且有效的視覺與語言預(yù)訓(xùn)練策略,它利用凍結(jié)的預(yù)訓(xùn)練模型和輕量級(jí)的查詢變換器,以顯著更少的可訓(xùn)練參數(shù)實(shí)現(xiàn)了領(lǐng)先的性能。這種方法使模型在各種任務(wù)中表現(xiàn)出色,包括零樣本VQAv2以及帶有自然語言指令的圖像到文本生成。
在接下來的部分,我們將使用transformers庫(kù)在VQA數(shù)據(jù)集上訓(xùn)練BLIP-2模型。VQA數(shù)據(jù)集包含14,550個(gè)樣本,每個(gè)樣本由一個(gè)輸入(圖像 + 問題)和一個(gè)輸出(答案)組成。
環(huán)境設(shè)置
我們從指定位置下載VQA數(shù)據(jù)集以及必要的庫(kù)。
# 下載并解壓數(shù)據(jù)集
!gdown 1--CmXocqkrcPR-bbSDztnxBM9dBC8uUJ
!unzip /content/IconDomainVQAData.zip
# 安裝必要的庫(kù)
!pip install -q peft transformers bitsandbytes datasets
然后,我們調(diào)用在實(shí)驗(yàn)過程中會(huì)用到的庫(kù)。
import os, json
from PIL import Image
from tqdm import tqdm
import torch
from torch.utils.data import Dataset, DataLoader
from torch import optim
from peft import LoraConfig, get_peft_model
from transformers import BlipProcessor, BlipForQuestionAnswering
from datasets import load_dataset, DatasetDict
實(shí)驗(yàn)設(shè)置
在這一步,我們準(zhǔn)備數(shù)據(jù)集,構(gòu)建模型,并制定訓(xùn)練策略,以確保模型在視覺問答任務(wù)中表現(xiàn)最佳。
現(xiàn)在,將數(shù)據(jù)集加載到數(shù)據(jù)集設(shè)置中:
ds = load_dataset("json", data_files=f"{data_path}/train.jsonl")
ds
輸出結(jié)果為:
DatasetDict({
train: Dataset({
features: ['question', 'answer', 'ques_type', 'grade', 'label', 'pid', 'unit', 'hint'],
num_rows: 14551
})
})
由于我們已經(jīng)擁有了數(shù)據(jù)集,接下來開始構(gòu)建在訓(xùn)練過程中起著至關(guān)重要作用的Dataset對(duì)象。
# 構(gòu)建Dataset
class VQADataset(Dataset):
def __init__(self, dataset, processor, data_path):
self.dataset = dataset
self.processor = processor
self.data_path = data_path
def __len__(self):
return len(self.dataset)
def __getitem__(self, idx):
question = self.dataset[idx]['question']
answer = self.dataset[idx]['answer']
image_id = self.dataset[idx]['pid']
image_path = os.path.join(self.data_path, f"train_fill_in_blank/train_fill_in_blank/{image_id}/image.png")
image = Image.open(image_path).convert("RGB")
text = question
encoding = self.processor(image, text, padding="max_length", truncatinotallow=True, return_tensors="pt")
labels = self.processor.tokenizer.encode(answer, max_length=8, pad_to_max_length=True, return_tensors="pt")
encoding['labels'] = labels
for k, v in encoding.items():
encoding[k] = v.squeeze()
return encoding
由于我們?cè)赩QADataset對(duì)象中有了處理器,接下來加載處理所需的內(nèi)容。我們使用Salesforce/blip-vqa-base模型,并對(duì)模型進(jìn)行一些量化處理。
model_id = "Salesforce/blip-vqa-base"
processor = BlipProcessor.from_pretrained(model_id)
model = BlipForQuestionAnswering.from_pretrained(model_id)
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
bias="none",
target_modules=["query", "key"]
)
model = get_peft_model(model, lora_config )
device = torch.device("cuda"if torch.cuda.is_available() else"cpu")
model.to(device)
model.print_trainable_parameters()
輸出結(jié)果為:
trainable params: 2,359,296 || all params: 387,031,868 || trainable%: 0.6096
我們創(chuàng)建DataLoader。首先,為訓(xùn)練數(shù)據(jù)集和驗(yàn)證數(shù)據(jù)集創(chuàng)建Dataset對(duì)象。
train_dataset = VQADataset(dataset=ds["train"],
processor=processor,
data_path=data_path)
val_dataset = VQADataset(dataset=ds["train"],
processor=processor,
data_path=data_path)
然后,進(jìn)行創(chuàng)建DataLoader所需的操作。
batch_size=8
train_dataloader = DataLoader(train_dataset,
batch_size=batch_size,
shuffle=True,
pin_memory=True)
val_dataloader = DataLoader(val_dataset,
batch_size=batch_size,
shuffle=False,
pin_memory=True)
此時(shí)我們可以開始訓(xùn)練過程。
# ======== 設(shè)置部分========
#
optimizer = optim.AdamW(model.parameters(),
lr=4e-5)
scheduler = optim.lr_scheduler.ExponentialLR(optimizer,
gamma=0.9,
last_epoch=-1,
verbose=True)
n_epochs = 1
min_val_loss = float("inf")
scaler = torch.cuda.amp.GradScaler()
# ======== 訓(xùn)練部分========
#
for epoch in range(n_epochs):
# 訓(xùn)練設(shè)置
train_loss = []
model.train()
for idx, batch in zip(tqdm(range(len(train_dataloader)),
desc=f"Training Batch {epoch+1}"), train_dataloader):
input_ids = batch.pop("input_ids").to(device)
pixel_values = batch.pop("pixel_values").to(device)
attention_masked = batch.pop('attention_mask').to(device)
labels = batch.pop('labels').to(device)
with torch.amp.autocast(device_type='cuda', dtype=torch.float16):
outputs = model(input_ids=input_ids,
pixel_values=pixel_values,
labels=labels)
loss = outputs.loss
train_loss.append(loss.item())
## 反向傳播
optimizer.zero_grad()
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
# 驗(yàn)證設(shè)置
val_loss = []
model.eval()
for idx, batch in zip(tqdm(range(len(val_dataloader)),
desc=f"Validating Batch {epoch+1}"), val_dataloader):
input_ids = batch.pop("input_ids").to(device)
pixel_values = batch.pop("pixel_values").to(device)
attention_masked = batch.pop('attention_mask').to(device)
labels = batch.pop('labels').to(device)
with torch.amp.autocast(device_type='cuda', dtype=torch.float16):
outputs = model(input_ids=input_ids,
pixel_values=pixel_values,
attention_mask=attention_masked,
labels=labels)
loss = outputs.loss
val_loss.append(loss.item())
avg_train_loss = sum(train_loss)/len(train_loss)
avg_val_loss = sum(val_loss)/len(val_loss)
lr_per_epoch = optimizer.param_groups[0]["lr"]
print(f"Epoch: {epoch + 1} - Training Loss: {avg_train_loss} - Eval Loss: {avg_val_loss} - LR: {lr_per_epoch}")
scheduler.step()
if avg_val_loss < min_val_loss:
model.save_pretrained('./save_model', from_pt=True)
print ("Saved model to ./save_model")
min_eval_loss = avg_val_loss
processor.save_pretrained ("./save_model", from_pt=True )
最后,我們可以通過以下步驟測(cè)試模型的能力:
調(diào)用剛剛訓(xùn)練好的模型:
retrain_model_dir = './save_model'
processor = BlipProcessor.from_pretrained(retrain_model_dir)
model = BlipForQuestionAnswering.from_pretrained(retrain_model_dir).to(device)
選擇一個(gè)樣本進(jìn)行測(cè)試:
test_data_dir = f"{data_path}/test_data/test_data"
samples = os.listdir(test_data_dir)
sample_path = os.path.join(test_data_dir, samples[0])
json_path = os.path.join(sample_path, "data.json")
with open(json_path, "r") as json_file:
data = json.load(json_file)
question = data["question"]
image_id = data["id"]
"""
question
"""
"""
image_path = os.path.join(test_data_dir, f"{image_id}", "image.png")
image = Image.open(image_path).convert("RGB")
image
"""
使用檢索到的樣本向模型提問答案:
encoding = processor(image,
question,
return_tensors="pt").to(device, torch.float16)
outputs = model.generate(**encoding)
generated_text = processor.decode(outputs[0], skip_special_tokens=True)
generated_text
結(jié)論
我們探索了多模態(tài)大語言模型(MLLMs)這個(gè)充滿魅力的世界,深入研究了它們的基本原理以及在各種應(yīng)用中,尤其是在視覺問答領(lǐng)域所蘊(yùn)含的令人興奮的潛力。此外,我們看到了這些模型如何經(jīng)過訓(xùn)練來理解和推理文本和視覺信息,使它們能夠以令人印象深刻的準(zhǔn)確性回答與圖像和視頻相關(guān)的問題。
隨著我們不斷前進(jìn),多模態(tài)大語言模型的研究和開發(fā)將繼續(xù)推動(dòng)人工智能領(lǐng)域的可能性邊界。我們可以期待看到更復(fù)雜的模型,這些模型能夠處理需要深入理解我們周圍世界的復(fù)雜任務(wù)。
本文轉(zhuǎn)載自公眾號(hào)Halo咯咯 作者:基咯咯
