LoRA Merger
このリポジトリはComfyUI上でLoRAのマージを実装したものです。
ノード
全てのノードはlora_mergeに入っています。
Load LoRA Weight Only: LoRAを単体で読み込みます。LoRA LoRA from Weight: LoRAをモデルに適用します。Merge LoRA: 二つのLoRAをマージします。Save LoRA: LoRAをComfyUI/models/lorasにセーブします。※設定したstrengthを適用したLoRAが保存されます。
マージについて
4つ設定があります。dtypeはLoRAの型になります。ファイルサイズを軽くしたいときはfloat16やbfloat16にしてください。
rank, deviceはmodeをsvdにしたときのみ参照されます。
modeの説明:
add:加算によってマージします。二つのLoRAのrankがイコールではない場合、エラーが起きます。また二つのLoRAが全く違うものである場合精度は低くなります。concat:結合によってマージします。rankは二つのLoRAの合計になってしまいますが、正確なマージになります。svd:特異値分解を利用してマージします。rankを設定することで、自由にマージ先のrankを変えることができます。ただし時間がかかるほか、精度も少し落ちます。deviceでGPUで計算するかCPUで計算するか選べます。thresholdを1.0未満にすると、rankを自動的に決定するモードに変わります。
リサイズについて
lora2を省略して、modeをsvdにすると、リサイズモードになります。
ブロック別重みについて
LoRA LoRA from Weightのlbw欄に、sd-webui-lora-block-weightに沿った文字列を記入するといい感じになります。プリセットはpreset.txtで追加できます。RとかUには未対応です。
注意点
- メタデータのことはよく分からないので何も考慮してません。
- LyCORISのLoHAやLokrには未対応です。というかこれらはマージできるのか?
- 異なる学習コードで作成されたLoRA同士のマージはうまくいかない可能性があります。
数学的な話
二つのLoRAのup層、down層、alpha、rank、strengthをそれぞれ、 A_i, B_i, \alpha_i, r_i, w_i (i = 1,2) とします。
すると、欲しいLoRAは、以下の通りです。
\displaystyle{\frac{\alpha}{r}}AB = w_1\displaystyle{\frac{\alpha_1}{r_1}}A_1B_1 + w_2\displaystyle{\frac{\alpha_2}{r_2}}A_2B_2
1.どちらか一方のLoRAにしかないモジュールについて
片方しかない場合、ある方 i のLoRAを使って、
A = \sqrt{w_i}A_i,\ \ B = \sqrt{w_i}B_i,\ \ \alpha = \alpha_i
とします。行列積をとることを考えて、 w_i の二乗根をとります。どちらか一方にだけそのままかけるという方法でもいいと思いますが、計算精度の問題でどちらがいいのかわからないのでとりあえずこっちにしてます。
- 両方ある場合
モードによって異なります。
add:
down層、up層をそれぞれ重み付き加算することによってマージします。
A = w_1A_1 + \displaystyle{\sqrt{\frac{\alpha_2}{\alpha _1}}}w_2A_2,\ \ B = w_1B_1 + \displaystyle{\sqrt{\frac{\alpha_2}{\alpha _1}}}w_2B_2,\ \ \alpha = \alpha_1,\ \ r=r_1=r_2
とします。
\displaystyle{\frac{\alpha}{r}}AB = {w_1}^2\displaystyle{\frac{\alpha_1}{r}}A_1B_1 + w_1w_2\displaystyle{\frac{\sqrt{\alpha_1\alpha_2}}{r}}A_1B_2 + w_1w_2\displaystyle{\frac{\sqrt{\alpha_1\alpha_2}}{r}}A_2B_1 + {w_2}^2\displaystyle{\frac{\alpha_2}{r}}A_2B_2
となります。正直意味わからないんですが、同じLoRAを w_1+w_2=1 でマージしたときに同じLoRAになるよう調整されています。
concat:
down層、up層をそれぞれrank方向に結合することによってマージします。
A = (\sqrt{w_1}\displaystyle{\sqrt{\frac{r_1+r_2}{r_1}}}A_1\ \ \displaystyle{\sqrt{w_2\frac{\alpha_2}{\alpha_1}}}\displaystyle{\sqrt{\frac{r_1+r_2}{r_2}}}A_2),\ \ B = (\sqrt{w_1}\displaystyle{\sqrt{\frac{r_1+r_2}{r_1}}}B_1\ \ \displaystyle{\sqrt{w_2\frac{\alpha_2}{\alpha_1}}}\displaystyle{\sqrt{\frac{r_1+r_2}{r_2}}}B_2)^T,\ \ \alpha=\alpha_1,\ \ r=r_1+r_2
とします。
\displaystyle{\frac{\alpha}{r}}AB = w_1\displaystyle{\frac{\alpha_1}{r_1}}A_1B_1 + w_2\displaystyle{\frac{\alpha_2}{r_2}}A_2B_2
となり完全なマージになります。
w < 0 のときはそのままでは二乗根を計算できないので、絶対値をとって、 A 側に後からマイナスをかけることにしています。
svd
W = \displaystyle{\frac{r}{r_1}}\sqrt{w_1}A_1\sqrt{w_1}B_1 + \displaystyle{\frac{r}{r_2}}\displaystyle{\sqrt{w_2\frac{\alpha_2}{\alpha_1}}}A_2\displaystyle{\sqrt{w_2\frac{\alpha_2}{\alpha_1}}}B_2
として W を特異値分解による低ランク近似で二つの r= rankの行列 A,B に分解します。