@@ -18,7 +18,7 @@ def extract_lora(diff, rank):
1818 else :
1919 diff = diff .squeeze ()
2020
21- U , S , Vh = torch .linalg .svd (diff .float ())
21+ U , S , Vh = torch .linalg .svd (diff .float (), full_matrices = False )
2222 U = U [:, :rank ]
2323 S = S [:rank ]
2424 U = U @ torch .diag (S )
@@ -36,65 +36,61 @@ def extract_lora(diff, rank):
3636 return (U , Vh )
3737
3838
39- def do_lora_handle (base_data , other_data , rank , prefix , require , do_bias , callback ):
39+ def do_lora_handle (base_data , other_data , rank , callback ):
4040 out_data = {}
4141 device = comfy .model_management .get_torch_device ()
4242 for key in base_data .keys ():
4343 callback ()
4444 if key not in other_data :
4545 continue
46- base_tensor = base_data [key ].float ()
47- other_tensor = other_data [key ].float ()
48- if key .startswith ("clip_g" ):
49- key = "1." + key [len ("clip_g." ):]
50- elif key .startswith ("clip_l" ):
51- key = "0." + key [len ("clip_l." ):]
52- if require :
53- if not key .startswith (require ):
54- print (f"Ignore unmatched key { key } (doesn't match { require } )" )
55- continue
56- key = key [len (require ):]
46+ if key .endswith (".weight_scale" ) or key .endswith (".comfy_quant" ):
47+ continue
48+ base_tensor = base_data [key ]
49+ other_tensor = other_data [key ]
50+ if key .endswith (".weight" ):
51+ fixed_key = key [:- len (".weight" )]
52+ scale_key = f"{ fixed_key } .weight_scale"
53+ if scale_key in base_data :
54+ scale = base_data [scale_key ]
55+ base_tensor = base_tensor .to (dtype = torch .bfloat16 ) * scale
56+ if scale_key in other_data :
57+ scale = other_data [scale_key ]
58+ other_tensor = other_tensor .to (dtype = torch .bfloat16 ) * scale
59+ elif key .endswith (".bias" ) or key .endswith (".scale" ) or key .endswith (".lin" ):
60+ fixed_key = key
5761 if base_tensor .shape != other_tensor .shape :
5862 continue
5963 target_dtype = base_tensor .dtype
6064 if target_dtype == torch .float8_e4m3fn or target_dtype == torch .float8_e5m2 :
6165 target_dtype = torch .bfloat16
6266 base_tensor = base_tensor .to (dtype = target_dtype )
6367 other_tensor = other_tensor .to (dtype = target_dtype )
64- diff = other_tensor .to (device ) - base_tensor .to (device )
68+ diff = other_tensor .to (device , dtype = torch . float32 ) - base_tensor .to (device , dtype = torch . float32 )
6569 other_tensor = other_tensor .cpu ()
6670 base_tensor = base_tensor .cpu ()
6771 max_diff = float (diff .abs ().max ())
6872 if max_diff < 1e-5 :
6973 print (f"discard unaltered key { key } ({ max_diff } )" )
7074 continue
71- if key .endswith (".weight" ):
72- fixed_key = key [:- len (".weight" )].replace ('.' , '_' )
73- name = f"lora_{ prefix } _{ fixed_key } "
74- if len (base_tensor .shape ) >= 2 :
75- print (f"extract key { name } ({ max_diff } )" )
76- out = extract_lora (diff , rank )
77- up = out [0 ].contiguous ().to (dtype = target_dtype ).cpu ()
78- down = out [1 ].contiguous ().to (dtype = target_dtype ).cpu ()
79- if up .isnan ().any () or up .isinf ().any ():
80- print (f"bad data for { name } .lora_up.weight" )
81- continue
82- if down .isnan ().any () or down .isinf ().any ():
83- print (f"bad data for { name } .lora_down.weight" )
84- continue
85- out_data [f"{ name } .lora_up.weight" ] = up
86- out_data [f"{ name } .lora_down.weight" ] = down
87- else :
88- print (f"ignore valid raw pass-through key { name } ({ max_diff } )" )
89- elif key .endswith (".bias" ) and do_bias :
90- fixed_key = key [:- len (".bias" )].replace ('.' , '_' )
91- name = f"lora_{ prefix } _{ fixed_key } "
92- print (f"extract bias key { name } ({ max_diff } )" )
93- diff = diff .contiguous ().to (dtype = target_dtype ).cpu ()
94- if diff .isnan ().any () or diff .isinf ().any ():
95- print (f"bad data for { name } .diff_b" )
75+ if len (base_tensor .shape ) >= 2 and base_tensor .numel () < 1024 :
76+ print (f"extract key { fixed_key } ({ max_diff } )" )
77+ out = extract_lora (diff , rank )
78+ up = out [0 ].contiguous ().to (dtype = target_dtype ).cpu ()
79+ down = out [1 ].contiguous ().to (dtype = target_dtype ).cpu ()
80+ if up .isnan ().any () or up .isinf ().any ():
81+ print (f"bad data for { fixed_key } .lora_up.weight" )
82+ continue
83+ if down .isnan ().any () or down .isinf ().any ():
84+ print (f"bad data for { fixed_key } .lora_down.weight" )
85+ continue
86+ out_data [f"{ fixed_key } .lora_up.weight" ] = up
87+ out_data [f"{ fixed_key } .lora_down.weight" ] = down
88+ else :
89+ out = diff .contiguous ().to (dtype = target_dtype ).cpu ()
90+ if out .isnan ().any () or out .isinf ().any ():
91+ print (f"bad data for { fixed_key } " )
9692 continue
97- out_data [f"{ name } .diff_b " ] = diff
93+ out_data [f"{ fixed_key } .diff " ] = out
9894
9995
10096 return out_data
@@ -108,13 +104,10 @@ def INPUT_TYPES(s):
108104 return {
109105 "required" : {
110106 "base_model" : ("MODEL" , ),
111- "base_model_clip" : ("CLIP" , ),
112107 "other_model" : ("MODEL" , ),
113- "other_model_clip" : ("CLIP" , ),
114108 "rank" : ("INT" , {"default" : 16 , "min" : 1 , "max" : 320 }),
115109 "save_rawpath" : ("STRING" , {"multiline" : False }),
116110 "save_filename" : ("STRING" , {"multiline" : False }),
117- "save_clip" : ("BOOLEAN" , {"default" : True }),
118111 "metadata" : ("STRING" , {"multiline" : True }),
119112 }
120113 }
@@ -125,29 +118,18 @@ def INPUT_TYPES(s):
125118 OUTPUT_NODE = True
126119 DESCRIPTION = "Internal node, do not use directly - extracts a LoRA from the difference between two models. This is used by SwarmUI Utilities tab."
127120
128- def extract_lora (self , base_model , base_model_clip , other_model , other_model_clip , rank , save_rawpath , save_filename , save_clip , metadata ):
121+ def extract_lora (self , base_model , other_model , rank , save_rawpath , save_filename , metadata ):
129122 base_data = base_model .model_state_dict ()
130123 other_data = other_model .model_state_dict ()
131124 key_count = len (base_data .keys ())
132- if save_clip :
133- if base_model_clip is None or other_model_clip is None :
134- print ("Warning: save_clip is True but CLIP model(s) are unavailable (model may not have embedded CLIP, e.g. Flux), skipping CLIP extraction" )
135- save_clip = False
136- else :
137- key_count += len (base_model_clip .get_sd ().keys ())
138125 pbar = comfy .utils .ProgressBar (key_count )
139126 class Helper :
140127 steps = 0
141128 def callback (self ):
142129 self .steps += 1
143130 pbar .update_absolute (self .steps , key_count , None )
144131 helper = Helper ()
145- out_data = do_lora_handle (base_data , other_data , rank , "unet" , "diffusion_model." , False , lambda : helper .callback ())
146- if save_clip :
147- # TODO: CLIP keys get wonky, this probably doesn't work? Model-arch-dependent.
148- out_clip = do_lora_handle (base_model_clip .get_sd (), other_model_clip .get_sd (), rank , "te_text_model_encoder_layers" , "0.transformer.text_model.encoder.layers." , False , lambda : helper .callback ())
149- out_clip = do_lora_handle (base_model_clip .get_sd (), other_model_clip .get_sd (), rank , "te2_text_model_encoder_layers" , "1.transformer.text_model.encoder.layers." , False , lambda : helper .callback ())
150- out_data .update (out_clip )
132+ out_data = do_lora_handle (base_data , other_data , rank , lambda : helper .callback ())
151133
152134 # Can't easily autodetect all the correct modelspec info, but at least supply some basics
153135 out_metadata = {
0 commit comments