Module neuralops.compression_engine.svd_linear
SVD-based low-rank linear layer replacement with optional Fused SVD kernel acceleration. Registered as 'svd_linear' and 'svd_linear_compressor'.
Functions
def fused_svd_linear(x: torch.Tensor,
V: torch.Tensor,
U: torch.Tensor,
norm_weight: torch.Tensor | None = None,
bias: torch.Tensor | None = None,
eps: float = 1e-06,
use_rms_norm: bool = False) ‑> torch.Tensor-
Expand source code
def fused_svd_linear( x: torch.Tensor, V: torch.Tensor, U: torch.Tensor, norm_weight: torch.Tensor | None = None, bias: torch.Tensor | None = None, eps: float = 1e-6, use_rms_norm: bool = False, ) -> torch.Tensor: """ Functional interface for Fused SVD-Linear with optional in-SRAM RMSNorm. Args: x: Input tensor of shape (..., in_features). V: Down-projection matrix of shape (rank, in_features). U: Up-projection matrix of shape (out_features, rank). norm_weight: Optional RMSNorm affine scale vector of shape (rank,). bias: Optional bias vector of shape (out_features,). eps: Small epsilon for RMSNorm numerical stability. use_rms_norm: Whether to apply in-SRAM RMSNorm on intermediate rank activations. Returns: Output tensor of shape (..., out_features). """ return _FusedSVDLinearAutogradFunction.apply(x, V, U, norm_weight, bias, eps, use_rms_norm)Functional interface for Fused SVD-Linear with optional in-SRAM RMSNorm.
- Args
- -----=
x- Input tensor of shape (…, in_features).
V- Down-projection matrix of shape (rank, in_features).
U- Up-projection matrix of shape (out_features, rank).
norm_weight- Optional RMSNorm affine scale vector of shape (rank,).
bias- Optional bias vector of shape (out_features,).
eps- Small epsilon for RMSNorm numerical stability.
use_rms_norm- Whether to apply in-SRAM RMSNorm on intermediate rank activations.
Returns -----= Output tensor of shape (…, out_features).
def svd_linear_factory(model: Any, comp_config: Any = None, **kwargs: Any) ‑> Any-
Expand source code
@COMPRESSOR_REGISTRY.register("svd_linear_factory") @COMPRESSOR_REGISTRY.register("svd_linear_compressor_factory") def svd_linear_factory(model: Any, comp_config: Any = None, **kwargs: Any) -> Any: """SVD Linear compressor factory.""" return_tuple = kwargs.pop("return_stats", kwargs.pop("return_tuple", False)) rank = kwargs.pop("rank", _cfg_get(comp_config, "svd_rank", _cfg_get(comp_config, "rank", 192))) max_error = kwargs.pop("max_error", _cfg_get(comp_config, "max_error", 0.50)) fallback = kwargs.pop( "fallback_to_original", _cfg_get(comp_config, "fallback_to_original", True) ) lora_r = kwargs.pop( "lora_r", _cfg_get(comp_config, "lora_r", _cfg_get(comp_config, "lora_rank", 0)) ) lora_alpha = kwargs.pop("lora_alpha", _cfg_get(comp_config, "lora_alpha", 1.0)) skip_attention_qkv = kwargs.pop( "skip_attention_qkv", _cfg_get(comp_config, "skip_attention_qkv", False) ) target_attention_only = kwargs.pop( "target_attention_only", _cfg_get(comp_config, "target_attention_only", False) ) protect_critical = kwargs.pop( "protect_critical", _cfg_get(comp_config, "protect_critical", True) ) compressor = SVDLinearCompressor( model, rank=rank, max_error=max_error, fallback_to_original=fallback, lora_r=lora_r, lora_alpha=lora_alpha, skip_attention_qkv=skip_attention_qkv, target_attention_only=target_attention_only, protect_critical=protect_critical, comp_config=comp_config, **kwargs, ) compressed_model, stats = compressor.compress() if return_tuple or kwargs.pop("return_both", False): return compressed_model, stats return compressed_modelSVD Linear compressor factory.
Classes
class FusedSVDLinear (in_features: int,
out_features: int,
rank: int = 32,
bias: bool = True,
use_rms_norm: bool = False,
eps: float = 1e-06,
dtype: torch.dtype = torch.float32,
device: torch.device | None = None)-
Expand source code
class FusedSVDLinear(nn.Module): """ RESEARCH SOTA: Fused SVD-Linear Module. Combines low-rank projection $Z = x V^T$, in-SRAM RMSNorm, and up-projection $Y = Z U^T + b$ into a single kernel invocation, eliminating HBM roundtrips. """ def __init__( self, in_features: int, out_features: int, rank: int = 32, bias: bool = True, use_rms_norm: bool = False, eps: float = 1e-6, dtype: torch.dtype = torch.float32, device: torch.device | None = None, ) -> None: super().__init__() self.in_features = in_features self.out_features = out_features self.rank = rank self.eps = eps self.use_rms_norm = use_rms_norm # Low-rank factorized parameters self.V = nn.Parameter(torch.empty(rank, in_features, dtype=dtype, device=device)) self.U = nn.Parameter(torch.empty(out_features, rank, dtype=dtype, device=device)) if use_rms_norm: self.norm_weight = nn.Parameter(torch.ones(rank, dtype=dtype, device=device)) else: self.register_parameter("norm_weight", None) if bias: self.bias = nn.Parameter(torch.empty(out_features, dtype=dtype, device=device)) else: self.register_parameter("bias", None) self.reset_parameters() def reset_parameters(self) -> None: # Symmetrical square-root initialization nn.init.orthogonal_(self.U) nn.init.orthogonal_(self.V) if self.bias is not None: nn.init.zeros_(self.bias) if self.norm_weight is not None: nn.init.ones_(self.norm_weight) @property def weight(self) -> torch.Tensor: """Reconstructed effective weight matrix for compatibility.""" return torch.matmul(self.U, self.V) def forward(self, x: torch.Tensor) -> torch.Tensor: # RESEARCH FIX: Dispatches fused kernel executing down-projection, RMSNorm, and up-projection return fused_svd_linear( x=x, V=self.V, U=self.U, norm_weight=self.norm_weight, bias=self.bias, eps=self.eps, use_rms_norm=self.use_rms_norm, ) def set_low_rank_factors(self, U_factor: torch.Tensor, V_factor: torch.Tensor) -> None: """Set precomputed U and V low-rank factors.""" with torch.no_grad(): self.U.copy_(U_factor) self.V.copy_(V_factor) def decompose_from_weight(self, weight: torch.Tensor, bias: torch.Tensor | None = None) -> None: """Decompose dense weight matrix into symmetric SVD factors.""" with torch.no_grad(): W = weight.float() U, S, Vt = torch.linalg.svd(W, full_matrices=False) r = min(self.rank, len(S)) sqrt_s = torch.sqrt(S[:r]) self.U.data.zero_() self.V.data.zero_() self.U.data[:, :r] = (U[:, :r] * sqrt_s).to(self.U.dtype) self.V.data[:r, :] = (sqrt_s.unsqueeze(1) * Vt[:r, :]).to(self.V.dtype) if bias is not None and self.bias is not None: self.bias.data.copy_(bias) def get_orthogonality_loss(self) -> torch.Tensor: """ # RESEARCH FIX: Computes Stiefel manifold penalty to prevent singular subspace collapse. """ u_gram = torch.matmul(self.U.T, self.U) v_gram = torch.matmul(self.V, self.V.T) eye_r = torch.eye(self.rank, device=self.U.device, dtype=self.U.dtype) loss_u = torch.norm(u_gram - eye_r, p="fro") ** 2 loss_v = torch.norm(v_gram - eye_r, p="fro") ** 2 return (loss_u + loss_v) / (self.rank * self.rank) def extra_repr(self) -> str: return ( f"in_features={self.in_features}, out_features={self.out_features}, " f"rank={self.rank}, rms_norm={self.use_rms_norm}, bias={self.bias is not None}" )RESEARCH SOTA: Fused SVD-Linear Module. Combines low-rank projection $Z = x V^T$, in-SRAM RMSNorm, and up-projection $Y = Z U^T + b$ into a single kernel invocation, eliminating HBM roundtrips.
Initialize internal Module state, shared by both nn.Module and ScriptModule.
Ancestors
- torch.nn.modules.module.Module
Instance variables
prop weight : torch.Tensor-
Expand source code
@property def weight(self) -> torch.Tensor: """Reconstructed effective weight matrix for compatibility.""" return torch.matmul(self.U, self.V)Reconstructed effective weight matrix for compatibility.
Methods
def decompose_from_weight(self, weight: torch.Tensor, bias: torch.Tensor | None = None) ‑> None-
Expand source code
def decompose_from_weight(self, weight: torch.Tensor, bias: torch.Tensor | None = None) -> None: """Decompose dense weight matrix into symmetric SVD factors.""" with torch.no_grad(): W = weight.float() U, S, Vt = torch.linalg.svd(W, full_matrices=False) r = min(self.rank, len(S)) sqrt_s = torch.sqrt(S[:r]) self.U.data.zero_() self.V.data.zero_() self.U.data[:, :r] = (U[:, :r] * sqrt_s).to(self.U.dtype) self.V.data[:r, :] = (sqrt_s.unsqueeze(1) * Vt[:r, :]).to(self.V.dtype) if bias is not None and self.bias is not None: self.bias.data.copy_(bias)Decompose dense weight matrix into symmetric SVD factors.
def extra_repr(self) ‑> str-
Expand source code
def extra_repr(self) -> str: return ( f"in_features={self.in_features}, out_features={self.out_features}, " f"rank={self.rank}, rms_norm={self.use_rms_norm}, bias={self.bias is not None}" )Return the extra representation of the module.
To print customized extra information, you should re-implement this method in your own modules. Both single-line and multi-line strings are acceptable.
def forward(self, x: torch.Tensor) ‑> torch.Tensor-
Expand source code
def forward(self, x: torch.Tensor) -> torch.Tensor: # RESEARCH FIX: Dispatches fused kernel executing down-projection, RMSNorm, and up-projection return fused_svd_linear( x=x, V=self.V, U=self.U, norm_weight=self.norm_weight, bias=self.bias, eps=self.eps, use_rms_norm=self.use_rms_norm, )Define the computation performed at every call.
Should be overridden by all subclasses.
Note
Although the recipe for forward pass needs to be defined within this function, one should call the :class:
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them. def get_orthogonality_loss(self) ‑> torch.Tensor-
Expand source code
def get_orthogonality_loss(self) -> torch.Tensor: """ # RESEARCH FIX: Computes Stiefel manifold penalty to prevent singular subspace collapse. """ u_gram = torch.matmul(self.U.T, self.U) v_gram = torch.matmul(self.V, self.V.T) eye_r = torch.eye(self.rank, device=self.U.device, dtype=self.U.dtype) loss_u = torch.norm(u_gram - eye_r, p="fro") ** 2 loss_v = torch.norm(v_gram - eye_r, p="fro") ** 2 return (loss_u + loss_v) / (self.rank * self.rank)RESEARCH FIX: Computes Stiefel manifold penalty to prevent singular subspace collapse.
def reset_parameters(self) ‑> None-
Expand source code
def reset_parameters(self) -> None: # Symmetrical square-root initialization nn.init.orthogonal_(self.U) nn.init.orthogonal_(self.V) if self.bias is not None: nn.init.zeros_(self.bias) if self.norm_weight is not None: nn.init.ones_(self.norm_weight) def set_low_rank_factors(self, U_factor: torch.Tensor, V_factor: torch.Tensor) ‑> None-
Expand source code
def set_low_rank_factors(self, U_factor: torch.Tensor, V_factor: torch.Tensor) -> None: """Set precomputed U and V low-rank factors.""" with torch.no_grad(): self.U.copy_(U_factor) self.V.copy_(V_factor)Set precomputed U and V low-rank factors.
class SVDLinearCompressor (model: nn.Module,
rank: int = 192,
max_error: float = 0.2,
fallback_to_original: bool = True,
protect_critical: bool = True,
target_attention_only: bool = False,
lora_r: int = 0,
lora_alpha: float = 1.0,
skip_attention_qkv: bool = False,
target_modules: list[str] | None = None,
cfg: Any | None = None,
**kwargs: Any)-
Expand source code
@COMPRESSOR_REGISTRY.register("svd_linear") @COMPRESSOR_REGISTRY.register("svd_linear_compressor") class SVDLinearCompressor(BaseCompressor): """Compresses model by replacing Linear/Conv1D layers with SVDLinearLayer.""" def __init__( self, model: nn.Module, rank: int = 192, max_error: float = 0.20, fallback_to_original: bool = True, protect_critical: bool = True, target_attention_only: bool = False, lora_r: int = 0, lora_alpha: float = 1.0, skip_attention_qkv: bool = False, target_modules: list[str] | None = None, cfg: Any | None = None, **kwargs: Any, ) -> None: super().__init__(model, name="svd_linear", **kwargs) conf = cfg or kwargs.get("comp_config") if conf is not None: rank = _cfg_get(conf, "svd_rank", _cfg_get(conf, "rank", rank)) max_error = _cfg_get(conf, "max_error", max_error) fallback_to_original = _cfg_get(conf, "fallback_to_original", fallback_to_original) protect_critical = _cfg_get(conf, "protect_critical", protect_critical) target_attention_only = _cfg_get(conf, "target_attention_only", target_attention_only) lora_r = _cfg_get(conf, "lora_r", _cfg_get(conf, "lora_rank", lora_r)) lora_alpha = _cfg_get(conf, "lora_alpha", lora_alpha) skip_attention_qkv = _cfg_get(conf, "skip_attention_qkv", skip_attention_qkv) self.rank = int(rank) self.max_error = float(max_error) self.fallback_to_original = bool(fallback_to_original) self.protect_critical = bool(protect_critical) self.target_attention_only = bool(target_attention_only) self.lora_r = int(lora_r) self.lora_alpha = float(lora_alpha) self.skip_attention_qkv = bool(skip_attention_qkv) self.target_modules = target_modules self.layer_errors: dict[str, float] = {} self.replaced_layers: list[dict[str, Any]] = [] self.compressed_layers: list[str] = [] self.fallback_layers: list[str] = [] self.skipped_layers: list[str] = [] self.stats: dict[str, Any] = {} def _is_gqa_asymmetric(self, name: str, module: nn.Module) -> bool: if not self.skip_attention_qkv: return False cfg = getattr(self.model, "config", None) num_heads = getattr(cfg, "num_attention_heads", None) num_kv_heads = getattr(cfg, "num_key_value_heads", None) if num_heads is not None and num_kv_heads is not None and num_kv_heads < num_heads: name_lower = name.lower() if any( p in name_lower for p in [ "q_proj", "k_proj", "v_proj", "query", "key", "value", "c_attn", ] ): return True return False def _is_target_module(self, name: str, module: nn.Module) -> bool: name_l = name.lower() if any( k in name_l for k in [ "out_lin", "out_proj", "o_proj", "c_proj", "output.dense", "self.output", ] ): return False if self.protect_critical and any( k in name_l for k in [ "classifier", "score", "lm_head", "pooler", "embed", "logits_processor", ] ): return False if self.target_attention_only: attn_keywords = [ "attn", "attention", "q_proj", "k_proj", "v_proj", "o_proj", "c_attn", "c_proj", "query", "key", "value", ] if not any(k in name_l for k in attn_keywords): return False base_mod = getattr(module, "base_layer", module) cls_name = base_mod.__class__.__name__.lower() is_linear_like = ( isinstance(base_mod, nn.Linear) or "conv1d" in cls_name or hasattr(base_mod, "nf") ) if not is_linear_like: return False if isinstance(base_mod, SVDLinearLayer): return False if not should_compress_linear(self.model, name): return False if self._is_gqa_asymmetric(name, module): return False if self.target_modules is not None: return any(t in name for t in self.target_modules) return True def compress(self, **kwargs: Any) -> tuple[nn.Module, dict[str, Any]]: """Compress linear layers with SVD factorization.""" self.layer_errors = {} self.replaced_layers = [] self.compressed_layers = [] self.fallback_layers = [] self.skipped_layers = [] seen_weights: set[int] = set() target_layers = [ (name, mod) for name, mod in list(self.model.named_modules()) if self._is_target_module(name, mod) ] if not target_layers: logger.warning("SVDLinear: No compressible linear layers found.") return self.model, self.get_compression_stats() for name, module in target_layers: base_mod = getattr(module, "base_layer", module) if not hasattr(base_mod, "weight") or base_mod.weight is None: continue w_id = id(base_mod.weight) if w_id in seen_weights: self.skipped_layers.append(name) continue seen_weights.add(w_id) try: new_layer = SVDLinearLayer( module, rank=self.rank, lora_r=self.lora_r, lora_alpha=self.lora_alpha, ) err = new_layer.reconstruction_error self.layer_errors[name] = err if err > self.max_error: logger.warning( "SVDLinear reconstruction error %.4f on %s exceeded threshold %.4f.", err, name, self.max_error, ) if self.fallback_to_original: logger.info( "Skipping SVDLinear compression for %s (fallback to original).", name, ) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False continue self.replaced_layers.append({"name": name, "rank": new_layer.rank, "error": err}) self.compressed_layers.append(name) replace_module(self.model, name, new_layer) except Exception as e: logger.warning("Failed to compress %s with SVDLinear: %s", name, e) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False # Post-compression forward sanity check forward_check_success = False try: self.model.eval() with torch.no_grad(): d = next(self.model.parameters()).device dummy = torch.randint(0, 100, (1, 16), device=d) self.model(dummy) forward_check_success = True except Exception as e: logger.debug("SVDLinear post-compression forward sanity check exception: %s", e) self._compressed_params = self._count_params(self.model) avg_err = ( sum(self.layer_errors[k] for k in self.compressed_layers) / max(1, len(self.compressed_layers)) if self.compressed_layers else 0.0 ) stats = self.get_compression_stats() stats.update( { "layer_errors": self.layer_errors, "compressed_layers": self.compressed_layers, "fallback_layers": self.fallback_layers, "skipped_layers": self.skipped_layers, "num_compressed": len(self.compressed_layers), "num_fallback": len(self.fallback_layers), "num_skipped": len(self.skipped_layers), "avg_reconstruction_error": avg_err, "max_error": self.max_error, "fallback_to_original": self.fallback_to_original, "target_attention_only": self.target_attention_only, "lora_r": self.lora_r, "lora_alpha": self.lora_alpha, "forward_check_success": forward_check_success, "method": "svd_linear_factorization", } ) self.stats = stats return self.model, statsCompresses model by replacing Linear/Conv1D layers with SVDLinearLayer.
Ancestors
Methods
def compress(self, **kwargs: Any) ‑> tuple[torch.nn.modules.module.Module, dict[str, typing.Any]]-
Expand source code
def compress(self, **kwargs: Any) -> tuple[nn.Module, dict[str, Any]]: """Compress linear layers with SVD factorization.""" self.layer_errors = {} self.replaced_layers = [] self.compressed_layers = [] self.fallback_layers = [] self.skipped_layers = [] seen_weights: set[int] = set() target_layers = [ (name, mod) for name, mod in list(self.model.named_modules()) if self._is_target_module(name, mod) ] if not target_layers: logger.warning("SVDLinear: No compressible linear layers found.") return self.model, self.get_compression_stats() for name, module in target_layers: base_mod = getattr(module, "base_layer", module) if not hasattr(base_mod, "weight") or base_mod.weight is None: continue w_id = id(base_mod.weight) if w_id in seen_weights: self.skipped_layers.append(name) continue seen_weights.add(w_id) try: new_layer = SVDLinearLayer( module, rank=self.rank, lora_r=self.lora_r, lora_alpha=self.lora_alpha, ) err = new_layer.reconstruction_error self.layer_errors[name] = err if err > self.max_error: logger.warning( "SVDLinear reconstruction error %.4f on %s exceeded threshold %.4f.", err, name, self.max_error, ) if self.fallback_to_original: logger.info( "Skipping SVDLinear compression for %s (fallback to original).", name, ) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False continue self.replaced_layers.append({"name": name, "rank": new_layer.rank, "error": err}) self.compressed_layers.append(name) replace_module(self.model, name, new_layer) except Exception as e: logger.warning("Failed to compress %s with SVDLinear: %s", name, e) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False # Post-compression forward sanity check forward_check_success = False try: self.model.eval() with torch.no_grad(): d = next(self.model.parameters()).device dummy = torch.randint(0, 100, (1, 16), device=d) self.model(dummy) forward_check_success = True except Exception as e: logger.debug("SVDLinear post-compression forward sanity check exception: %s", e) self._compressed_params = self._count_params(self.model) avg_err = ( sum(self.layer_errors[k] for k in self.compressed_layers) / max(1, len(self.compressed_layers)) if self.compressed_layers else 0.0 ) stats = self.get_compression_stats() stats.update( { "layer_errors": self.layer_errors, "compressed_layers": self.compressed_layers, "fallback_layers": self.fallback_layers, "skipped_layers": self.skipped_layers, "num_compressed": len(self.compressed_layers), "num_fallback": len(self.fallback_layers), "num_skipped": len(self.skipped_layers), "avg_reconstruction_error": avg_err, "max_error": self.max_error, "fallback_to_original": self.fallback_to_original, "target_attention_only": self.target_attention_only, "lora_r": self.lora_r, "lora_alpha": self.lora_alpha, "forward_check_success": forward_check_success, "method": "svd_linear_factorization", } ) self.stats = stats return self.model, statsCompress linear layers with SVD factorization.
Inherited members
class SVDLinearLayer (linear_layer: nn.Module,
rank: int,
use_fused_kernel: bool = False,
use_rms_norm: bool = False,
eps: float = 1e-06,
lora_r: int = 0,
lora_alpha: float = 1.0)-
Expand source code
class SVDLinearLayer(nn.Module): is_custom_attention: bool = True """ Replace a linear layer with factorized U @ V, keeping top-`rank` components. Optionally dispatches fused in-SRAM kernel to eliminate memory roundtrips. Includes optional trainable LoRA adapters (lora_A, lora_B). """ def __init__( self, linear_layer: nn.Module, rank: int, use_fused_kernel: bool = False, use_rms_norm: bool = False, eps: float = 1e-6, lora_r: int = 0, lora_alpha: float = 1.0, ) -> None: super().__init__() base_layer = getattr(linear_layer, "base_layer", linear_layer) raw_w = base_layer.weight.data cls_name = base_layer.__class__.__name__.lower() is_conv1d = "conv1d" in cls_name or hasattr(base_layer, "nf") if is_conv1d: W = raw_w.t().contiguous().float() self.out_features = W.shape[0] self.in_features = W.shape[1] else: W = raw_w.float() self.out_features = getattr(base_layer, "out_features", W.shape[0]) self.in_features = getattr(base_layer, "in_features", W.shape[1]) self.nf = self.out_features # Conv1D compatibility param_cap = max( 1, (self.in_features * self.out_features) // (self.in_features + self.out_features) - 1, ) self.rank: int = max(1, min(rank, self.in_features, self.out_features, param_cap)) self.use_fused_kernel: bool = use_fused_kernel self.use_rms_norm: bool = use_rms_norm self.eps: float = eps has_existing_lora = hasattr(linear_layer, "lora_A") and hasattr(linear_layer, "lora_B") effective_lora_r = lora_r if has_existing_lora and getattr(linear_layer, "r", 0) > 0: effective_lora_r = getattr(linear_layer, "r", lora_r) self.lora_r = int(effective_lora_r) self.lora_alpha = float(lora_alpha) self.scaling = (self.lora_alpha / self.lora_r) if self.lora_r > 0 else 1.0 device = base_layer.weight.device dtype = base_layer.weight.dtype with torch.no_grad(): U, S, Vh = torch.linalg.svd(W, full_matrices=False) sqrt_s = torch.sqrt(torch.clamp(S[: self.rank], min=1e-12)) U_weighted = (U[:, : self.rank] * sqrt_s.unsqueeze(0)).to(device=device, dtype=dtype) V_weighted = (sqrt_s.unsqueeze(1) * Vh[: self.rank, :]).to(device=device, dtype=dtype) requires_grad = not (self.lora_r > 0) self.U = nn.Parameter(U_weighted, requires_grad=requires_grad) self.V = nn.Parameter(V_weighted, requires_grad=requires_grad) if use_rms_norm: self.norm_weight = nn.Parameter(torch.ones(self.rank, dtype=dtype, device=device)) else: self.register_parameter("norm_weight", None) if base_layer.bias is not None: self.bias = nn.Parameter(base_layer.bias.data.clone(), requires_grad=requires_grad) else: self.register_parameter("bias", None) if self.lora_r > 0: self.lora_A = nn.Parameter( torch.empty((self.lora_r, self.in_features), dtype=torch.float32, device=device) ) self.lora_B = nn.Parameter( torch.zeros((self.out_features, self.lora_r), dtype=torch.float32, device=device) ) if has_existing_lora: try: if hasattr(linear_layer.lora_A, "weight"): self.lora_A.data.copy_(linear_layer.lora_A.weight.data) if hasattr(linear_layer.lora_B, "weight"): self.lora_B.data.copy_(linear_layer.lora_B.weight.data) except Exception: nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) else: nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) else: self.register_parameter("lora_A", None) self.register_parameter("lora_B", None) # Frobenius reconstruction error W_rec = torch.matmul(U_weighted.float(), V_weighted.float()) norm_orig = torch.norm(W, p="fro") self.reconstruction_error = (torch.norm(W - W_rec, p="fro") / (norm_orig + 1e-8)).item() @property def weight(self) -> torch.Tensor: """Reconstructed effective weight matrix for compatibility.""" return torch.matmul(self.U, self.V) def forward(self, x: torch.Tensor) -> torch.Tensor: """Forward pass with optional fused kernel acceleration and LoRA branch.""" if self.use_fused_kernel and self.lora_r <= 0 and fused_svd_linear is not None: return fused_svd_linear( x=x, V=self.V, U=self.U, norm_weight=self.norm_weight if self.use_rms_norm else None, bias=self.bias, eps=self.eps, use_rms_norm=self.use_rms_norm, ) latent = F.linear(x, self.V) out = F.linear(latent, self.U, self.bias) if self.lora_r > 0 and self.lora_A is not None and self.lora_B is not None: orig_shape = x.shape x_flat = x.reshape(-1, self.in_features) lora_out = F.linear(F.linear(x_flat.float(), self.lora_A), self.lora_B) out = out + (self.scaling * lora_out).reshape(*orig_shape[:-1], self.out_features).to( out.dtype ) return outBase class for all neural network modules.
Your models should also subclass this class.
Modules can also contain other Modules, allowing them to be nested in a tree structure. You can assign the submodules as regular attributes::
import torch.nn as nn import torch.nn.functional as F class Model(nn.Module): def __init__(self) -> None: super().__init__() self.conv1 = nn.Conv2d(1, 20, 5) self.conv2 = nn.Conv2d(20, 20, 5) def forward(self, x): x = F.relu(self.conv1(x)) return F.relu(self.conv2(x))Submodules assigned in this way will be registered, and will also have their parameters converted when you call :meth:
to, etc.Note
As per the example above, an
__init__()call to the parent class must be made before assignment on the child.:ivar training: Boolean represents whether this module is in training or evaluation mode. :vartype training: bool
Initialize internal Module state, shared by both nn.Module and ScriptModule.
Ancestors
- torch.nn.modules.module.Module
Class variables
var is_custom_attention : bool-
Replace a linear layer with factorized U @ V, keeping top-
rankcomponents. Optionally dispatches fused in-SRAM kernel to eliminate memory roundtrips. Includes optional trainable LoRA adapters (lora_A, lora_B).
Instance variables
prop weight : torch.Tensor-
Expand source code
@property def weight(self) -> torch.Tensor: """Reconstructed effective weight matrix for compatibility.""" return torch.matmul(self.U, self.V)Reconstructed effective weight matrix for compatibility.
Methods
def forward(self, x: torch.Tensor) ‑> torch.Tensor-
Expand source code
def forward(self, x: torch.Tensor) -> torch.Tensor: """Forward pass with optional fused kernel acceleration and LoRA branch.""" if self.use_fused_kernel and self.lora_r <= 0 and fused_svd_linear is not None: return fused_svd_linear( x=x, V=self.V, U=self.U, norm_weight=self.norm_weight if self.use_rms_norm else None, bias=self.bias, eps=self.eps, use_rms_norm=self.use_rms_norm, ) latent = F.linear(x, self.V) out = F.linear(latent, self.U, self.bias) if self.lora_r > 0 and self.lora_A is not None and self.lora_B is not None: orig_shape = x.shape x_flat = x.reshape(-1, self.in_features) lora_out = F.linear(F.linear(x_flat.float(), self.lora_A), self.lora_B) out = out + (self.scaling * lora_out).reshape(*orig_shape[:-1], self.out_features).to( out.dtype ) return outForward pass with optional fused kernel acceleration and LoRA branch.