Jingya HF Staff commited on
Commit
d38fb47
·
verified ·
1 Parent(s): 95be5bc

Upload src/pipeline.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. src/pipeline.py +234 -39
src/pipeline.py CHANGED
@@ -103,6 +103,66 @@ def _build_fused_qkv_weight(q_w, k_w, v_w) -> torch.Tensor:
103
  # TP-aware attention processors
104
  # ---------------------------------------------------------------------------
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  class Flux2AttnProcessorTP(Flux2AttnProcessor):
107
  def __init__(self, tp_size: int):
108
  super().__init__()
@@ -167,10 +227,9 @@ class Flux2AttnProcessorFusedQKV(Flux2AttnProcessor):
167
  that torch.compile(fullgraph=True) would not trace through.
168
  """
169
 
170
- def __init__(self, tp_size: int, compile_mode: bool = False):
171
  super().__init__()
172
  self.tp_size = tp_size
173
- self.compile_mode = compile_mode
174
  self._nki_qkv = None
175
 
176
  def _init_nki(self):
@@ -180,19 +239,8 @@ class Flux2AttnProcessorFusedQKV(Flux2AttnProcessor):
180
  _dir = os.path.dirname(os.path.abspath(__file__))
181
  if _dir not in sys.path:
182
  sys.path.insert(0, _dir)
183
- from nki_qkv import fused_qkv_op
184
-
185
- if self.compile_mode:
186
- # Outer torch.compile(transformer, ...) traces through fused_qkv_op
187
- # via FunctionalTensorMode → nki_kernel_wrapper HOP is captured as
188
- # an XLA custom call within the full transformer NEFF.
189
- self._nki_qkv = fused_qkv_op
190
- else:
191
- # Eager / lazy-XLA: compile just the QKV function as a standalone NEFF.
192
- # dynamic=False: specialize on concrete shapes so dump_config sees plain ints.
193
- def _compiled_qkv(x_T, fused_w):
194
- return fused_qkv_op(x_T, fused_w)
195
- self._nki_qkv = torch.compile(_compiled_qkv, backend="neuron", dynamic=False)
196
 
197
  def _fused_proj(self, attn, q_attr, k_attr, v_attr, cache_attr, x):
198
  """Build (once) and apply the fused [H, 3*(H//tp)] QKV weight."""
@@ -268,6 +316,95 @@ class Flux2AttnProcessorFusedQKV(Flux2AttnProcessor):
268
  return hidden_states
269
 
270
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  class Flux2ParallelSelfAttnProcessorTP(Flux2ParallelSelfAttnProcessor):
272
  def __init__(self, tp_size: int):
273
  super().__init__()
@@ -303,6 +440,53 @@ class Flux2ParallelSelfAttnProcessorTP(Flux2ParallelSelfAttnProcessor):
303
  return attn.to_out(hidden_states)
304
 
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  # ---------------------------------------------------------------------------
307
  # Tensor parallelism
308
  # ---------------------------------------------------------------------------
@@ -372,7 +556,7 @@ def apply_tp_flux2_transformer(
372
  model: Flux2Transformer2DModel,
373
  tp_mesh: DeviceMesh,
374
  fuse_qkv: bool = False,
375
- compile_mode: bool = False,
376
  ) -> Flux2Transformer2DModel:
377
  tp_size = tp_mesh.size()
378
  double_plan = {
@@ -395,8 +579,36 @@ def apply_tp_flux2_transformer(
395
  block.ff_context.linear_in.weight.data = _permute_swiglu_for_tp(
396
  block.ff_context.linear_in.weight.data, tp_size)
397
  parallelize_module(block, tp_mesh, double_plan)
398
- if fuse_qkv:
399
- block.attn.set_processor(Flux2AttnProcessorFusedQKV(tp_size, compile_mode=compile_mode))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
  else:
401
  block.attn.set_processor(Flux2AttnProcessorTP(tp_size))
402
 
@@ -413,30 +625,13 @@ def apply_tp_flux2_transformer(
413
  attn.to_out.weight.data = _permute_out_for_tp(
414
  attn.to_out.weight.data, tp_size, inner_dim, mlp_hidden)
415
  parallelize_module(block, tp_mesh, single_plan)
416
- block.attn.set_processor(Flux2ParallelSelfAttnProcessorTP(tp_size))
 
 
 
417
  return model
418
 
419
 
420
- def prebuild_fused_qkv_weights(model: Flux2Transformer2DModel) -> None:
421
- """Pre-build and cache fused QKV weights on all Flux2AttnProcessorFusedQKV processors.
422
-
423
- Must be called AFTER model.to(device) and BEFORE torch.compile so that
424
- _fused_proj finds the pre-built plain tensor (not a DTensor) during tracing.
425
- """
426
- for block in model.transformer_blocks:
427
- proc = block.attn.processor
428
- if not isinstance(proc, Flux2AttnProcessorFusedQKV):
429
- continue
430
- attn = block.attn
431
- dev = next(attn.to_q.parameters()).device
432
- if not hasattr(attn, "_fused_img_qkv_w") or attn._fused_img_qkv_w is None:
433
- attn._fused_img_qkv_w = _build_fused_qkv_weight(
434
- attn.to_q.weight, attn.to_k.weight, attn.to_v.weight).to(dev)
435
- if not hasattr(attn, "_fused_txt_qkv_w") or attn._fused_txt_qkv_w is None:
436
- attn._fused_txt_qkv_w = _build_fused_qkv_weight(
437
- attn.add_q_proj.weight, attn.add_k_proj.weight, attn.add_v_proj.weight).to(dev)
438
-
439
-
440
  def apply_tp_text_encoder(model: Qwen3ForCausalLM, tp_mesh: DeviceMesh) -> Qwen3ForCausalLM:
441
  """Llama-style TP plan for Qwen3ForCausalLM; no weight permutations needed."""
442
  layer_plan = {
 
103
  # TP-aware attention processors
104
  # ---------------------------------------------------------------------------
105
 
106
+ class Flux2AttnProcessorFlashAttn(Flux2AttnProcessor):
107
+ """
108
+ TP-aware double-stream attention processor using a NKI flash attention kernel.
109
+ Replaces dispatch_attention_fn with flux2_flash_attn (BSHD in, BSHD out).
110
+ """
111
+ def __init__(self, tp_size: int):
112
+ super().__init__()
113
+ self.tp_size = tp_size
114
+ self._flash_attn = None
115
+
116
+ def _init_nki(self):
117
+ if self._flash_attn is None:
118
+ import sys, os
119
+ _dir = os.path.dirname(os.path.abspath(__file__))
120
+ if _dir not in sys.path:
121
+ sys.path.insert(0, _dir)
122
+ from nki_flash_attn import flux2_flash_attn
123
+ self._flash_attn = flux2_flash_attn
124
+
125
+ def __call__(self, attn, hidden_states, encoder_hidden_states=None,
126
+ attention_mask=None, image_rotary_emb=None, **kwargs):
127
+ self._init_nki()
128
+ local_heads = attn.heads // self.tp_size
129
+ head_dim = attn.head_dim
130
+
131
+ query, key, value, encoder_query, encoder_key, encoder_value = \
132
+ _get_qkv_projections(attn, hidden_states, encoder_hidden_states)
133
+ query = query.unflatten(-1, (local_heads, head_dim))
134
+ key = key.unflatten(-1, (local_heads, head_dim))
135
+ value = value.unflatten(-1, (local_heads, head_dim))
136
+ query = attn.norm_q(query)
137
+ key = attn.norm_k(key)
138
+ if attn.added_kv_proj_dim is not None:
139
+ encoder_query = encoder_query.unflatten(-1, (local_heads, head_dim))
140
+ encoder_key = encoder_key.unflatten(-1, (local_heads, head_dim))
141
+ encoder_value = encoder_value.unflatten(-1, (local_heads, head_dim))
142
+ encoder_query = attn.norm_added_q(encoder_query)
143
+ encoder_key = attn.norm_added_k(encoder_key)
144
+ query = torch.cat([encoder_query, query], dim=1)
145
+ key = torch.cat([encoder_key, key], dim=1)
146
+ value = torch.cat([encoder_value, value], dim=1)
147
+ if image_rotary_emb is not None:
148
+ query = apply_rotary_emb(query, image_rotary_emb, sequence_dim=1)
149
+ key = apply_rotary_emb(key, image_rotary_emb, sequence_dim=1)
150
+
151
+ hidden_states = self._flash_attn(query, key, value) # (B, S, H_local, D) BSHD
152
+ hidden_states = hidden_states.flatten(2, 3).to(query.dtype)
153
+
154
+ if encoder_hidden_states is not None:
155
+ encoder_hidden_states, hidden_states = hidden_states.split_with_sizes(
156
+ [encoder_hidden_states.shape[1],
157
+ hidden_states.shape[1] - encoder_hidden_states.shape[1]], dim=1)
158
+ encoder_hidden_states = attn.to_add_out(encoder_hidden_states)
159
+ hidden_states = attn.to_out[0](hidden_states)
160
+ hidden_states = attn.to_out[1](hidden_states)
161
+ if encoder_hidden_states is not None:
162
+ return hidden_states, encoder_hidden_states
163
+ return hidden_states
164
+
165
+
166
  class Flux2AttnProcessorTP(Flux2AttnProcessor):
167
  def __init__(self, tp_size: int):
168
  super().__init__()
 
227
  that torch.compile(fullgraph=True) would not trace through.
228
  """
229
 
230
+ def __init__(self, tp_size: int):
231
  super().__init__()
232
  self.tp_size = tp_size
 
233
  self._nki_qkv = None
234
 
235
  def _init_nki(self):
 
239
  _dir = os.path.dirname(os.path.abspath(__file__))
240
  if _dir not in sys.path:
241
  sys.path.insert(0, _dir)
242
+ from nki_qkv import fused_qkv_kernel
243
+ self._nki_qkv = fused_qkv_kernel
 
 
 
 
 
 
 
 
 
 
 
244
 
245
  def _fused_proj(self, attn, q_attr, k_attr, v_attr, cache_attr, x):
246
  """Build (once) and apply the fused [H, 3*(H//tp)] QKV weight."""
 
316
  return hidden_states
317
 
318
 
319
+ class Flux2AttnProcessorFusedQKVFlashAttn(Flux2AttnProcessor):
320
+ """
321
+ TP-aware double-stream processor: NKI fused QKV projection + NKI flash attention.
322
+ Combines Flux2AttnProcessorFusedQKV (QKV side) with Flux2AttnProcessorFlashAttn
323
+ (attention side). Activated by --fused-qkv --flash-attn together.
324
+ """
325
+ def __init__(self, tp_size: int):
326
+ super().__init__()
327
+ self.tp_size = tp_size
328
+ self._nki_qkv = None
329
+ self._flash_attn = None
330
+
331
+ def _init_nki(self):
332
+ if self._nki_qkv is None:
333
+ import sys
334
+ _dir = os.path.dirname(os.path.abspath(__file__))
335
+ if _dir not in sys.path:
336
+ sys.path.insert(0, _dir)
337
+ from nki_qkv import fused_qkv_kernel
338
+ self._nki_qkv = fused_qkv_kernel
339
+ if self._flash_attn is None:
340
+ import sys
341
+ _dir = os.path.dirname(os.path.abspath(__file__))
342
+ if _dir not in sys.path:
343
+ sys.path.insert(0, _dir)
344
+ from nki_flash_attn import flux2_flash_attn
345
+ self._flash_attn = flux2_flash_attn
346
+
347
+ def _fused_proj(self, attn, q_attr, k_attr, v_attr, cache_attr, x):
348
+ fused_w = getattr(attn, cache_attr, None)
349
+ if fused_w is None:
350
+ fused_w = _build_fused_qkv_weight(
351
+ getattr(attn, q_attr).weight,
352
+ getattr(attn, k_attr).weight,
353
+ getattr(attn, v_attr).weight,
354
+ ).to(x.device)
355
+ setattr(attn, cache_attr, fused_w)
356
+ local_dim = attn.inner_dim // self.tp_size
357
+ B, S, H = x.shape
358
+ x_T = x.reshape(B * S, H).T.contiguous()
359
+ out = self._nki_qkv(x_T, fused_w)
360
+ return out.reshape(B, S, -1).split(local_dim, dim=-1)
361
+
362
+ def __call__(self, attn, hidden_states, encoder_hidden_states=None,
363
+ attention_mask=None, image_rotary_emb=None, **kwargs):
364
+ self._init_nki()
365
+ local_heads = attn.heads // self.tp_size
366
+ head_dim = attn.head_dim
367
+
368
+ query, key, value = self._fused_proj(
369
+ attn, "to_q", "to_k", "to_v", "_fused_img_qkv_w", hidden_states)
370
+ query = query.unflatten(-1, (local_heads, head_dim))
371
+ key = key.unflatten(-1, (local_heads, head_dim))
372
+ value = value.unflatten(-1, (local_heads, head_dim))
373
+ query = attn.norm_q(query)
374
+ key = attn.norm_k(key)
375
+
376
+ if attn.added_kv_proj_dim is not None:
377
+ encoder_query, encoder_key, encoder_value = self._fused_proj(
378
+ attn, "add_q_proj", "add_k_proj", "add_v_proj",
379
+ "_fused_txt_qkv_w", encoder_hidden_states)
380
+ encoder_query = encoder_query.unflatten(-1, (local_heads, head_dim))
381
+ encoder_key = encoder_key.unflatten(-1, (local_heads, head_dim))
382
+ encoder_value = encoder_value.unflatten(-1, (local_heads, head_dim))
383
+ encoder_query = attn.norm_added_q(encoder_query)
384
+ encoder_key = attn.norm_added_k(encoder_key)
385
+ query = torch.cat([encoder_query, query], dim=1)
386
+ key = torch.cat([encoder_key, key], dim=1)
387
+ value = torch.cat([encoder_value, value], dim=1)
388
+
389
+ if image_rotary_emb is not None:
390
+ query = apply_rotary_emb(query, image_rotary_emb, sequence_dim=1)
391
+ key = apply_rotary_emb(key, image_rotary_emb, sequence_dim=1)
392
+
393
+ hidden_states = self._flash_attn(query, key, value)
394
+ hidden_states = hidden_states.flatten(2, 3).to(query.dtype)
395
+
396
+ if encoder_hidden_states is not None:
397
+ encoder_hidden_states, hidden_states = hidden_states.split_with_sizes(
398
+ [encoder_hidden_states.shape[1],
399
+ hidden_states.shape[1] - encoder_hidden_states.shape[1]], dim=1)
400
+ encoder_hidden_states = attn.to_add_out(encoder_hidden_states)
401
+ hidden_states = attn.to_out[0](hidden_states)
402
+ hidden_states = attn.to_out[1](hidden_states)
403
+ if encoder_hidden_states is not None:
404
+ return hidden_states, encoder_hidden_states
405
+ return hidden_states
406
+
407
+
408
  class Flux2ParallelSelfAttnProcessorTP(Flux2ParallelSelfAttnProcessor):
409
  def __init__(self, tp_size: int):
410
  super().__init__()
 
440
  return attn.to_out(hidden_states)
441
 
442
 
443
+ class Flux2ParallelSelfAttnProcessorFlashAttn(Flux2ParallelSelfAttnProcessor):
444
+ """
445
+ TP-aware single-stream attention processor using a NKI flash attention kernel.
446
+ """
447
+ def __init__(self, tp_size: int):
448
+ super().__init__()
449
+ self.tp_size = tp_size
450
+ self._flash_attn = None
451
+
452
+ def _init_nki(self):
453
+ if self._flash_attn is None:
454
+ import sys, os
455
+ _dir = os.path.dirname(os.path.abspath(__file__))
456
+ if _dir not in sys.path:
457
+ sys.path.insert(0, _dir)
458
+ from nki_flash_attn import flux2_flash_attn
459
+ self._flash_attn = flux2_flash_attn
460
+
461
+ def __call__(self, attn, hidden_states, attention_mask=None,
462
+ image_rotary_emb=None, **kwargs):
463
+ self._init_nki()
464
+ local_heads = attn.heads // self.tp_size
465
+ head_dim = attn.head_dim
466
+ local_inner = attn.inner_dim // self.tp_size
467
+ local_mlp_gate = attn.mlp_hidden_dim * attn.mlp_mult_factor // self.tp_size
468
+
469
+ hidden_states = attn.to_qkv_mlp_proj(hidden_states)
470
+ qkv, mlp_hidden_states = torch.split(
471
+ hidden_states, [3 * local_inner, local_mlp_gate], dim=-1)
472
+ query, key, value = qkv.chunk(3, dim=-1)
473
+ query = query.unflatten(-1, (local_heads, head_dim))
474
+ key = key.unflatten(-1, (local_heads, head_dim))
475
+ value = value.unflatten(-1, (local_heads, head_dim))
476
+ query = attn.norm_q(query)
477
+ key = attn.norm_k(key)
478
+ if image_rotary_emb is not None:
479
+ query = apply_rotary_emb(query, image_rotary_emb, sequence_dim=1)
480
+ key = apply_rotary_emb(key, image_rotary_emb, sequence_dim=1)
481
+
482
+ hidden_states = self._flash_attn(query, key, value) # (B, S, H_local, D) BSHD
483
+ hidden_states = hidden_states.flatten(2, 3).to(query.dtype)
484
+
485
+ mlp_hidden_states = attn.mlp_act_fn(mlp_hidden_states)
486
+ hidden_states = torch.cat([hidden_states, mlp_hidden_states], dim=-1)
487
+ return attn.to_out(hidden_states)
488
+
489
+
490
  # ---------------------------------------------------------------------------
491
  # Tensor parallelism
492
  # ---------------------------------------------------------------------------
 
556
  model: Flux2Transformer2DModel,
557
  tp_mesh: DeviceMesh,
558
  fuse_qkv: bool = False,
559
+ flash_attn: bool = False,
560
  ) -> Flux2Transformer2DModel:
561
  tp_size = tp_mesh.size()
562
  double_plan = {
 
579
  block.ff_context.linear_in.weight.data = _permute_swiglu_for_tp(
580
  block.ff_context.linear_in.weight.data, tp_size)
581
  parallelize_module(block, tp_mesh, double_plan)
582
+ if fuse_qkv and flash_attn:
583
+ block.attn.set_processor(Flux2AttnProcessorFusedQKVFlashAttn(tp_size))
584
+ block.attn._fused_img_qkv_w = _build_fused_qkv_weight(
585
+ block.attn.to_q.weight,
586
+ block.attn.to_k.weight,
587
+ block.attn.to_v.weight,
588
+ )
589
+ block.attn._fused_txt_qkv_w = _build_fused_qkv_weight(
590
+ block.attn.add_q_proj.weight,
591
+ block.attn.add_k_proj.weight,
592
+ block.attn.add_v_proj.weight,
593
+ )
594
+ elif flash_attn:
595
+ block.attn.set_processor(Flux2AttnProcessorFlashAttn(tp_size))
596
+ elif fuse_qkv:
597
+ block.attn.set_processor(Flux2AttnProcessorFusedQKV(tp_size))
598
+ # Pre-build fused weights now (real DTensors present) so they are
599
+ # plain local tensors by the time torch.compile traces the forward.
600
+ # Lazy build inside _fused_proj fails under Dynamo fake-tensor mode
601
+ # because _to_local() on a fake DTensor returns the full DTensor.
602
+ block.attn._fused_img_qkv_w = _build_fused_qkv_weight(
603
+ block.attn.to_q.weight,
604
+ block.attn.to_k.weight,
605
+ block.attn.to_v.weight,
606
+ )
607
+ block.attn._fused_txt_qkv_w = _build_fused_qkv_weight(
608
+ block.attn.add_q_proj.weight,
609
+ block.attn.add_k_proj.weight,
610
+ block.attn.add_v_proj.weight,
611
+ )
612
  else:
613
  block.attn.set_processor(Flux2AttnProcessorTP(tp_size))
614
 
 
625
  attn.to_out.weight.data = _permute_out_for_tp(
626
  attn.to_out.weight.data, tp_size, inner_dim, mlp_hidden)
627
  parallelize_module(block, tp_mesh, single_plan)
628
+ if flash_attn:
629
+ block.attn.set_processor(Flux2ParallelSelfAttnProcessorFlashAttn(tp_size))
630
+ else:
631
+ block.attn.set_processor(Flux2ParallelSelfAttnProcessorTP(tp_size))
632
  return model
633
 
634
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
635
  def apply_tp_text_encoder(model: Qwen3ForCausalLM, tp_mesh: DeviceMesh) -> Qwen3ForCausalLM:
636
  """Llama-style TP plan for Qwen3ForCausalLM; no weight permutations needed."""
637
  layer_plan = {