[AIGC] Stable Diffusion with Lora 訓練記錄

講在最前面,市面上的 Stable Diffusion, Lora 的相關影片相當多,如果有興趣的人可以去找影片看看,我這邊僅只是記錄一些實驗性的東西。


Lora 相關硬資料

基本的論文跟相關資料在這邊,由於在下英文很爛,加上本身是做水電的,所以多半是有看沒有懂,只能一直問 Google 或是 ChatGPT(但後者其實...)。

關於教學影片看這個就好了,不過這邊就是講個大概,剩下的得自己嘗試。

Youtube, ULTIMATE FREE LORA Training In Stable Diffusion! Less Than 7GB VRAM!


Lora 訓練準備

上面的影片可以解決大概 8 成以上的問題,不過本著實驗精神,所以我還是準備了一些東西來驗證。這邊有一個關於 Lora 訓練圖片準備細節 可以參考。

根據 Lora 的訓練圖集,所以我準備了這些圖片。

  • 98 張訓練集圖片
  • 520 張正規化圖片 (REGULARIZATION IMAGES)

我實在不知道要怎麼把 REGULARIZATION 翻譯成比較合適的文字,只好用比較通用的中文翻譯來寫。這邊說明一下 REGULARIZATION IMAGES 做了什麼事情。主要的功能有兩個,

  1. 識別類 (Class)
  2. 避免過度擬合 (Overfitting)

舉個比較淺顯易懂的例子,如果你的訓練圖片是你自己設計的未來汽車,那麼,你的 REGULARIZATION IMAGES 就得準備一些「汽車」的圖片 (除非你的汽車沒有四顆輪子),訓練集的圖片會與 REGULARIZATION IMAGES 去做比對,取用 REGULARIZATION IMAGES 有的,並排除訓練集裡沒有的部分。換句話說,以汽車為例子,你的 REGULARIZATION IMAGES 就是一大堆汽車的圖片,這樣就可以避免在訓練時,他變成自行車或機車之類的東西。這樣一來,你在訓練的過程中就能確保「汽車」這個類 (Class) 會被保存,而且不會歪掉。

接著,這些圖片該怎麼準備?我參考了不少作法,但大部分(例如說 b 站)都是二次元的訓練,真的要拿到三次元來用其實是不太合用的。所以,我就自己弄了一個生成老婆的 Lora 來驗證關於訓練輪次 (Epochs) 與權重 (Block weight)、過度擬合 (Overfitting) 之間的數據實驗。

回到剛剛我所準備的圖片,為了實驗這些數據,所以這些圖片都有經過預處理。

  • 訓練集圖片僅以人臉五官為主,並特別去背,尺寸為 768x768 的大小。
  • 正規化圖片 (REGULARIZATION IMAGES),為 人物本人 半身、全身照,並裁切成 768x768 的大小。

以訓練人臉五官的概念來說,REGULARIZATION IMAGES 的基本用意在於,確保人的頭部(或臉部)的相對位置,在 REGULARIZATION IMAGES 是正確的。換句話說,這樣做的用意,是不會讓你訓練出來的人臉出現在不該出現的位置上。如果以剛才的汽車為例子,就是你不會產出臉長在肚子上的情況。請留意,我這邊是使用 本人 的照片來當作 REGULARIZATION 的圖片使用,如果你的訓練集圖片與 REGULARIZATION IMAGES 是不同的人,我還是建議把 REGULARIZATION IMAGES 裡面的人臉抹掉,避免訓練出什麼奇怪的東西。

同理,衣服、鞋子或是其他飾品的概念也是雷同,我後面會提到。

好的,根據 剛剛那篇文章,我們準備的資料夾如下,

TrainData
    - 50_hina wife a woman
        - IMG_01.png
        - IMG_01.txt
        - ...
        - ...
    - reg
        - 20_hina wife a woman
            - IMG_123.png
            - IMG_456.png

其中 100_hina a woman 就放有我們的訓練集圖片,而 20_hina a woman 則放有我們的正規化圖片 (REGULARIZATION IMAGES)。這邊就不解釋 100_20_ 是什麼意思了。至於 .txt 檔案怎麼產生?你可以使用 Stable Diffusion WEBUITrain/Preprocess images 工具來產生,或是使用 Kohya_ss GUIUtilities/BLIP Captioning 來產生都可以。產出的檔案記得要打開來確認一下,有沒有跟圖片不符合的描述。 例如我老婆的照片有 12 張被加上 1boy 的提詞(我沒有業配),檢查並且調整好提詞之後,再繼續往下做。

請注意!正規化圖片 (REGULARIZATION IMAGES) 可以不使用提詞文字檔,你堅持要用也可以。


Lora 訓練設定與過程

接著,我把他放到 Kohya_ss GUI 裡面,使用 Dreambooth LoRA 開始跑訓練模型,基本上的參數大概是這樣,

{
    ss_batch_size_per_device:  "1",
    ss_bucket_no_upscale:  "True",
    ss_cache_latents:  "True",
    ss_caption_dropout_every_n_epochs:  "0",
    ss_caption_dropout_rate:  "0.0",
    ss_caption_tag_dropout_rate:  "0.0",
    ss_clip_skip:  "2",
    ss_color_aug:  "False",
    ss_enable_bucket:  "True",
    ss_epoch:  "10",
    ss_face_crop_aug_range:  "None",
    ss_flip_aug:  "True",
    ss_full_fp16:  "False",
    ss_gradient_accumulation_steps:  "1",
    ss_gradient_checkpointing:  "False",
    ss_keep_tokens:  "0",
    ss_learning_rate:  "5e-05",
    ss_lowram:  "False",
    ss_lr_scheduler:  "constant",
    ss_lr_warmup_steps:  "0",
    ss_max_bucket_reso:  "768",
    ss_max_token_length:  "150",
    ss_min_bucket_reso:  "320",
    ss_mixed_precision:  "bf16",
    ss_network_alpha:  "128.0",
    ss_network_dim:  "128",
    ss_network_module:  "networks.lora",
    ss_noise_offset:  "None",
    ss_num_epochs:  "10",
    ss_optimizer:  "bitsandbytes.optim.adamw.AdamW8bit",
    ss_output_name:  "hina_lora_v2",
    ss_resolution:  "(768, 768)",
    ss_sd_model_name:  "runwayml/stable-diffusion-v1-5",
    ss_seed:  "1234",
    ss_shuffle_caption:  "False",
    ss_text_encoder_lr:  "5e-05",
    ss_total_batch_size:  "1",
    ss_training_comment:  "None",
    ss_unet_lr:  "0.0001",
    ss_v2:  "False"
}

上面的內容翻譯一下大概是(請注意!以下設定是基於實驗,如果你的設備撐不下來請斟酌使用),

  1. Source Model 使用 runwayml/stable-diffusion-v1-5
  2. Mixed precision, Mixed precision 使用 bf16 你要改用 fp16 也可以。
  3. Caption Extension 填入 .txt
  4. Learning rate 預設是 0.0001,而我使用的是 0.00005
  5. LR Scheduler 選 constant 也有人建議 cosine_with_restarts(效果更好?)。
  6. LR warmup 目前看得到的教學都寫 0,但 Kohya_ss GUI 預設是 10,我這次訓練使用 0
  7. Optimizer 選擇 AdamW8bit
  8. Network Rank (Dimension)、Network Alpha 我都使用 128,後者 (Network Alpha) 可以比前者小,用 64 之類的。
  9. Max resolution 我們準備的圖片是 768x768,這邊就寫 768
  10. Clip skip 寫 2(魔術數字,效果較好)。
  11. 勾選 Enable buckets
  12. Max Token Length 我這次使用 150
  13. 勾選 Use xformers, Flip augmentation
  14. 如果你的顯卡 VRAM 在 8GB 以下,請勾選 Memory efficient attention

以下為了實驗所以才這樣設定,

  • 勾選 Save training state
  • Max train epoch 寫 10,大家都說訓練 10 次,甚至 20 次,真的會比較好?
  • Max num workers for DataLoader 我這邊使用 8

另外,大部分的教學會提及 Train batch size 的設定,這個數字的意思是每次跑的步數要放幾份資料進去跑,換句話說,如果你的步數是 4,000,而 Train batch size 這裡設定為 2 的話,那麼你的總步數就會變成 2,000 步。

具體來說,比較高的 Train batch size 數字,會加速整個學習的過程,但是相對的,學習精度會下降,所以基本上,這邊必須要看你所提供的訓練集的數量,來決定你是不是要提高這個數字。

通常,市面上的教學都會說「少量的訓練集」訓練出高品質的模型。嗯,關於這一點我還是有點存疑就是。不過由於我是實驗性的測試,所以這個數字我維持在 1 讓他每次學習就跑一個 batch 就好。

當然,我也試過 2 或是 3 甚至是 4,以結果論來說,1 的效果還是比較好,特別是當你的訓練集圖片的數量相當少的時候。

再提醒一次,由於這是實驗,我先說一下使用的機器規格,

  • Intel 13th i5 with 64GB DDR5
  • NVIDIA A4500 20GB GDDR6 ECC

Lora 訓練初步結果

我說明一下上述的訓練過程所使用到的「步數」,在 Kohya_ss 中的訓練,「步數」是由資料夾、圖片數量以及剛剛的 Max train epoch 來決定,所以,根據我準備的資料,我所需要跑的訓練「步數」大概是,

「步數」 = 98 x 100 x 10 = 98,000

也就是說,如果你照抄我的設定,那麼你就會跑 98,000 步,才會把整個訓練跑完。我的機器跑 1 個輪次 (Epoch) 大概要花兩個半小時,

所以跑完 10 個輪次,大概花了我 27 個小時左右。

那麼,這 27 個小時訓練了 10 次的結果如何?我們來看一下。這是每一個輪次會自動輸出的 Sample 圖檔,由左上到右下依序,無固定種子,使用 Euler a 產出圖片。

你可以看到大概是 5, 6, 7 次的訓練輸出是比較理想,而第 8, 9 次就已經出現過度訓練的狀況,第 10 次更是明顯。

接著,我使用 X/Y/Z Polt 分別將每一次的模型,依照權重 0.2, 0.25, 0.4, 0.5, 0.75, 0.8, 1.0 來輸出固定種子檔案,用以比較差異。

這是第 1~3 次 Epoch 所輸出的結果。
這是第 4~6 次 Epoch 所輸出的結果。
這是第 7~10 次 Epoch 所輸出的結果。

從上述結果中,紅色框線的部分是我個人比較可以接受的區塊。所以就結論來看,我們準備了 98,000 步的訓練,其實在第 7 個輪次 (Epoch) 就已經能符合預期。換句話說,多次的訓練並不一定能拿到比較好的結果。但是,這邊我必須打個問號,如果是高解析度圖片,訓練步數真的可以少一點嗎?


LoRA Block Weight 權重調配

這是一個 Stable Diffusion 的外掛,他可以將 LoRA 所作用域的 17 層分別給予權重(至於哪 17 層就不要問我了)。

LoRA Block Weight

這個外掛可以自定義權重調配的變數,也就是說你可以在各層中間去調配每一層的權重,用以去跟其他的 Lora 做混搭,或是與原本的模型調配做出更好的輸出。這邊的範例調配數值是,

HINA4:0.25,0.25,0.25,0.25,0.75,0.5,0.75,0.75,0.8,0.8,0.8,0.85,0.85,0.85,1,1,1
使用 LoRA Block Weight 做比較

人物由於沒有「畫風」的問題,所以中間層可以拉大,至於 OUT 的部分是否要放大就看訓練的資料,每一種 Lora 的調配都不太一樣,需要慢慢嘗試才知道什麼結果比較好。

使用 LoRA Block Weight 做比較

LoRA 高解析度訓練

最初我們提及了訓練的資料夾結構與正規化圖片 (REGULARIZATION IMAGES),我這邊也測試了一個高解析度的輸出,有興趣的人可以參考一下,

TrainData
    - 100_hina wife a woman
        - IMG_01.png
        - IMG_01.txt
        - ...
        - ... 共 20 張,1024x1024 圖片,去背五官,多角度照片。
    - 20_hina portrait
        - IMG_001.png
        - IMG_001.txt
        - ...
        - ... 共 50 張,768x768 圖片,全身、半身,適度去背,多角度照片。
    - reg
        - 5_hina wife a woman
            - IMG_123.png
            - IMG_456.png
            - ...
            - ... 共 500 張,全身、半身,多角度照片(可以跟底下資料夾共用同一份圖檔)。
        - 5_hina portrait
            - IMG_123.png
            - IMG_456.png
            - ...
            - ... 共 500 張,全身、半身,多角度照片(可以跟上方資料夾共用同一份圖檔)。

請注意 reg 底下正規化圖片 (REGULARIZATION IMAGES) 資料夾的名稱,必須與訓練集相同,只有步數不同。接著關於訓練輪次 (Epoch) 的問題,這樣的訓練總共需要,

「步數」 = (20 x 100 + 20 x 50) x epoch = 3000 x epoch

另外在 Additional parameters 的地方,由於我這邊所使用的圖片介於 512px ~ 1024px 之間,所以額外填入了參數,

--min_bucket_reso=512 --max_bucket_reso=1024

關於訓練所使用的參數,可以參考這個地方:

Train network

Fine Tune

我使用的輪次 (Epoch) 是 8,這樣跑下去的結果大概是這樣:

Portrait
Full body shot

那麼,準備資料集怎麼做會比較好?剛剛有說了,扣除掉你想要訓練的人臉跟要準備的正規化圖片 (REGULARIZATION IMAGES) 之外,這邊具體用一張圖片來說明訓練集的安排。

訓練集提取區塊

根據上面這張照片,你可以提取的部分有:

  • 紅框:臉部照片。
  • 橘框:單純訓練「衣服」。
  • 綠框:單純訓練「鞋子」。
  • 藍框:去除臉部照片,可用於訓練身體動作。
  • 紫框:全身照片,可用於正規化圖片 (REGULARIZATION IMAGES)。

所以說,對於模型訓練你就能分的更細:

TrainData
    - X_hina wife a woman
        - IMG_01.png
        - IMG_01.txt
        - ...
        - ... 放紅框的照片。
    - Y_hina portrait
        - IMG_001.png
        - IMG_001.txt
        - ...
        - ... 放籃框的照片。
    - Z_hina clothing
        - IMG_001.png
        - IMG_001.txt
        - ...
        - ... 放橘框的照片。
    - W_hina shoes
        - IMG_001.png
        - IMG_001.txt
        - ...
        - ... 放綠框的照片。
    - reg
        - R_hina wife a woman
            - IMG_123.png
            - IMG_456.png
            - ...
            - ... 放紫色框照片。
        - O_hina portrait
            - IMG_123.png
            - IMG_456.png
            - ...
            - ... 放紫色框照片。

其中的 X,Y,Z,W,R,O 請自行依照你的圖片數量,來斟酌你每張圖片所要訓練的次數。另外,我這邊只針對臉部及半身照片有做正規化 (REGULARIZATION),鞋子跟衣服的部分沒有,你要做也可以,這樣可以在學習過程中,不要讓衣服或是鞋子長在不對的地方。


結語

其實最難的是調整提詞文字檔。

Hina Chen
偏執與強迫症的患者,算不上是無可救藥,只是我已經遇上我的良醫了。
Taipei