寫在最前面的,本篇文章的內容僅為個人實驗以及在各種訓練產生的結果上,總結出來的一些結論,並無法完全通用在各種情況。如果以下所提及的所有內容與結果與你有所雷同,那就雷同吧。
前言
以下的訓練皆使用 Kohya_ss GUI 24.1.4 進行,整個運作環境版本如下:
- torch 2.2.2
- torchvision 0.17.2
- xformers 0.0.25.post1
- lycoris_lora 3.0.0.post1
附帶一題,我個人所使用的 kohya-ss 訓練工具中,有經過特殊的客製化,包含了:
- 使用 LogNorm 干涉 timesteps,請參考這個 Issue
- 使用 min-SNR 5 + Debiased 兩階段訓練
所以我所跑出來的訓練可能會跟大家不太一樣。就當作是一個提醒就好,使用本文章所提及的任何資料,所訓練出來的結果,可能比較好,也可能比較差,差異可能在於我的訓練工具跟你的不太一樣。
提詞工具
我目前所使用的提詞工具,是由 @gesne2egee 所提供的,加上我額外撰寫的 phi3,
當然還有純手動提詞,但這個方法實在太地獄,除非你有精準標示的需求,不然還是先用工具跑一次會比較快一點。
無論你用上述哪一種提詞工具,我們目前所使用的提詞都是使用 --enable_wildcard
的方式進行,也就是說,提詞出來的結果會包含多行的提詞結果。同時,如果需要,也會使用 --keep_tokens_separator="___"
來固定特定區塊標籤。
如果不清楚 --enable_wildcard
與 --keep_tokens_separator
可以參考一下 kohya 那邊的說明:
提詞方法
對於一張圖片的描述,概括目前所知道對於特徵描述的方式,大概有以下幾種:
- 刪除特徵標籤
- 保留部分特徵標籤,刪除特定特徵標籤
- 全保留不刪除
- 使用觸發詞
對於標記方式,整篇文章我都會使用自行製作甲冑 Lora 來當作例子,雖然絕大部分他是手動標籤,但也恰好可以說明自動標籤與手動標籤的差異性。
特徵標籤
如其名,我們會希望畫面中的「特徵」被學起來。因此,這個部分就有了兩種作法,各有各的擁護者,
- 刪除特徵標籤
- 不刪除特徵標籤
對於第一次接觸訓練的人來說,或許會有個疑問,為什麼要 刪除特徵標籤 ?這裡必需要提及一下在 Kohya 訓練當中的 Class token
這件事情,也就是,在你的訓練概念資料夾名稱的 10_Concept
這件事。
所謂的 Concept
就是一種 Class token
,當你把這張圖片的特徵標籤 刪除 時,在整個訓練過程中,相對於基底模型是全新概念的情況下,而你又沒有將他標記,那麼他就會被歸類到這個 Class token
裡面。
但是,當你有使用觸發詞時,整個畫面當中的全新概念,是會有高機率會被歸類到你的觸發詞當中(當你的訓練使用 Shuffle caption
,並使用 Keep n Token = 1
時)。
而,不刪除特徵標籤的情況,則會因為你是否有使用觸發詞,且 Shuffle caption
與 Keep n Token = 1
時,這個特徵標籤的強度會因此而改變。
另外,所謂的「特徵標籤」對於基底模型來說,是不是你要的那個東西,那又是另外一個故事了。我在後面 觸發詞 會再提及這件事情。
整體而言,特徵標籤的整理順序可以照這個方向去做,
- 觸發詞(如果你需要的話)
- 最明顯的主要目標
- 依附在主要目標上面的細節
- 次要明顯的目標
- 依附在次要明顯的目標上面的細節
- 整體攝影鏡位、角度、光影等
- 背景
舉例來說,
上述的圖片,一般來說標記的方向為:
- 觸發詞
samurai armor
- 特殊物件
- 家徽
- 腰帶
- 胸甲
- 上胸甲
- 其他細節
- 動作或姿勢
- 背景
對於這麼複雜的學習目標,首先我們必需要釐清幾個重點,
- 他沒有人物,所以描述詞不能出現
1girl
,1boy
這種描述。 - 局部描寫,所以需要說明他是
close-up shot
或medium shot
等鏡位。 - 有特徵被裁切,所以要把被切掉的特徵稍微描述,例如
XXX out of frame
。 - 去掉畫面沒有的描述(自動標籤很容易有幻覺)。
那麼,我們使用 Phi3 來對剛剛的圖片自動下標籤,會得到這樣的結果,
自然語言的長描述,
the image features a large black drum with a gold emblem on it, the drum is adorned with blue and white patterns and has two golden tassels hanging from its sides, in front of the drum lies a blue and gold robe draped over a chair, the drum is set against a dark gray background, the drum occupies a significant portion of the frame and is positioned centrally, the chair is located in the lower right corner of the frame, the drum's design is intricate with a combination of geometric shapes and floral motifs.
而以下是 Phi3 所整理出來的短標籤,
focus on drum, the main object in the foreground, shadows, the main object, still life, close-up, the main object is a drum in front of a drum, drum, chair, the main object is a drum, indoors, the main object is a drum in front of a drummer, the main object is a drum with a gold emblem, objects, the main object in the center, portraits, background
首先,我們需要先解決「幻覺」的部分,
the main object is a drum in front of a drummer
他不是鼓,你會發現上面出現了很多關於鼓的描述,所以 Phi3 把這個胸甲誤認為「鼓」了,所以必須清理這個部分。畢竟這個物件太特殊,所以你要做的手動的部分就會特別多。
清理過程我就不贅述了。
刪除特定特徵標籤
當你的資料集不夠豐富,或是單一特徵素材過於稀少時,為了不要讓特徵鎖定的狀況過於嚴重,那麼可以採用這種方式來「擴充」資料集的資料。
比較簡單的作法是,
- 有一張圖片 A1,特徵標籤 B, C, D, E
- 複製一張圖片 A2,特徵標籤 C, D, E
- 複製一張圖片 A3,特徵標籤 B, E
你會有三張 A 的圖片,你可以讓他有些微的不同,例如尺寸裁切、左右翻轉、上下翻轉等。而這三張訓練集的差異在於,
- A1 包含了所有特徵標籤
- A2 刪除了 B 特徵標籤
- A3 刪除了 C, D 特徵標籤
在訓練的時候,A2 缺少的 B 特徵,就會被歸入觸發詞或 Class token
裡面,同樣的道理,對於 A3 來說,C, D 特徵也會被歸入觸發詞或 Class token
裡。
這樣的作法是利用刪除特定標籤,將特定標籤所指定的「特徵」塞到觸發詞或 Class token
裡面,用以強調該特徵在觸發詞或 Class token
的表現。
而通常,這種訓練方式需要額外做概念平衡,避免過多的概念被集中。換句話說,你需要將這樣「複製」的訓練資料集,分別放到不同的概念資料夾中,去做步數平衡。
舉例來說,我所製作的這個 Lora 對於「小面積臉部」有修正效果,那麼,怎麼定義「小面積臉部」?
說穿了就是區分為三個概念,
4_close up
6_portrait
12_full body
在這三個概念中,都包含了有人臉的照片,但是,在 full body
的概念裡面,就不會特別描述臉部,而著重在肢體表現,將臉、上半身、髮型的特徵全數刪除。
全標籤
老實說我一開始訓練的時候也是使用全標籤的方式進行,而實際操作的結果也沒有特徵練不好的問題。底下這兩個結果你看得出來哪一個有刪標籤嗎?
訓練集中不刪除特徵標籤的訓練,對於細節特徵的保留上會偏弱,但這個「偏弱」的程度取決於你的資料夾品質的好壞,跟是否有針對概念作一些特殊的平衡動作。
在全標籤的操作下,操作的手法可以跟刪除特定標籤雷同,將需要強化的部分「多看幾次」,也就是說,當我需要針對臉部特徵作強化時,我們的概念資料夾就可以這樣來區分,
10_close up face
4_hina ai girl
在這樣的描述詞的情況下,礙於訓練器對於 Text Encoder 訓練的權重問題,根據實驗,在前 20 個 Token 的表現會是最好的,而越後面的 Token 的表現就會偏弱。所以,因為全保留的關係,你的描述詞就需要稍微調整一下。
全描述的表達方式以下方結構是對於訓練來說比較有效的,
觸發詞, ___特徵 1, 特徵 2, ..., 特徵 n___ 其他短描述, 長描述
無論你是否要刪除標籤,這樣的結構基本上一體適用,只是對於全保留的情況下,這樣的描述在標籤平衡上會比較好一點。
由於標籤權重的關係,所以即便使用這樣的結構,你的標籤也需要將比較重要的放在前面,即便在有使用 Shuffle caption
或 --keep_tokens_separator="___"
的情況下,雖然順序會被打亂,但請在 ___
之前盡可能不要讓特徵超出 20 個 Token 數。
在全長標籤(自然語言標籤)的訓練當中,不使用 Shuffle caption
也不使用觸發詞的情況下,訓練出來的效果會偏弱(不考慮過度訓練的情況)。所以若你使用的是自然語言描述標籤,也請將重點放在前面。
觸發詞
使用觸發詞的狀況有幾種方式,先以觸發詞、基底模型之間的反應來看,
觸發詞是否有反應 | 訓練目標說明 | 訓練難度 |
---|---|---|
無反應 | 獨立特定概念 | 中等 |
完全不相符 | 可用於清洗概念,若非此目的,則不適合在這個基底模型訓練 | 高 |
接近 | 混合、融合概念,將訓練集的特徵融入模型中,用於泛化或多型表現 | 中等 |
幾乎完全相符 | 可用於清洗概念,若非此目的,則此訓練效果很低 | 高 |
所以挑選觸發詞請先從你訓練的基底模型開始測試。而,除了觸發詞之外,上面所提到的特徵標籤也是有相同的問題。在訓練的時候,我們可能需要避免的是特定特徵被基底模型污染這件事。
舉例來說,當我呼叫 samurai armor
的時候,他告訴我的是什麼東西?當基底模型告訴你的結果跟你想訓練的概念相近的時候,那麼這個觸發詞就很適合使用。
然而,觸發詞也是會有污染的情況,例如:
shoulder armor
neck armor
arm armor
body armor
因為都有 armor
這個字,在 samurai armor
的使用上,提詞權重就有機會被這幾個提示詞「分走」。所以,如果要避免觸發詞與其他用詞有干擾的情況,觸發詞可能需要更換會比較保險。
提詞結構
我在上面有提到了提詞工具跟標籤整理順序,我們這邊就來看 --enable_wildcard
與 --keep_tokens_separator="___"
訓練設定相關的提詞結構。
目前實作了約 100 爐 SDXL 測試 MoE/Florence2/Phi 提詞、手動提詞等等,目前實驗下來比較好的結構為:
觸發詞, ___特徵 1, 特徵 2, ..., 特徵 n___ 其他短描述, 長描述
觸發詞, 長描述
觸發詞, 其他短描述, 長描述
觸發詞, ___特徵 1, 特徵 2, ..., 特徵 n___
使用 --enable_wildcard
的情況下,會使用上述四組提示標籤來隨機抽一組使用,這樣的方式我們的目的為,
- 第一組等於全標籤,不刪標籤,平衡第二、三組帶來的特徵鎖定,增加泛化能力
- 第二組刪除所有特徵標籤,自然語言,將所有模型不認識的特徵全部交給觸發詞
- 第三組同第二組,但保留非特徵標籤的其他短描述,增加泛化能力
- 第四組全特徵標籤,將部分非特徵標籤交給觸發詞,降低觸發詞特徵鎖定,增加多型能力
這麼一來,我們在上面所提到的複製圖片的方式,在這邊就可以使用完全相同的提詞,僅需要複製圖片,並且把圖片作適當的修剪即可。
舉例來說,
a fantasy armor in this image, {rating:general|}, the whole image consists of the following: ___solo, full armor, gauntlets, pauldrons, red cape, knight, breastplate, upper body, gem, standing, arch, long hair, holding sword, horns, 1other, facing viewer, ambiguous gender, indoors, white hair, male focus, statue___, in the image, a man dressed in medieval armor is standing in front of a building, he is holding a sword and appears to be facing the viewer, the scene seems to be a historical or artistic representation, possibly depicting a knight or warrior from a bygone era, the man's attire and the presence of the sword suggest that he might be a figure of historical significance or a symbol of strength and courage.
大概這一類的提詞,我僅使用了一張圖片,分區塊裁切成 12 張不同的素材,用來訓練一個 SDXL 的 Lora,
訓練不更新 Text Encoder aka. network_train_unet_only
在 SDXL 一開始推出訓練的時候,其實在 Kohya 那邊有建議使用 --network_train_unet_only
這件事情。而,之所以會這樣建議,是因為 SDXL 有兩個 Text Encoder 的關係,所以無法保證 Te 的訓練效果。
而,在 SD1.5 的時代,訓練 Te 這件事情很容易會遇到一個狀況,
絕大部分的 Lora 的 Text Encoder 可能都過度訓練。
為何會這麼說?關於 Text Encoder / Unet 過度訓練的狀況,可以參考這一篇文章:
Overtrained Text Encoder vs Overtrained UNET [Stable Diffusion Experiment]
TL;DR
我先說結論,
- 過度訓練的 Text Encoder 會更難以表達 Prompt 的描述
- 過度訓練的 Unet 會疊加更多風格,但仍舊會遵循 Prompt 的描述
所以,Text Encoder 過度訓練的結果會讓畫面更偏離,而不是更貼近你的訓練目標。所以針對這個部分,我也做了一連串的實驗。
所謂的 --network_train_unet_only
是在訓練時不更新 Text Encoder 的部分,也就是說,訓練的時候還是有給他提示詞的。關於完全不提詞的情況,我在後面會提出來聊一下。
在完全相同參數、設定、資料集,僅 --network_train_unet_only
的差異下,訓練出來的結果有以下的差別,
- 觸發詞、關鍵提詞其實會有反應,但變弱了
- 對於背景、非主體相關(或關連)的表現變好
- 在高權重下,偽影的狀況少很多
- 同樣的高權重下,關鍵細節會略少
- 跟其他 Lora 搭配的相容度比較好
同樣的,你應該可以很明確挑出哪一張是 --network_train_unet_only
吧?
為了避免提詞偏弱,特別是概念觸發詞或特殊特徵提詞,所以在訓練的時候可以壓低 Text Encoder Lr 來達到類似 --network_train_unet_only
的效果,但是又保有更新 Text Encoder 的部分的好處。
所以,我使用了 Lion8bit 的訓練,並且測試了兩組 Text Encoder Lr 的設定,對我來說,以下的設定還蠻適合我的訓練資料集,
UNet Lr 5e-5
Text Encoder Lr 5e-7
你可以對比一下,在更低的 Text Encoder 學習率的情況下,反應情況會是如何。
同時,我們來看有無 --network_train_unet_only
的差異。
不提詞
我實在不會解釋 unconditional 到底是怎麼回事。但這一篇文章是講提詞的,所以這邊我也不多說什麼,就說一些實驗上遇到的狀況就好,
- 垃圾正規化資料集,不提詞有奇效(原因不明)
- 訓練集可以少量混入不提詞的資料
- 基底模型不提詞如果可以抽到你的概念,拿來當正規化
在這裡所謂的不提詞,並不是單純指不給 .txt
這樣的提詞檔案,而是連訓練資料夾的 Class token
都省略的情況,換句話說,我的資料夾會長這樣,
image/
10_
reg/
8_
這樣在訓練時會掉入 unconditional 裡面(理論上),雖然不太清楚原理,但是這樣在某些訓練上有奇妙的效果,有興趣的人可以自行嘗試。
另,我上述所有的訓練,皆使用垃圾正規化資料集,不提詞。
小結
提詞的時候,Google 翻譯或是 ChatGPT 是你的好朋友(認真)。