多模態大模型:算法、應用與微調
用LoRA訓練Stable Diffusion時,首先凍結模型的權重,然后在U-Net結構中注入LoRA模型,將其與交叉注意力模塊結合在一起,最后微調時將只對這部分參數進行更新。
數據收集
本次微調將使用數碼寶貝數據集作為下游細分任務,數據來源于數碼獸數據庫(http://digimons.net/digimon/chn.html)。Stable Diffusion的訓練數據非常直觀,就是一張圖片對應一段文本描述,因此我們需要先通過一個爬蟲將數據整理下來。
我們需要從數碼獸圖鑒網頁中爬取所有數碼獸的信息,包括名稱、介紹和對應的圖片鏈接。然后將這些信息整理成下面這種格式,保存到對應的文件夾中。
# 數據格式 metadata.jsonl + 圖片
folder/train/metadata.jsonl #存儲caption描述
folder/train/0001.png
folder/train/0002.png
folder/train/0003.png
# metadata.jsonl中的內容
{"file_name": "0001.png", "text": "image 1 description"}
{"file_name": "0002.png", "text": "image 2 description"}
{"file_name": "0003.png", "text": "image 3 description"}
為了實現這個任務,我們需要使用Python的爬蟲和文件操作相關的庫。首先,使用requests庫獲取數碼獸圖鑒頁面的HTML內容,并使用BeautifulSoup庫解析HTML,以便對頁面進行提取信息。然后我們分析這個頁面,所有的數碼獸都存在一個id為digimon_list的ul列表中,每一行就是一個li標簽,這個標簽里面有一個a標簽,對應了該數碼獸的詳情鏈接。
接下來,我們遍歷頁面中所有的li標簽,提取數碼獸的名稱和詳情頁面鏈接。然后進入詳情頁面,獲取數碼獸的介紹和圖片鏈接。最后,將這些信息整理成指定的格式,并保存到對應的文件夾中。具體而言,我們需要在指定的文件夾中創建一個metadata.jsonl文件來保存每個圖片的文件名和對應的描述文本,并使用文件名對應的順序來保存對應的圖片文件。
最終,我們會得到一個數據集,其中包含每個數碼獸的名稱、介紹和對應的圖片,以及一個metadata.jsonl文件,其中保存了每個圖片的文件名和對應的描述文本。
python
import os
import json
import requests
from bs4 import BeautifulSoup
# 創建文件夾
data_dir = "./train"
if not os.path.exists(data_dir):
os.makedirs(data_dir)
# 請求數碼獸圖鑒頁面
url = "http://digimons.net/digimon/chn.html"
response = requests.get(url)
soup = BeautifulSoup(response.content, "html.parser")
# 遍歷所有的 li 標簽
digimon_list = soup.find("ul", id="digimon_list")
for digimon in digimon_list.find_all("li"):
try:
# 獲取數碼獸名稱和詳情頁面鏈接
name = digimon.find('a')["href"].split('/')[0]
detail_url = "http://digimons.net/digimon/" + digimon.find('a')["href"]
print(f"detail_url: {detail_url}")
# 進入詳情頁面,獲取數碼獸介紹和圖片鏈接
response = requests.get(detail_url)
soup = BeautifulSoup(response.content, "html.parser")
caption = soup.find("div", class_="profile_eng").find('p').text.strip()
img_url = f"http://digimons.net/digimon/{name}/{name}.jpg"
# 保存圖片
img_data = requests.get(img_url).content
file_name = f"{len(os.listdir(data_dir)) + 1:04d}.png"
with open(os.path.join(data_dir, file_name), "wb") as f:
f.write(img_data)
# 將數據整理成指定的格式,并保存到對應的文件中
metadata = {"file_name": file_name, "text": f"{name}. {caption}"}
with open(os.path.join(data_dir, "metadata.jsonl"), 'a') as f:
f.write(json.dumps(metadata) + '\n')
except Exception as _:
pass
數據集整理完成后,如果我們想讓其他人也可以用我們的數據集,那么就可以將其發布到Hugging Face Hub上。Hugging Face Hub是一個收集了多個領域中多種任務的模型以及數據集的平臺,使用戶可以非常方便地下載和使用各種資源。并且,Hugging Face也非常鼓勵用戶上傳自己的數據集,以幫助壯大AI學習社區并加快其發展步伐,造福大家。
如果你還沒有Hugging Face Hub賬號,需要先去其官網注冊一個賬號,然后按照以下步驟創建數據集。
- 點擊頁面右上角profile下的New DataSet選項創建一個新的數據集倉庫,如圖7-4所示。
- 輸入數據集的名稱,并選擇是否為公開數據集,公開數據集對所有人可見,而私有數據集僅對自己或組織成員可見。
圖7-4 Hugging Face創建數據集頁面樣例
1)進入數據集頁面,點擊頂部菜單欄的“Files and versions”,然后點擊右邊“Add file”按鈕下的“upload files”按鈕,之后將圖片文件和訓練數據元文件直接拖拽到上傳文件框,最后編寫修改信息,點擊提交即可,如圖7-5所示。
圖7-5 Hugging Face上傳數據集內容頁面樣例
訓練參數設置
需要注意的是,為了保證能夠成功運行最新版的訓練代碼,建議通過源碼重新安裝Diffusers庫。
bash
pip install git+https://github.com/Hugging Face/diffusers
然后我們需要初始化Accelerate分布式訓練環境。
bash
> accelerate config
[2023-07-20 18:37:53,537] [INFO] [real_accelerator.py:110:get_accelerator] Setting ds_accelerator to cuda (auto detect)
NOTE: Redirects are currently not supported in Windows or MacOs.
--------------------------------------------------------------------------------------------------------------------In which compute environment are you running?
This machine
--------------------------------------------------------------------------------------------------------------------Which type of machine are you using?
No distributed training
Do you want to run your training on CPU only (even if a GPU / Apple Silicon device is available)? [yes/NO]: NO
Do you wish to optimize your script with torch dynamo?[yes/NO]: NO
Do you want to use DeepSpeed? [yes/NO]: NO
What GPU(s) (by id) should be used for training on this machine as a comma-seperated list? [all]: all
--------------------------------------------------------------------------------------------------------------------Do you wish to use FP16 or BF16 (mixed precision)?
no
accelerate configuration saved at C:\Users\admin/.cache\Hugging Face\accelerate\default_config.yaml
模型訓練與測試
當前,LoRA技術主要支持 UNet2DConditionalModel。Diffusers團隊已經推出了一款適用于LoRA的微調腳本,這款腳本的優勢在于它能夠在僅有11GB GPU RAM的環境中穩定運行,而且不需要依賴8-bit等優化技術。下面我們將展示了如何使用此腳本結合數碼獸數據集來進行模型的微調操作。
bash
accelerate launch --mixed_precisinotallow="fp16" train_text_to_image_lora.py \
--pretrained_model_name_or_path="runwayml/stable-diffusion-v1-5" \
--train_data_dir="./train_data" \
--dataloader_num_workers=0 \
--resolutinotallow=512 --center_crop --random_flip \
--train_batch_size=1 \
--gradient_accumulation_steps=4 \
--max_train_steps=15000 \
--learning_rate=1e-04 \
--max_grad_norm=1 \
--lr_scheduler="cosine" --lr_warmup_steps=0 \
--output_dir="./finetune/lora/digimon" \
--checkpointing_steps=500 \
--validation_prompt="Blue Agumon" \
--seed=1024
該腳本是啟動了一個混合精度為fp16的加速微調訓練任務。它采用的預訓練模型是runwayml/stable-diffusion-v1-5,并從"./train_data"路徑下獲取訓練數據。腳本配置為單線程加載數據,并將圖像解析為512x512的分辨率,同時允許中心裁剪和隨機翻轉。雖然每次只處理一個批次的數據,但它會累計四個批次的梯度進行一次更新。訓練的最大步數被設置為15000步,學習率為0.0001,并限制了梯度的最大范數為1。學習率調度器采用的是余弦退
火策略,不進行預熱。訓練結果將保存在"./finetune/lora/digimon"目錄下,每500步保存一次檢查點。此外,驗證的提示詞設置為"Blue Agumon",并指定了隨機種子為1024,以確保實驗的可重復性。
正如我們前面所討論的,LoRA的主要優勢之一是可以通過訓練比原始模型小幾個數量級的權重來獲得出色的結果。通過load_attn_procs函數,我們可以在原始的Stable Diffusion模型權重之上加載額外的權重。
python
import torch
from diffusers import StableDiffusionPipeline, DPMSolverMultistepScheduler
model_path = "runwayml/stable-diffusion-v1-5"
LoRA_path = "./finetune/lora/digimon" # 修改成本地LoRA模型路徑
pipe = StableDiffusionPipeline.from_pretrained(model_path, torch_dtype=torch.float16)
pipe.scheduler = DPMSolverMultistepScheduler.from_config(pipe.scheduler.config)
pipe.unet.load_attn_procs(LoRA_path)
pipe.to("cuda")
image = pipe("blue skin agumon", num_inference_steps=50).images[0]
image.save("test.png")
在上面這段代碼中,我們首先定義了兩個路徑,一個是主模型路徑model_path,如果還是使用原生的Stable Diffusion則不需要修改,還有一個是LoRA模型的路徑LoRA_path,需要將LoRA_path修改為正確的本地LoRA模型路徑。接下來創建StableDiffusionPipeline流水線對象,然后通過load_attn_procs方法用于加載本地的LoRA模型,并將其應用于流水線的unet模塊,再將管道移至GPU以加快推理速度。最后,我們給定一個提示詞“blue skin agumon”,讓模型生成一個藍色皮膚的亞古獸,訓練數據集的亞古獸圖片與生成的圖片如圖7-6所示。
圖7-6 原始亞古獸圖片(左)與微調后的模型生成的“藍色亞古獸”圖片(右)對比
在推理時我們還可以調整LoRA的權重系統:
圖片
如果將設置為0時,其效果與只使用主模型完全一致;如果將設置為1時,與使用的效果相同。因此,如果LoRA存在過擬合的現象,我們可以將設置為較低的值,如果使用LoRA的效果不太明顯,那我們可以將設置為略高于1的值。
除了使用單個LoRA模型,還可以將多個LoRA模型同時添加到一個主模型中,同樣也可以調整兩個LoRA模型的權重:
圖片
如果將和都設置為0.5,那么在主模型添加的就是兩個LoRA模型的平均權重。如果將設置為0.7,設置為0.3,那么第一個LoRA模型的效果將占據70%的效果。
在代碼中,將LoRA權重與凍結的預訓練模型權重合并時,也可以選擇調整與參數合并的權重數量scale,scale值為0表示不使用LoRA權重,而scale值為1表示使用完全微調的 LoRA 權重。
python
pipe.unet.load_attn_procs(lora_model_path)
pipe.to("cuda")
image = pipe(
"A agumon with blue skin.", num_inference_steps=25, guidance_scale=7.5, cross_attention_kwargs={"scale": 0.5}
).images[0]
image.save("blue_pokemon.png")