1212from lightx2v .models .networks .flux2 .weights .pre_weights import Flux2DevPreWeights , Flux2PreWeights
1313from lightx2v .models .networks .flux2 .weights .transformer_weights import Flux2TransformerWeights
1414from lightx2v .utils .custom_compiler import compiled_method
15- from lightx2v_platform .base import global_var
1615
1716
1817class _Flux2TransformerModelBase (BaseTransformerModel ):
@@ -25,248 +24,10 @@ def __init__(self, config, model_path, device):
2524 super ().__init__ (model_path , config , device )
2625 self .in_channels = self .config .get ("transformer_in_channels" , self .config .get ("in_channels" , 64 ))
2726 self .attention_kwargs = {}
28- self ._init_tensor_parallel ()
2927 self ._init_infer_class ()
3028 self ._init_weights ()
3129 self ._init_infer ()
3230
33- def _init_tensor_parallel (self ):
34- if self .config .get ("tensor_parallel" , False ):
35- self .use_tp = True
36- self .tp_group = self .config .get ("device_mesh" ).get_group (mesh_dim = "tensor_p" )
37- self .tp_rank = dist .get_rank (self .tp_group )
38- self .tp_size = dist .get_world_size (self .tp_group )
39- else :
40- self .use_tp = False
41- self .tp_group = None
42- self .tp_rank = 0
43- self .tp_size = 1
44-
45- def _should_load_weights (self ):
46- if self .config .get ("device_mesh" ) is None :
47- return True
48- if dist .is_initialized () and self .use_tp :
49- return dist .get_rank () == 0
50- return super ()._should_load_weights ()
51-
52- def _load_ckpt (self , unified_dtype , sensitive_layer ):
53- if not self .use_tp :
54- return super ()._load_ckpt (unified_dtype , sensitive_layer )
55- original_device = self .device
56- self .device = torch .device ("cpu" )
57- try :
58- return super ()._load_ckpt (unified_dtype , sensitive_layer )
59- finally :
60- self .device = original_device
61-
62- def _load_quant_ckpt (self , unified_dtype , sensitive_layer ):
63- if not self .use_tp :
64- return super ()._load_quant_ckpt (unified_dtype , sensitive_layer )
65- original_device = self .device
66- self .device = torch .device ("cpu" )
67- try :
68- return super ()._load_quant_ckpt (unified_dtype , sensitive_layer )
69- finally :
70- self .device = original_device
71-
72- def _rank_device (self ):
73- ai_device = global_var .AI_DEVICE
74- if ai_device is None :
75- return torch .device ("cpu" )
76- if dist .is_initialized ():
77- return torch .device (f"{ ai_device } :{ dist .get_rank ()} " )
78- return torch .device (ai_device )
79-
80- def _sync_device (self ):
81- ai_device = global_var .AI_DEVICE
82- device_module = getattr (torch , ai_device , None ) if ai_device else None
83- if device_module is not None and hasattr (device_module , "synchronize" ):
84- device_module .synchronize ()
85-
86- def _load_weights_from_rank0 (self , weight_dict , is_weight_loader ):
87- if not self .use_tp :
88- return super ()._load_weights_from_rank0 (weight_dict , is_weight_loader )
89- if self .cpu_offload :
90- raise NotImplementedError ("Flux2 tensor parallel weight loading does not support cpu_offload yet." )
91-
92- global_src_rank = 0
93- target_device = self ._rank_device ()
94-
95- if is_weight_loader :
96- processed_weight_dict = {}
97- meta_dict = {}
98- processed_bias_keys = set ()
99- for key , tensor in weight_dict .items ():
100- split_type = self ._get_split_type (key )
101- if key .endswith (".weight" ) and split_type is not None :
102- split_weights = self ._split_weight_for_tp (key , tensor , self .tp_size )
103- for rank_idx , split_weight in enumerate (split_weights ):
104- rank_key = f"{ key } __tp_rank_{ rank_idx } "
105- processed_weight_dict [rank_key ] = split_weight .contiguous ()
106- meta_dict [key ] = {"shape" : split_weights [0 ].shape , "dtype" : split_weights [0 ].dtype , "is_tp" : True }
107-
108- bias_key = key .replace (".weight" , ".bias" )
109- if bias_key in weight_dict and split_type in ("col" , "ff_fused_col" , "single_fused_col" ):
110- bias_splits = self ._split_bias_for_tp (bias_key , weight_dict [bias_key ], split_type , self .tp_size )
111- for rank_idx , split_bias in enumerate (bias_splits ):
112- processed_weight_dict [f"{ bias_key } __tp_rank_{ rank_idx } " ] = split_bias .contiguous ()
113- meta_dict [bias_key ] = {"shape" : bias_splits [0 ].shape , "dtype" : bias_splits [0 ].dtype , "is_tp" : True }
114- processed_bias_keys .add (bias_key )
115- elif key not in processed_bias_keys :
116- processed_weight_dict [key ] = tensor
117- meta_dict [key ] = {"shape" : tensor .shape , "dtype" : tensor .dtype , "is_tp" : False }
118-
119- obj_list = [meta_dict ]
120- dist .broadcast_object_list (obj_list , src = global_src_rank )
121- synced_meta_dict = obj_list [0 ]
122- weight_dict = processed_weight_dict
123- else :
124- obj_list = [None ]
125- dist .broadcast_object_list (obj_list , src = global_src_rank )
126- synced_meta_dict = obj_list [0 ]
127-
128- distributed_weight_dict = {key : torch .empty (meta ["shape" ], dtype = meta ["dtype" ], device = target_device ) for key , meta in synced_meta_dict .items ()}
129- dist .barrier ()
130-
131- for key in sorted (synced_meta_dict .keys ()):
132- meta = synced_meta_dict [key ]
133- if meta .get ("is_tp" , False ):
134- for rank_idx in range (self .tp_size ):
135- if is_weight_loader :
136- src_tensor = weight_dict [f"{ key } __tp_rank_{ rank_idx } " ].to (target_device , non_blocking = True )
137- else :
138- src_tensor = torch .empty (meta ["shape" ], dtype = meta ["dtype" ], device = target_device )
139- dist .broadcast (src_tensor , src = global_src_rank )
140- if rank_idx == self .tp_rank :
141- distributed_weight_dict [key ].copy_ (src_tensor , non_blocking = True )
142- del src_tensor
143- else :
144- if is_weight_loader :
145- distributed_weight_dict [key ].copy_ (weight_dict [key ].to (target_device , non_blocking = True ), non_blocking = True )
146- dist .broadcast (distributed_weight_dict [key ], src = global_src_rank )
147-
148- self ._sync_device ()
149- return distributed_weight_dict
150-
151- def _get_split_type (self , key ):
152- if ".norm_" in key :
153- return None
154- if key .endswith (".weight" ) and "single_transformer_blocks." in key and ".attn.to_qkv_mlp_proj." in key :
155- return "single_fused_col"
156- if key .endswith (".weight" ) and "single_transformer_blocks." in key and ".attn.to_out." in key :
157- return "single_fused_row"
158- if key .endswith (".weight" ) and (".ff.linear_in." in key or ".ff_context.linear_in." in key ):
159- return "ff_fused_col"
160- col_patterns = (
161- ".attn.to_q." ,
162- ".attn.to_k." ,
163- ".attn.to_v." ,
164- ".attn.add_q_proj." ,
165- ".attn.add_k_proj." ,
166- ".attn.add_v_proj." ,
167- )
168- row_patterns = (
169- ".attn.to_out.0." ,
170- ".attn.to_add_out." ,
171- ".ff.linear_out." ,
172- ".ff_context.linear_out." ,
173- )
174- if any (pattern in key for pattern in col_patterns ):
175- return "col"
176- if any (pattern in key for pattern in row_patterns ):
177- return "row"
178- return None
179-
180- def _split_bias_for_tp (self , key , bias , split_type , tp_size ):
181- if split_type == "col" :
182- assert bias .shape [0 ] % tp_size == 0 , f"bias dimension ({ bias .shape [0 ]} ) must be divisible by tp_size ({ tp_size } ) for { key } "
183- return list (torch .chunk (bias , tp_size , dim = 0 ))
184-
185- if split_type == "ff_fused_col" :
186- assert bias .shape [0 ] % 2 == 0 , f"invalid fused SwiGLU bias dim for { key } : { bias .shape [0 ]} "
187- ffn_dim = bias .shape [0 ] // 2
188- assert ffn_dim % tp_size == 0 , f"ffn_dim ({ ffn_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
189- gate , up = torch .split (bias , [ffn_dim , ffn_dim ], dim = 0 )
190- gate_chunks = torch .chunk (gate , tp_size , dim = 0 )
191- up_chunks = torch .chunk (up , tp_size , dim = 0 )
192- return [torch .cat ([gate_chunks [rank_idx ], up_chunks [rank_idx ]], dim = 0 ) for rank_idx in range (tp_size )]
193-
194- if split_type == "single_fused_col" :
195- inner_dim = self .config ["num_attention_heads" ] * self .config ["attention_head_dim" ]
196- ffn_dim_twice = bias .shape [0 ] - 3 * inner_dim
197- assert ffn_dim_twice > 0 and ffn_dim_twice % 2 == 0 , f"invalid fused qkv/mlp bias dim for { key } : { bias .shape [0 ]} "
198- ffn_dim = ffn_dim_twice // 2
199- assert inner_dim % tp_size == 0 , f"inner_dim ({ inner_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
200- assert ffn_dim % tp_size == 0 , f"ffn_dim ({ ffn_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
201- q , k , v , mlp_1 , mlp_2 = torch .split (bias , [inner_dim , inner_dim , inner_dim , ffn_dim , ffn_dim ], dim = 0 )
202- chunks = [torch .chunk (part , tp_size , dim = 0 ) for part in (q , k , v , mlp_1 , mlp_2 )]
203- return [torch .cat ([part_chunks [rank_idx ] for part_chunks in chunks ], dim = 0 ) for rank_idx in range (tp_size )]
204-
205- raise ValueError (f"Unsupported Flux2 TP bias split type { split_type } for { key } " )
206-
207- def _split_weight_for_tp (self , key , weight , tp_size ):
208- split_type = self ._get_split_type (key )
209- if split_type is None :
210- return [weight ] * tp_size
211-
212- if split_type == "col" :
213- assert weight .shape [0 ] % tp_size == 0 , f"out_dim ({ weight .shape [0 ]} ) must be divisible by tp_size ({ tp_size } ) for { key } "
214- return list (torch .chunk (weight , tp_size , dim = 0 ))
215-
216- if split_type == "row" :
217- assert weight .shape [1 ] % tp_size == 0 , f"in_dim ({ weight .shape [1 ]} ) must be divisible by tp_size ({ tp_size } ) for { key } "
218- return list (torch .chunk (weight , tp_size , dim = 1 ))
219-
220- if split_type == "ff_fused_col" :
221- assert weight .shape [0 ] % 2 == 0 , f"invalid fused SwiGLU out_dim for { key } : { weight .shape [0 ]} "
222- ffn_dim = weight .shape [0 ] // 2
223- assert ffn_dim % tp_size == 0 , f"ffn_dim ({ ffn_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
224- gate , up = torch .split (weight , [ffn_dim , ffn_dim ], dim = 0 )
225- gate_chunks = torch .chunk (gate , tp_size , dim = 0 )
226- up_chunks = torch .chunk (up , tp_size , dim = 0 )
227- return [torch .cat ([gate_chunks [rank_idx ], up_chunks [rank_idx ]], dim = 0 ) for rank_idx in range (tp_size )]
228-
229- inner_dim = self .config ["num_attention_heads" ] * self .config ["attention_head_dim" ]
230- if split_type == "single_fused_col" :
231- ffn_dim_twice = weight .shape [0 ] - 3 * inner_dim
232- assert ffn_dim_twice > 0 and ffn_dim_twice % 2 == 0 , f"invalid fused qkv/mlp out_dim for { key } : { weight .shape [0 ]} "
233- ffn_dim = ffn_dim_twice // 2
234- assert inner_dim % tp_size == 0 , f"inner_dim ({ inner_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
235- assert ffn_dim % tp_size == 0 , f"ffn_dim ({ ffn_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
236- q , k , v , mlp_1 , mlp_2 = torch .split (weight , [inner_dim , inner_dim , inner_dim , ffn_dim , ffn_dim ], dim = 0 )
237- return [
238- torch .cat (
239- [
240- torch .chunk (q , tp_size , dim = 0 )[rank_idx ],
241- torch .chunk (k , tp_size , dim = 0 )[rank_idx ],
242- torch .chunk (v , tp_size , dim = 0 )[rank_idx ],
243- torch .chunk (mlp_1 , tp_size , dim = 0 )[rank_idx ],
244- torch .chunk (mlp_2 , tp_size , dim = 0 )[rank_idx ],
245- ],
246- dim = 0 ,
247- )
248- for rank_idx in range (tp_size )
249- ]
250-
251- if split_type == "single_fused_row" :
252- ffn_dim = weight .shape [1 ] - inner_dim
253- assert ffn_dim > 0 , f"invalid fused output in_dim for { key } : { weight .shape [1 ]} "
254- assert inner_dim % tp_size == 0 , f"inner_dim ({ inner_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
255- assert ffn_dim % tp_size == 0 , f"ffn_dim ({ ffn_dim } ) must be divisible by tp_size ({ tp_size } ) for { key } "
256- attn , mlp = torch .split (weight , [inner_dim , ffn_dim ], dim = 1 )
257- return [
258- torch .cat (
259- [
260- torch .chunk (attn , tp_size , dim = 1 )[rank_idx ],
261- torch .chunk (mlp , tp_size , dim = 1 )[rank_idx ],
262- ],
263- dim = 1 ,
264- )
265- for rank_idx in range (tp_size )
266- ]
267-
268- raise ValueError (f"Unsupported Flux2 TP split type { split_type } for { key } " )
269-
27031 def _init_infer (self ):
27132 self .transformer_infer = self .transformer_infer_class (self .config )
27233 self .pre_infer = self .pre_infer_class (self .config )
0 commit comments