Add defensive checks for torch.mps.is_available() to handle PyTorch versions where the method doesn't exist on non-Mac platforms. Resolves AttributeError: module 'torch.mps' has no attribute 'is_available'
608 lines
26 KiB
Python
608 lines
26 KiB
Python
"""
|
|
Compatibility module for SeedVR2
|
|
Contains FP8/FP16 compatibility layers and wrappers for different model architectures
|
|
|
|
Extracted from: seedvr2.py (lines 1045-1630)
|
|
"""
|
|
|
|
import torch
|
|
import types
|
|
import os
|
|
|
|
# Flash Attention & Triton Compatibility Layer
|
|
# 1. Flash Attention - speedup for attention operations
|
|
try:
|
|
from flash_attn import flash_attn_varlen_func
|
|
FLASH_ATTN_AVAILABLE = True
|
|
except ImportError:
|
|
flash_attn_varlen_func = None
|
|
FLASH_ATTN_AVAILABLE = False
|
|
|
|
def validate_flash_attention_availability(requested_mode: str, debug=None) -> str:
|
|
"""
|
|
Validate Flash Attention availability and warn if fallback needed.
|
|
|
|
Args:
|
|
requested_mode: Either 'flash_attn' or 'sdpa'
|
|
debug: Optional debug instance for logging
|
|
|
|
Returns:
|
|
Validated mode ('flash_attn' or 'sdpa')
|
|
"""
|
|
if requested_mode == 'flash_attn' and not FLASH_ATTN_AVAILABLE:
|
|
error_msg = (
|
|
f"Cannot use 'flash_attn' attention mode: Flash Attention is not installed.\n"
|
|
f"\n"
|
|
f"Flash Attention provides speedup on some hardware through optimized CUDA kernels.\n"
|
|
f"Falling back to PyTorch SDPA (scaled dot-product attention).\n"
|
|
f"\n"
|
|
f"To fix this issue:\n"
|
|
f" 1. Install Flash Attention: pip install flash-attn\n"
|
|
f" 2. OR change attention_mode to 'sdpa' (default, always available)\n"
|
|
f"\n"
|
|
f"For more info: https://github.com/Dao-AILab/flash-attention"
|
|
)
|
|
if debug:
|
|
debug.log(error_msg, level="WARNING", category="setup", force=True)
|
|
|
|
return 'sdpa'
|
|
|
|
return requested_mode
|
|
|
|
|
|
# 2. Triton - Required for torch.compile with inductor backend
|
|
try:
|
|
import triton
|
|
TRITON_AVAILABLE = True
|
|
except ImportError:
|
|
TRITON_AVAILABLE = False
|
|
|
|
|
|
# 3. GGUF - Required for quantized model loading
|
|
try:
|
|
import gguf
|
|
from gguf import GGMLQuantizationType
|
|
GGUF_AVAILABLE = True
|
|
except ImportError:
|
|
GGUF_AVAILABLE = False
|
|
gguf = None
|
|
GGMLQuantizationType = None
|
|
|
|
|
|
def validate_gguf_availability(operation: str = "load GGUF model", debug=None) -> None:
|
|
"""
|
|
Validate GGUF availability and raise error if not installed.
|
|
|
|
Args:
|
|
operation: Description of the operation requiring GGUF
|
|
debug: Optional debug instance for logging
|
|
|
|
Raises:
|
|
RuntimeError: If GGUF is not available
|
|
"""
|
|
if not GGUF_AVAILABLE:
|
|
error_msg = (
|
|
f"Cannot {operation}: GGUF library is not installed.\n"
|
|
f"\n"
|
|
f"GGUF provides quantized model support for memory-efficient loading.\n"
|
|
f"\n"
|
|
f"To fix this issue:\n"
|
|
f" 1. Install GGUF: pip install gguf\n"
|
|
f" 2. OR use a non-quantized model format (.safetensors)\n"
|
|
f"\n"
|
|
f"For more info: https://github.com/ggerganov/ggml"
|
|
)
|
|
if debug:
|
|
debug.log(error_msg, level="ERROR", category="setup", force=True)
|
|
raise RuntimeError(f"GGUF library required to {operation}")
|
|
|
|
|
|
# 4. NVIDIA Conv3d Memory Bug - Workaround for PyTorch 2.9-2.10 + cuDNN >= 91002
|
|
def _check_conv3d_memory_bug():
|
|
"""
|
|
Check if Conv3d memory bug workaround needed.
|
|
Bug: PyTorch 2.9-2.10 with cuDNN >= 91002 uses 3x memory for Conv3d
|
|
with fp16/bfloat16 due to buggy dispatch layer.
|
|
"""
|
|
try:
|
|
if not (torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 3):
|
|
return False
|
|
|
|
# Parse torch version
|
|
version_str = torch.__version__.split('+')[0]
|
|
parts = version_str.split('.')
|
|
torch_version = tuple(int(p) for p in parts[:2])
|
|
|
|
if not ((2, 9) <= torch_version <= (2, 10)):
|
|
return False
|
|
|
|
if not hasattr(torch.backends.cudnn, 'version'):
|
|
return False
|
|
|
|
cudnn_version = torch.backends.cudnn.version()
|
|
if cudnn_version is None or cudnn_version < 91002:
|
|
return False
|
|
|
|
return True
|
|
except:
|
|
return False
|
|
|
|
NVIDIA_CONV3D_MEMORY_BUG_WORKAROUND = _check_conv3d_memory_bug()
|
|
|
|
|
|
# Log all optimization status once globally (cross-process) using environment variable
|
|
if not os.environ.get("SEEDVR2_OPTIMIZATIONS_LOGGED"):
|
|
os.environ["SEEDVR2_OPTIMIZATIONS_LOGGED"] = "1"
|
|
|
|
# Flash Attention & Triton status
|
|
has_both = FLASH_ATTN_AVAILABLE and TRITON_AVAILABLE
|
|
has_neither = not FLASH_ATTN_AVAILABLE and not TRITON_AVAILABLE
|
|
|
|
if has_both:
|
|
print("⚡ SeedVR2 optimizations check: Flash Attention ✅ | Triton ✅")
|
|
elif has_neither:
|
|
print("⚠️ SeedVR2 optimizations check: Flash Attention ❌ | Triton ❌")
|
|
print("💡 For best performance: pip install flash-attn triton")
|
|
elif FLASH_ATTN_AVAILABLE:
|
|
print("⚡ SeedVR2 optimizations check: Flash Attention ✅ | Triton ❌")
|
|
print("💡 Install Triton for torch.compile: pip install triton")
|
|
else: # TRITON_AVAILABLE only
|
|
print("⚠️ SeedVR2 optimizations check: Flash Attention ❌ | Triton ✅")
|
|
print("💡 Install Flash Attention for faster inference: pip install flash-attn")
|
|
|
|
# Conv3d workaround status (if applicable)
|
|
if NVIDIA_CONV3D_MEMORY_BUG_WORKAROUND:
|
|
torch_ver = torch.__version__.split('+')[0]
|
|
cudnn_ver = torch.backends.cudnn.version()
|
|
print(f"🔧 Conv3d workaround active: PyTorch {torch_ver}, cuDNN {cudnn_ver} (fixing VAE 3x memory bug)")
|
|
|
|
|
|
def call_rope_with_stability(method, *args, **kwargs):
|
|
"""
|
|
Call RoPE method with stability fixes:
|
|
1. Clear cache if available
|
|
2. Disable autocast to prevent numerical issues
|
|
This prevents artifacts in FP8/mixed precision models.
|
|
"""
|
|
if hasattr(method, 'cache_clear'):
|
|
method.cache_clear()
|
|
|
|
with torch.cuda.amp.autocast(enabled=False):
|
|
return method(*args, **kwargs)
|
|
|
|
|
|
class FP8CompatibleDiT(torch.nn.Module):
|
|
"""
|
|
Wrapper for DiT models with automatic compatibility management + advanced optimizations
|
|
|
|
Precision Handling:
|
|
- FP8: Keeps native FP8 parameters (memory efficient), converts inputs/outputs to compute_dtype for arithmetic
|
|
- FP16: Uses native FP16 precision throughout
|
|
- BFloat16: Uses native BFloat16 precision throughout
|
|
- Float32: Uses full precision for maximum quality
|
|
- RoPE: Converted from FP8 to compute_dtype for numerical consistency
|
|
|
|
Optimizations:
|
|
- Flash Attention: Automatic optimization of attention layers
|
|
- RoPE Stabilization: Error handling for numerical stability in mixed precision
|
|
- MPS Compatibility: Unified dtype conversion for Apple Silicon backends
|
|
"""
|
|
|
|
def __init__(self, dit_model, debug: 'Debug', compute_dtype: torch.dtype = torch.bfloat16, skip_conversion: bool = False):
|
|
super().__init__()
|
|
self.dit_model = dit_model
|
|
self.debug = debug
|
|
self.compute_dtype = compute_dtype
|
|
self.model_dtype = self._detect_model_dtype()
|
|
self.is_fp8_model = self.model_dtype in (torch.float8_e4m3fn, torch.float8_e5m2)
|
|
self.is_fp16_model = self.model_dtype == torch.float16
|
|
|
|
# Only convert if not already done (e.g., when reusing cached weights)
|
|
if not skip_conversion and self.is_fp8_model:
|
|
# FP8 models need RoPE frequency conversion to compute dtype
|
|
model_variant = self._get_model_variant()
|
|
self.debug.log(f"Detected NaDiT {model_variant} FP8 - Converting RoPE freqs for FP8 compatibility",
|
|
category="precision")
|
|
self.debug.start_timer("_convert_rope_freqs")
|
|
self._convert_rope_freqs(target_dtype=self.compute_dtype)
|
|
self.debug.end_timer("_convert_rope_freqs", "RoPE freqs conversion")
|
|
|
|
if hasattr(torch, 'mps') and callable(getattr(torch.mps, 'is_available', None)) and torch.mps.is_available():
|
|
self.debug.log(f"Also converting NaDiT parameters/buffers for MPS backend", category="setup", force=True)
|
|
self.debug.start_timer("_force_nadit_precision")
|
|
self._force_nadit_precision(target_dtype=self.compute_dtype)
|
|
self.debug.end_timer("_force_nadit_precision", "NaDiT parameters/buffers conversion")
|
|
|
|
# Apply RoPE stabilization for numerical stability
|
|
self.debug.log(f"Stabilizing RoPE computations for numerical stability", category="setup")
|
|
self.debug.start_timer("_stabilize_rope_computations")
|
|
self._stabilize_rope_computations()
|
|
self.debug.end_timer("_stabilize_rope_computations", "RoPE stabilization")
|
|
|
|
# 🚀 FLASH ATTENTION OPTIMIZATION (Phase 2)
|
|
self.debug.start_timer("_apply_flash_attention_optimization")
|
|
self._apply_flash_attention_optimization()
|
|
self.debug.end_timer("_apply_flash_attention_optimization", "Flash Attention application")
|
|
|
|
def _detect_model_dtype(self) -> torch.dtype:
|
|
"""Detect main model dtype"""
|
|
try:
|
|
return next(self.dit_model.parameters()).dtype
|
|
except:
|
|
return torch.bfloat16
|
|
|
|
def _get_model_variant(self) -> str:
|
|
"""Detect model variant from module path"""
|
|
model_module = str(self.dit_model.__class__.__module__).lower()
|
|
if 'dit_7b' in model_module:
|
|
return "7B"
|
|
elif 'dit_3b' in model_module:
|
|
return "3B"
|
|
else:
|
|
return "Unknown"
|
|
|
|
def _convert_rope_freqs(self, target_dtype: torch.dtype = torch.bfloat16) -> None:
|
|
"""
|
|
Convert RoPE frequency buffers from FP8 to target dtype for compatibility.
|
|
|
|
Args:
|
|
target_dtype: Target dtype for RoPE freqs (default: bfloat16 for stability)
|
|
"""
|
|
converted = 0
|
|
for module in self.dit_model.modules():
|
|
if 'RotaryEmbedding' in type(module).__name__:
|
|
if hasattr(module, 'rope') and hasattr(module.rope, 'freqs'):
|
|
if module.rope.freqs.dtype in (torch.float8_e4m3fn, torch.float8_e5m2):
|
|
if module.rope.freqs.device.type == "mps":
|
|
module.rope.freqs.data = module.rope.freqs.to("cpu").to(target_dtype).to("mps")
|
|
else:
|
|
module.rope.freqs.data = module.rope.freqs.to(target_dtype)
|
|
converted += 1
|
|
self.debug.log(f"Converted {converted} RoPE frequency buffers from FP8 to {target_dtype} for compatibility", category="success")
|
|
|
|
def _force_nadit_precision(self, target_dtype: torch.dtype = torch.bfloat16) -> None:
|
|
"""
|
|
Force ALL NaDiT parameters to target dtype to avoid promotion errors (MPS requirement).
|
|
|
|
Args:
|
|
target_dtype: Target dtype for all parameters (default: bfloat16 for MPS compatibility)
|
|
"""
|
|
converted_count = 0
|
|
original_dtype = None
|
|
|
|
# Convert ALL parameters to target dtype
|
|
for name, param in self.dit_model.named_parameters():
|
|
if original_dtype is None:
|
|
original_dtype = param.dtype
|
|
if param.dtype != target_dtype:
|
|
if param.device.type == "mps":
|
|
temp_cpu = param.data.to("cpu")
|
|
temp_converted = temp_cpu.to(target_dtype)
|
|
param.data = temp_converted.to("mps")
|
|
del temp_cpu, temp_converted
|
|
else:
|
|
param.data = param.data.to(target_dtype)
|
|
converted_count += 1
|
|
|
|
# Also convert buffers
|
|
for name, buffer in self.dit_model.named_buffers():
|
|
if buffer.dtype != target_dtype:
|
|
if buffer.device.type == "mps":
|
|
temp_cpu = buffer.data.to("cpu")
|
|
temp_converted = temp_cpu.to(target_dtype)
|
|
buffer.data = temp_converted.to("mps")
|
|
del temp_cpu, temp_converted
|
|
else:
|
|
buffer.data = buffer.data.to(target_dtype)
|
|
converted_count += 1
|
|
|
|
self.debug.log(f"Converted {converted_count} NaDiT parameters/buffers to {target_dtype} for MPS", category="success")
|
|
|
|
# Update detected dtype
|
|
self.model_dtype = target_dtype
|
|
self.is_fp8_model = (target_dtype in (torch.float8_e4m3fn, torch.float8_e5m2))
|
|
|
|
def _stabilize_rope_computations(self):
|
|
"""
|
|
Add error handling to RoPE computations to prevent artifacts.
|
|
|
|
Wraps the get_axial_freqs method of RoPE modules with a try-except handler.
|
|
During normal operation, uses the original cached method for performance.
|
|
Only on exceptions (e.g., numerical instability, NaN propagation) does it
|
|
intervene by clearing the cache and retrying the computation through
|
|
call_rope_with_stability.
|
|
|
|
This prevents artifacts in FP8, mixed precision, and edge cases while
|
|
maintaining optimal performance for normal operations.
|
|
"""
|
|
if not hasattr(self.dit_model, 'blocks'):
|
|
return
|
|
|
|
rope_count = 0
|
|
|
|
# Wrap RoPE modules to handle numerical instability
|
|
for name, module in self.dit_model.named_modules():
|
|
if "rope" in name.lower() and hasattr(module, "get_axial_freqs"):
|
|
# Check if already wrapped
|
|
if hasattr(module, '_rope_wrapped'):
|
|
continue
|
|
|
|
original_method = module.get_axial_freqs
|
|
|
|
# Mark as wrapped and store original
|
|
module._rope_wrapped = 'stability'
|
|
module._original_get_axial_freqs = original_method
|
|
|
|
# Error handler that prevents NaN propagation
|
|
def stable_rope_computation(self, *args, **kwargs):
|
|
try:
|
|
return original_method(*args, **kwargs)
|
|
except Exception:
|
|
return call_rope_with_stability(original_method, *args, **kwargs)
|
|
|
|
module.get_axial_freqs = types.MethodType(stable_rope_computation, module)
|
|
rope_count += 1
|
|
|
|
if rope_count > 0:
|
|
self.debug.log(f"Stabilized {rope_count} RoPE modules", category="success")
|
|
|
|
def _apply_flash_attention_optimization(self) -> None:
|
|
"""🚀 FLASH ATTENTION OPTIMIZATION - 30-50% speedup of attention layers"""
|
|
attention_layers_optimized = 0
|
|
flash_attention_available = self._check_flash_attention_support()
|
|
|
|
for name, module in self.dit_model.named_modules():
|
|
# Identify all attention layers
|
|
if self._is_attention_layer(name, module):
|
|
# Apply optimization based on availability
|
|
if self._optimize_attention_layer(name, module, flash_attention_available):
|
|
attention_layers_optimized += 1
|
|
|
|
if not flash_attention_available:
|
|
self.debug.log("Flash Attention not available, using PyTorch SDPA as fallback", category="info", force=True)
|
|
|
|
def _check_flash_attention_support(self) -> bool:
|
|
"""Check if Flash Attention is available"""
|
|
# Check PyTorch SDPA (includes Flash Attention on H100/A100)
|
|
if hasattr(torch.nn.functional, 'scaled_dot_product_attention'):
|
|
return True
|
|
|
|
# Check flash-attn package (uses module-level check from top of file)
|
|
return FLASH_ATTN_AVAILABLE
|
|
|
|
def _is_attention_layer(self, name: str, module: torch.nn.Module) -> bool:
|
|
"""Identify if a module is an attention layer"""
|
|
attention_keywords = [
|
|
'attention', 'attn', 'self_attn', 'cross_attn', 'mhattn', 'multihead',
|
|
'transformer_block', 'dit_block'
|
|
]
|
|
|
|
# Check by name
|
|
if any(keyword in name.lower() for keyword in attention_keywords):
|
|
return True
|
|
|
|
# Check by module type
|
|
module_type = type(module).__name__.lower()
|
|
if any(keyword in module_type for keyword in attention_keywords):
|
|
return True
|
|
|
|
# Check by attributes (modules with q, k, v projections)
|
|
if hasattr(module, 'q_proj') or hasattr(module, 'qkv') or hasattr(module, 'to_q'):
|
|
return True
|
|
|
|
return False
|
|
|
|
def _optimize_attention_layer(self, name: str, module: torch.nn.Module, flash_attention_available: bool) -> bool:
|
|
"""Optimize a specific attention layer"""
|
|
try:
|
|
# Save original forward method
|
|
if not hasattr(module, '_original_forward'):
|
|
module._original_forward = module.forward
|
|
|
|
# Create new optimized forward method
|
|
if flash_attention_available:
|
|
optimized_forward = self._create_flash_attention_forward(module, name)
|
|
else:
|
|
optimized_forward = self._create_sdpa_forward(module, name)
|
|
|
|
# Replace forward method
|
|
module.forward = optimized_forward
|
|
return True
|
|
|
|
except Exception as e:
|
|
self.debug.log(f"Failed to optimize attention layer '{name}': {e}", level="WARNING", category="dit", force=True)
|
|
return False
|
|
|
|
def _create_flash_attention_forward(self, module: torch.nn.Module, layer_name: str):
|
|
"""Create optimized forward with Flash Attention"""
|
|
original_forward = module._original_forward
|
|
|
|
def flash_attention_forward(*args, **kwargs):
|
|
try:
|
|
# Try to use Flash Attention via SDPA
|
|
return self._sdpa_attention_forward(original_forward, module, *args, **kwargs)
|
|
except Exception as e:
|
|
# Fallback to original implementation
|
|
self.debug.log(f"Flash Attention failed for {layer_name}, using original: {e}", level="WARNING", category="dit", force=True)
|
|
return original_forward(*args, **kwargs)
|
|
|
|
return flash_attention_forward
|
|
|
|
def _create_sdpa_forward(self, module: torch.nn.Module, layer_name: str):
|
|
"""Create optimized forward with PyTorch SDPA"""
|
|
original_forward = module._original_forward
|
|
|
|
def sdpa_forward(*args, **kwargs):
|
|
try:
|
|
return self._sdpa_attention_forward(original_forward, module, *args, **kwargs)
|
|
except Exception as e:
|
|
# Fallback to original implementation
|
|
return original_forward(*args, **kwargs)
|
|
|
|
return sdpa_forward
|
|
|
|
def _sdpa_attention_forward(self, original_forward, module: torch.nn.Module, *args, **kwargs):
|
|
"""Optimized forward pass using SDPA (Scaled Dot Product Attention)"""
|
|
# Detect if we can intercept and optimize this layer
|
|
if len(args) >= 1 and isinstance(args[0], torch.Tensor):
|
|
input_tensor = args[0]
|
|
|
|
# Check dimensions to ensure it's standard attention
|
|
if len(input_tensor.shape) >= 3: # [batch, seq_len, hidden_dim] or similar
|
|
try:
|
|
return self._optimized_attention_computation(module, input_tensor, *args[1:], **kwargs)
|
|
except:
|
|
pass
|
|
|
|
# Fallback to original implementation
|
|
return original_forward(*args, **kwargs)
|
|
|
|
def _optimized_attention_computation(self, module: torch.nn.Module, input_tensor: torch.Tensor, *args, **kwargs):
|
|
"""Optimized attention computation with SDPA"""
|
|
# Try to detect standard attention format
|
|
batch_size, seq_len = input_tensor.shape[:2]
|
|
|
|
# Check if module has standard Q, K, V projections
|
|
if hasattr(module, 'qkv') or (hasattr(module, 'q_proj') and hasattr(module, 'k_proj') and hasattr(module, 'v_proj')):
|
|
return self._compute_sdpa_attention(module, input_tensor, *args, **kwargs)
|
|
|
|
# If no standard format detected, use original
|
|
return module._original_forward(input_tensor, *args, **kwargs)
|
|
|
|
def _compute_sdpa_attention(self, module: torch.nn.Module, x: torch.Tensor, *args, **kwargs):
|
|
"""Optimized SDPA computation for standard attention modules"""
|
|
try:
|
|
# Case 1: Module with combined QKV projection
|
|
if hasattr(module, 'qkv'):
|
|
qkv = module.qkv(x)
|
|
# Reshape to separate Q, K, V
|
|
batch_size, seq_len, _ = qkv.shape
|
|
qkv = qkv.reshape(batch_size, seq_len, 3, -1)
|
|
q, k, v = qkv.unbind(dim=2)
|
|
|
|
# Case 2: Separate Q, K, V projections
|
|
elif hasattr(module, 'q_proj') and hasattr(module, 'k_proj') and hasattr(module, 'v_proj'):
|
|
q = module.q_proj(x)
|
|
k = module.k_proj(x)
|
|
v = module.v_proj(x)
|
|
else:
|
|
# Unsupported format, use original
|
|
return module._original_forward(x, *args, **kwargs)
|
|
|
|
# Detect number of heads
|
|
head_dim = getattr(module, 'head_dim', None)
|
|
num_heads = getattr(module, 'num_heads', None)
|
|
|
|
if head_dim is None or num_heads is None:
|
|
# Try to guess from dimensions
|
|
hidden_dim = q.shape[-1]
|
|
if hasattr(module, 'num_heads'):
|
|
num_heads = module.num_heads
|
|
head_dim = hidden_dim // num_heads
|
|
else:
|
|
# Reasonable defaults
|
|
head_dim = 64
|
|
num_heads = hidden_dim // head_dim
|
|
|
|
# Reshape for multi-head attention
|
|
batch_size, seq_len = q.shape[:2]
|
|
q = q.view(batch_size, seq_len, num_heads, head_dim).transpose(1, 2)
|
|
k = k.view(batch_size, seq_len, num_heads, head_dim).transpose(1, 2)
|
|
v = v.view(batch_size, seq_len, num_heads, head_dim).transpose(1, 2)
|
|
|
|
if hasattr(torch, 'mps') and callable(getattr(torch.mps, 'is_available', None)) and torch.mps.is_available():
|
|
attn_output = torch.nn.functional.scaled_dot_product_attention(
|
|
q, k, v,
|
|
dropout_p=0.0,
|
|
is_causal=False
|
|
)
|
|
else:
|
|
# Use optimized SDPA
|
|
with torch.backends.cuda.sdp_kernel(
|
|
enable_flash=True,
|
|
enable_math=True,
|
|
enable_mem_efficient=True
|
|
):
|
|
attn_output = torch.nn.functional.scaled_dot_product_attention(
|
|
q, k, v,
|
|
dropout_p=0.0,
|
|
is_causal=False
|
|
)
|
|
|
|
# Reshape back
|
|
attn_output = attn_output.transpose(1, 2).contiguous().view(
|
|
batch_size, seq_len, num_heads * head_dim
|
|
)
|
|
|
|
# Output projection if it exists
|
|
if hasattr(module, 'out_proj') or hasattr(module, 'o_proj'):
|
|
proj = getattr(module, 'out_proj', None) or getattr(module, 'o_proj', None)
|
|
attn_output = proj(attn_output)
|
|
|
|
return attn_output
|
|
|
|
except Exception as e:
|
|
# In case of error, use original implementation
|
|
return module._original_forward(x, *args, **kwargs)
|
|
|
|
def forward(self, *args, **kwargs):
|
|
"""
|
|
Forward pass with minimal dtype conversion overhead
|
|
|
|
Conversion strategy:
|
|
- FP16/BFloat16/Float32 models: Use native precision (no conversion needed)
|
|
- FP8 models: Convert FP8 tensors to compute_dtype for arithmetic operations
|
|
(FP8 parameters stay in FP8 for memory efficiency, only converted for computation)
|
|
"""
|
|
|
|
# Only convert if we have an FP8 model for arithmetic operations
|
|
if self.is_fp8_model:
|
|
fp8_dtypes = (torch.float8_e4m3fn, torch.float8_e5m2)
|
|
target_dtype = self.compute_dtype
|
|
|
|
# Convert args
|
|
converted_args = []
|
|
for arg in args:
|
|
if isinstance(arg, torch.Tensor) and arg.dtype in fp8_dtypes:
|
|
converted_args.append(arg.to(target_dtype))
|
|
else:
|
|
converted_args.append(arg)
|
|
|
|
# Convert kwargs
|
|
converted_kwargs = {}
|
|
for key, value in kwargs.items():
|
|
if isinstance(value, torch.Tensor) and value.dtype in fp8_dtypes:
|
|
converted_kwargs[key] = value.to(target_dtype)
|
|
else:
|
|
converted_kwargs[key] = value
|
|
|
|
args = tuple(converted_args)
|
|
kwargs = converted_kwargs
|
|
|
|
# Execute forward pass
|
|
try:
|
|
return self.dit_model(*args, **kwargs)
|
|
except Exception as e:
|
|
self.debug.log(f"Forward pass error: {e}", level="ERROR", category="generation", force=True)
|
|
if self.is_fp8_model:
|
|
self.debug.log(f"FP8 model - converted FP8 tensors to {self.compute_dtype}", category="info", force=True)
|
|
else:
|
|
self.debug.log(f"{self.model_dtype} model - no conversion applied", category="info", force=True)
|
|
raise
|
|
|
|
def __getattr__(self, name):
|
|
"""Redirect all other attributes to original model"""
|
|
if name in ['dit_model', 'model_dtype', 'is_fp8_model', 'is_fp16_model']:
|
|
return super().__getattr__(name)
|
|
return getattr(self.dit_model, name)
|
|
|
|
def __setattr__(self, name, value):
|
|
"""Redirect assignments to original model except for our attributes"""
|
|
if name in ['dit_model', 'model_dtype', 'is_fp8_model', 'is_fp16_model']:
|
|
super().__setattr__(name, value)
|
|
else:
|
|
if hasattr(self, 'dit_model'):
|
|
setattr(self.dit_model, name, value)
|
|
else:
|
|
super().__setattr__(name, value)
|
|
|