Module neuralops.compression_engine.grecodec_compressor
GreCoDeC: Greedy Coordinate Descent Low-Rank Compressor. Registered in COMPRESSOR_REGISTRY as 'grecodec'.
Functions
def grecodec_compressor_factory(model: nn.Module,
comp_config: Any | None = None,
rank: int | None = None,
**kwargs: Any) ‑> torch.nn.modules.module.Module | tuple[torch.nn.modules.module.Module, dict[str, typing.Any]]-
Expand source code
@COMPRESSOR_REGISTRY.register("grecodec") @COMPRESSOR_REGISTRY.register("grecodec_compressor") @COMPRESSOR_REGISTRY.register("grecodec_factory") @COMPRESSOR_REGISTRY.register("grecodec_compressor_factory") def grecodec_compressor_factory( model: nn.Module, comp_config: Any | None = None, rank: int | None = None, **kwargs: Any, ) -> nn.Module | tuple[nn.Module, dict[str, Any]]: """Factory function for GreCoDeC compressor.""" effective_rank = rank or _cfg_get( comp_config, "grecodec_rank", _cfg_get(comp_config, "rank", 16) ) max_iter = _cfg_get(comp_config, "grecodec_max_iter", _cfg_get(comp_config, "max_iter", 15)) lora_r = kwargs.pop("lora_r", _cfg_get(comp_config, "lora_r", 0)) lora_alpha = kwargs.pop("lora_alpha", _cfg_get(comp_config, "lora_alpha", 1.0)) max_error = kwargs.pop( "max_error", _cfg_get(comp_config, "grecodec_max_error", _cfg_get(comp_config, "max_error", 0.35)), ) target_attention_only = kwargs.pop( "target_attention_only", _cfg_get( comp_config, "grecodec_target_attention_only", _cfg_get(comp_config, "target_attention_only", False), ), ) fallback = kwargs.pop( "fallback_to_original", _cfg_get( comp_config, "grecodec_fallback_to_original", _cfg_get(comp_config, "fallback_to_original", True), ), ) return_stats = kwargs.pop("return_stats", kwargs.pop("return_tuple", False)) compressor = GreCoDeCCompressor( model, rank=effective_rank, max_iter=max_iter, lora_r=lora_r, lora_alpha=lora_alpha, max_error=max_error, target_attention_only=target_attention_only, fallback_to_original=fallback, cfg=comp_config, **kwargs, ) compressed_model, stats = compressor.compress() try: compressed_model.compression_stats = stats except Exception: pass if return_stats: return compressed_model, stats return compressed_modelFactory function for GreCoDeC compressor.
def grecodec_factory(model: nn.Module,
comp_config: Any | None = None,
rank: int | None = None,
**kwargs: Any) ‑> torch.nn.modules.module.Module | tuple[torch.nn.modules.module.Module, dict[str, typing.Any]]-
Expand source code
@COMPRESSOR_REGISTRY.register("grecodec") @COMPRESSOR_REGISTRY.register("grecodec_compressor") @COMPRESSOR_REGISTRY.register("grecodec_factory") @COMPRESSOR_REGISTRY.register("grecodec_compressor_factory") def grecodec_compressor_factory( model: nn.Module, comp_config: Any | None = None, rank: int | None = None, **kwargs: Any, ) -> nn.Module | tuple[nn.Module, dict[str, Any]]: """Factory function for GreCoDeC compressor.""" effective_rank = rank or _cfg_get( comp_config, "grecodec_rank", _cfg_get(comp_config, "rank", 16) ) max_iter = _cfg_get(comp_config, "grecodec_max_iter", _cfg_get(comp_config, "max_iter", 15)) lora_r = kwargs.pop("lora_r", _cfg_get(comp_config, "lora_r", 0)) lora_alpha = kwargs.pop("lora_alpha", _cfg_get(comp_config, "lora_alpha", 1.0)) max_error = kwargs.pop( "max_error", _cfg_get(comp_config, "grecodec_max_error", _cfg_get(comp_config, "max_error", 0.35)), ) target_attention_only = kwargs.pop( "target_attention_only", _cfg_get( comp_config, "grecodec_target_attention_only", _cfg_get(comp_config, "target_attention_only", False), ), ) fallback = kwargs.pop( "fallback_to_original", _cfg_get( comp_config, "grecodec_fallback_to_original", _cfg_get(comp_config, "fallback_to_original", True), ), ) return_stats = kwargs.pop("return_stats", kwargs.pop("return_tuple", False)) compressor = GreCoDeCCompressor( model, rank=effective_rank, max_iter=max_iter, lora_r=lora_r, lora_alpha=lora_alpha, max_error=max_error, target_attention_only=target_attention_only, fallback_to_original=fallback, cfg=comp_config, **kwargs, ) compressed_model, stats = compressor.compress() try: compressed_model.compression_stats = stats except Exception: pass if return_stats: return compressed_model, stats return compressed_modelFactory function for GreCoDeC compressor.
Classes
class GreCoDeCCompressor (model: nn.Module | None = None,
rank: int = 16,
max_iter: int = 15,
lora_r: int = 0,
lora_alpha: float = 1.0,
max_error: float = 0.35,
target_attention_only: bool = False,
fallback_to_original: bool = True,
skip_attention_qkv: bool = False,
cfg: Any | None = None,
target_layers: list[str] | None = None,
**kwargs: Any)-
Expand source code
@COMPRESSOR_REGISTRY.register("grecodec_compressor_class") class GreCoDeCCompressor(BaseCompressor): """ GreCoDeC Compressor. Decomposes linear layers using coordinate descent low-rank factorization. """ def __init__( self, model: nn.Module | None = None, rank: int = 16, max_iter: int = 15, lora_r: int = 0, lora_alpha: float = 1.0, max_error: float = 0.35, target_attention_only: bool = False, fallback_to_original: bool = True, skip_attention_qkv: bool = False, cfg: Any | None = None, target_layers: list[str] | None = None, **kwargs: Any, ) -> None: super().__init__(model if model is not None else nn.Identity(), name="grecodec", **kwargs) cfg = cfg or kwargs.get("comp_config") self.cfg = cfg self.rank = int(_cfg_get(cfg, "grecodec_rank", _cfg_get(cfg, "rank", rank))) self.max_iter = int(_cfg_get(cfg, "grecodec_max_iter", _cfg_get(cfg, "max_iter", max_iter))) self.lora_r = int(_cfg_get(cfg, "lora_r", _cfg_get(cfg, "lora_rank", lora_r))) self.lora_alpha = float(_cfg_get(cfg, "lora_alpha", lora_alpha)) c_max_error = _cfg_get(cfg, "grecodec_max_error", _cfg_get(cfg, "max_error", max_error)) if c_max_error is None or c_max_error == 0.35: c_max_error = adaptive_max_error(self.rank, 768, 768, "distilbert") self.max_error = float(c_max_error) self.target_attention_only = bool( _cfg_get( cfg, "grecodec_target_attention_only", _cfg_get(cfg, "target_attention_only", target_attention_only), ) ) self.fallback_to_original = bool( _cfg_get( cfg, "grecodec_fallback_to_original", _cfg_get(cfg, "fallback_to_original", fallback_to_original), ) ) self.skip_attention_qkv = bool(_cfg_get(cfg, "skip_attention_qkv", skip_attention_qkv)) self.target_layers = target_layers or _cfg_get(cfg, "target_layers", None) self.compressed_layers: list[str] = [] self.fallback_layers: list[str] = [] self.skipped_layers: list[str] = [] self.layer_errors: dict[str, float] = {} 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_lower = name.lower() if any(part in name.split(".") for part in ["lora_A", "lora_B", "base_layer"]): return False if any( skip in name_lower for skip in [ "classifier", "score", "lm_head", "pooler", "embedding", "router", "gate", ] ): return False if self.target_attention_only: if not any(attn_kw in name_lower for attn_kw in _ATTN_NAMES): return False if self._is_gqa_asymmetric(name, module): return False target_mod = getattr(module, "base_layer", module) cls_name = target_mod.__class__.__name__.lower() is_linear = isinstance(target_mod, nn.Linear) is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") if not (is_linear or is_conv1d) or isinstance(target_mod, GreCoDeCLinear): return False if not should_compress_linear(self.model, name): return False if self.target_layers is not None: return any(t in name for t in self.target_layers) return True def compress(self, model: nn.Module | None = None) -> tuple[nn.Module, dict[str, Any]]: """Compress linear layers using GreCoDeC decomposition.""" if model is not None: self.model = model self._original_params = self._count_params(self.model) self.compressed_layers = [] self.fallback_layers = [] self.skipped_layers = [] self.layer_errors = {} seen_weights: set[int] = set() total_orig_params = 0 total_comp_params = 0 # Freeze skipped non-target layers if target_attention_only is enabled if self.target_attention_only: for name, module in self.model.named_modules(): if any(part in name.split(".") for part in ["lora_A", "lora_B", "base_layer"]): continue target_mod = getattr(module, "base_layer", module) cls_name = target_mod.__class__.__name__.lower() is_linear = isinstance(target_mod, nn.Linear) is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") if (is_linear or is_conv1d) and not self._is_target_module(name, module): for p in module.parameters(): p.requires_grad = False self.skipped_layers.append(name) for name, module in list(self.model.named_modules()): if not self._is_target_module(name, module): continue target_mod = getattr(module, "base_layer", module) if not hasattr(target_mod, "weight") or target_mod.weight is None: continue w_id = id(target_mod.weight) if w_id in seen_weights: self.skipped_layers.append(name) continue seen_weights.add(w_id) cls_name = target_mod.__class__.__name__.lower() is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") raw_w = target_mod.weight.data W_target = raw_w.t().contiguous().float() if is_conv1d else raw_w.float() out_f, in_f = W_target.shape[0], W_target.shape[1] orig_params = in_f * out_f total_orig_params += orig_params max_rank = min(in_f, out_f) bound_r = (in_f * out_f) // (in_f + out_f) if max_rank < 16 or bound_r < 4: self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params continue try: grecodec_layer = GreCoDeCLinear.from_linear( module, rank=self.rank, max_iter=self.max_iter, lora_r=self.lora_r, lora_alpha=self.lora_alpha, ) W_comp = grecodec_layer.get_weight().float() W_norm = torch.norm(W_target, p="fro").clamp_min(1e-8) err = float(torch.norm(W_target - W_comp, p="fro").item() / W_norm) self.layer_errors[name] = err if err > self.max_error: if self.fallback_to_original: self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params continue replace_module(self.model, name, grecodec_layer) self.compressed_layers.append(name) comp_params = grecodec_layer.rank * (in_f + out_f) if grecodec_layer.lora_r > 0: comp_params += grecodec_layer.lora_r * (in_f + out_f) total_comp_params += comp_params except Exception as e: logger.debug("GreCoDeC error on %s: %s", name, e) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params # Post-compression forward sanity check forward_check_success = True try: with torch.no_grad(): first_p = next(self.model.parameters(), None) dev = first_p.device if first_p is not None else torch.device("cpu") dummy = torch.randint(0, 100, (1, 8), device=dev) try: self.model(dummy) except Exception: first_linear = next( ( m for m in self.model.modules() if isinstance(m, (nn.Linear, GreCoDeCLinear)) ), None, ) in_dim = getattr(first_linear, "in_features", 8) self.model(torch.randn(1, 8, in_dim, device=dev)) except Exception: forward_check_success = False effective_ratio = total_comp_params / total_orig_params if total_orig_params > 0 else 1.0 self._compressed_params = self._count_params(self.model) stats = self.get_compression_stats() avg_err = ( (sum(self.layer_errors.values()) / len(self.layer_errors)) if self.layer_errors else 0.0 ) stats.update( { "compressed_layers": self.compressed_layers, "compressed_layer_count": len(self.compressed_layers), "fallback_layers": self.fallback_layers, "fallback_layer_count": len(self.fallback_layers), "skipped_layers": self.skipped_layers, "skipped_layer_count": len(self.skipped_layers), "num_compressed": len(self.compressed_layers), "num_fallback": len(self.fallback_layers), "num_skipped": len(self.skipped_layers), "layer_errors": self.layer_errors, "avg_reconstruction_error": avg_err, "grecodec_rank": self.rank, "grecodec_max_iter": self.max_iter, "lora_r": self.lora_r, "lora_alpha": self.lora_alpha, "effective_parameter_ratio": round(effective_ratio, 4), "forward_check_success": forward_check_success, } ) logger.info( "Compressor grecodec finished: %d compressed, %d fallback, avg_err=%.4f, max_err_threshold=%.4f", len(self.compressed_layers), len(self.fallback_layers), avg_err, self.max_error, ) return self.model, statsGreCoDeC Compressor. Decomposes linear layers using coordinate descent low-rank factorization.
Ancestors
Methods
def compress(self, model: nn.Module | None = None) ‑> tuple[torch.nn.modules.module.Module, dict[str, typing.Any]]-
Expand source code
def compress(self, model: nn.Module | None = None) -> tuple[nn.Module, dict[str, Any]]: """Compress linear layers using GreCoDeC decomposition.""" if model is not None: self.model = model self._original_params = self._count_params(self.model) self.compressed_layers = [] self.fallback_layers = [] self.skipped_layers = [] self.layer_errors = {} seen_weights: set[int] = set() total_orig_params = 0 total_comp_params = 0 # Freeze skipped non-target layers if target_attention_only is enabled if self.target_attention_only: for name, module in self.model.named_modules(): if any(part in name.split(".") for part in ["lora_A", "lora_B", "base_layer"]): continue target_mod = getattr(module, "base_layer", module) cls_name = target_mod.__class__.__name__.lower() is_linear = isinstance(target_mod, nn.Linear) is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") if (is_linear or is_conv1d) and not self._is_target_module(name, module): for p in module.parameters(): p.requires_grad = False self.skipped_layers.append(name) for name, module in list(self.model.named_modules()): if not self._is_target_module(name, module): continue target_mod = getattr(module, "base_layer", module) if not hasattr(target_mod, "weight") or target_mod.weight is None: continue w_id = id(target_mod.weight) if w_id in seen_weights: self.skipped_layers.append(name) continue seen_weights.add(w_id) cls_name = target_mod.__class__.__name__.lower() is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") raw_w = target_mod.weight.data W_target = raw_w.t().contiguous().float() if is_conv1d else raw_w.float() out_f, in_f = W_target.shape[0], W_target.shape[1] orig_params = in_f * out_f total_orig_params += orig_params max_rank = min(in_f, out_f) bound_r = (in_f * out_f) // (in_f + out_f) if max_rank < 16 or bound_r < 4: self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params continue try: grecodec_layer = GreCoDeCLinear.from_linear( module, rank=self.rank, max_iter=self.max_iter, lora_r=self.lora_r, lora_alpha=self.lora_alpha, ) W_comp = grecodec_layer.get_weight().float() W_norm = torch.norm(W_target, p="fro").clamp_min(1e-8) err = float(torch.norm(W_target - W_comp, p="fro").item() / W_norm) self.layer_errors[name] = err if err > self.max_error: if self.fallback_to_original: self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params continue replace_module(self.model, name, grecodec_layer) self.compressed_layers.append(name) comp_params = grecodec_layer.rank * (in_f + out_f) if grecodec_layer.lora_r > 0: comp_params += grecodec_layer.lora_r * (in_f + out_f) total_comp_params += comp_params except Exception as e: logger.debug("GreCoDeC error on %s: %s", name, e) self.fallback_layers.append(name) for p in module.parameters(): p.requires_grad = False total_comp_params += orig_params # Post-compression forward sanity check forward_check_success = True try: with torch.no_grad(): first_p = next(self.model.parameters(), None) dev = first_p.device if first_p is not None else torch.device("cpu") dummy = torch.randint(0, 100, (1, 8), device=dev) try: self.model(dummy) except Exception: first_linear = next( ( m for m in self.model.modules() if isinstance(m, (nn.Linear, GreCoDeCLinear)) ), None, ) in_dim = getattr(first_linear, "in_features", 8) self.model(torch.randn(1, 8, in_dim, device=dev)) except Exception: forward_check_success = False effective_ratio = total_comp_params / total_orig_params if total_orig_params > 0 else 1.0 self._compressed_params = self._count_params(self.model) stats = self.get_compression_stats() avg_err = ( (sum(self.layer_errors.values()) / len(self.layer_errors)) if self.layer_errors else 0.0 ) stats.update( { "compressed_layers": self.compressed_layers, "compressed_layer_count": len(self.compressed_layers), "fallback_layers": self.fallback_layers, "fallback_layer_count": len(self.fallback_layers), "skipped_layers": self.skipped_layers, "skipped_layer_count": len(self.skipped_layers), "num_compressed": len(self.compressed_layers), "num_fallback": len(self.fallback_layers), "num_skipped": len(self.skipped_layers), "layer_errors": self.layer_errors, "avg_reconstruction_error": avg_err, "grecodec_rank": self.rank, "grecodec_max_iter": self.max_iter, "lora_r": self.lora_r, "lora_alpha": self.lora_alpha, "effective_parameter_ratio": round(effective_ratio, 4), "forward_check_success": forward_check_success, } ) logger.info( "Compressor grecodec finished: %d compressed, %d fallback, avg_err=%.4f, max_err_threshold=%.4f", len(self.compressed_layers), len(self.fallback_layers), avg_err, self.max_error, ) return self.model, statsCompress linear layers using GreCoDeC decomposition.
Inherited members
class GreCoDeCLinear (in_features: int,
out_features: int,
rank: int = 16,
bias: bool = True,
lora_r: int = 0,
lora_alpha: float = 1.0,
dtype: torch.dtype | None = None,
device: torch.device | None = None)-
Expand source code
class GreCoDeCLinear(nn.Module): is_custom_attention: bool = True """ Greedy Coordinate Descent Factorized Linear Layer with optional LoRA adapter. W ≈ U @ V - U in R^{out_features x rank} - V in R^{rank x in_features} Forward pass: y = ((x @ V^T) @ U^T) + bias + (x @ lora_A^T @ lora_B^T) * (alpha / r) """ def __init__( self, in_features: int, out_features: int, rank: int = 16, bias: bool = True, lora_r: int = 0, lora_alpha: float = 1.0, dtype: torch.dtype | None = None, device: torch.device | None = None, ) -> None: super().__init__() self.in_features = in_features self.out_features = out_features self.rank = max(1, min(rank, in_features, out_features)) self.lora_r = int(lora_r) self.lora_alpha = float(lora_alpha) self.scaling = (lora_alpha / lora_r) if lora_r > 0 else 1.0 requires_grad = not (self.lora_r > 0) self.U = nn.Parameter( torch.empty(out_features, self.rank, dtype=dtype, device=device), requires_grad=requires_grad, ) self.V = nn.Parameter( torch.empty(self.rank, in_features, dtype=dtype, device=device), requires_grad=requires_grad, ) bound = 1.0 / math.sqrt(max(1, in_features)) nn.init.uniform_(self.U, -bound, bound) nn.init.uniform_(self.V, -bound, bound) if bias: self.bias = nn.Parameter( torch.zeros(out_features, dtype=dtype, device=device), 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, in_features), dtype=torch.float32, device=device) ) self.lora_B = nn.Parameter( torch.zeros((out_features, self.lora_r), dtype=torch.float32, device=device) ) nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) else: self.register_parameter("lora_A", None) self.register_parameter("lora_B", None) @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: """ Sub-quadratic forward execution. y = ((x @ V^T) @ U^T) + bias + LoRA """ orig_shape = x.shape x_flat = x.reshape(-1, self.in_features) x_v = F.linear(x_flat, self.V) out = F.linear(x_v, self.U, self.bias) if self.lora_r > 0 and self.lora_A is not None and self.lora_B is not None: out = out + self.scaling * F.linear( F.linear(x_flat.float(), self.lora_A), self.lora_B ).to(out.dtype) out = out return out.view(*orig_shape[:-1], self.out_features) def get_weight(self) -> torch.Tensor: """Materialize full reconstructed weight matrix W = U @ V.""" return torch.matmul(self.U, self.V) @classmethod def from_linear( cls, linear: nn.Linear | Any, rank: int = 16, max_iter: int = 15, lora_r: int = 0, lora_alpha: float = 1.0, ) -> GreCoDeCLinear: """Construct GreCoDeCLinear by greedy coordinate descent low-rank factorization.""" target_mod = getattr(linear, "base_layer", linear) cls_name = target_mod.__class__.__name__.lower() is_conv1d = "conv1d" in cls_name or hasattr(target_mod, "nf") raw_w = target_mod.weight.data if is_conv1d: in_f = raw_w.shape[0] out_f = raw_w.shape[1] W_float = raw_w.t().contiguous().float() # [out_features, in_features] else: out_f = raw_w.shape[0] in_f = raw_w.shape[1] W_float = raw_w.float() bias = ( target_mod.bias.data if getattr(target_mod, "bias", None) is not None and target_mod.bias is not None else None ) r = max(1, min(rank, out_f, in_f)) # Check existing LoRA parameters existing_lora_A = getattr(linear, "lora_A", None) existing_lora_B = getattr(linear, "lora_B", None) # Coordinate descent low-rank approximation with SVD initialization fallback U_init, S_init, Vt_init = torch.linalg.svd(W_float, full_matrices=False) sqrt_S = torch.sqrt(torch.clamp(S_init[:r], min=1e-12)) U_mat = (U_init[:, :r] * sqrt_S.unsqueeze(0)).clone() V_mat = (sqrt_S.unsqueeze(1) * Vt_init[:r, :]).clone() # Iterative coordinate descent refinement for _ in range(max_iter): # Update U given V: min_U ||W - U V||_F^2 => U = W V^T (V V^T)^{-1} VVt = torch.matmul(V_mat, V_mat.t().contiguous()) VVt_reg = VVt + 1e-6 * torch.eye(r, device=VVt.device, dtype=VVt.dtype) try: WV_t = torch.matmul(W_float, V_mat.t().contiguous()) U_mat = torch.linalg.solve(VVt_reg, WV_t.t().contiguous()).t().contiguous() except Exception: break # Update V given U: min_V ||W - U V||_F^2 => V = (U^T U)^{-1} U^T W UtU = torch.matmul(U_mat.t().contiguous(), U_mat) UtU_reg = UtU + 1e-6 * torch.eye(r, device=UtU.device, dtype=UtU.dtype) try: UtW = torch.matmul(U_mat.t().contiguous(), W_float) V_mat = torch.linalg.solve(UtU_reg, UtW) except Exception: break # Symmetrical norm balancing U_norm = torch.norm(U_mat, p=2, dim=0, keepdim=True).clamp_min(1e-8) V_norm = torch.norm(V_mat, p=2, dim=1, keepdim=True).clamp_min(1e-8) geom = torch.sqrt(U_norm.squeeze(0) * V_norm.squeeze(1)) U_data = U_mat * (geom / U_norm.squeeze(0)).unsqueeze(0) V_data = V_mat * (geom / V_norm.squeeze(1)).unsqueeze(1) effective_lora_r = lora_r if existing_lora_A is not None and hasattr(existing_lora_A, "weight"): effective_lora_r = existing_lora_A.weight.shape[0] grecodec_layer = cls( in_features=in_f, out_features=out_f, rank=r, bias=bias is not None, lora_r=effective_lora_r, lora_alpha=lora_alpha, dtype=raw_w.dtype, device=raw_w.device, ) grecodec_layer.U.data.copy_(U_data.to(dtype=raw_w.dtype, device=raw_w.device)) grecodec_layer.V.data.copy_(V_data.to(dtype=raw_w.dtype, device=raw_w.device)) if bias is not None and grecodec_layer.bias is not None: grecodec_layer.bias.data.copy_(bias.to(dtype=raw_w.dtype, device=raw_w.device)) if ( existing_lora_A is not None and existing_lora_B is not None and grecodec_layer.lora_A is not None ): w_A = getattr(existing_lora_A, "weight", existing_lora_A) w_B = getattr(existing_lora_B, "weight", existing_lora_B) if hasattr(w_A, "data") and hasattr(w_B, "data"): grecodec_layer.lora_A.data.copy_(w_A.data) grecodec_layer.lora_B.data.copy_(w_B.data) grecodec_layer.lora_A.requires_grad = True grecodec_layer.lora_B.requires_grad = True return grecodec_layerBase 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-
Greedy Coordinate Descent Factorized Linear Layer with optional LoRA adapter. W ≈ U @ V - U in R^{out_features x rank} - V in R^{rank x in_features}
Forward pass: y = ((x @ V^T) @ U^T) + bias + (x @ lora_A^T @ lora_B^T) * (alpha / r)
Static methods
def from_linear(linear: nn.Linear | Any,
rank: int = 16,
max_iter: int = 15,
lora_r: int = 0,
lora_alpha: float = 1.0) ‑> GreCoDeCLinear-
Construct GreCoDeCLinear by greedy coordinate descent low-rank factorization.
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: """ Sub-quadratic forward execution. y = ((x @ V^T) @ U^T) + bias + LoRA """ orig_shape = x.shape x_flat = x.reshape(-1, self.in_features) x_v = F.linear(x_flat, self.V) out = F.linear(x_v, self.U, self.bias) if self.lora_r > 0 and self.lora_A is not None and self.lora_B is not None: out = out + self.scaling * F.linear( F.linear(x_flat.float(), self.lora_A), self.lora_B ).to(out.dtype) out = out return out.view(*orig_shape[:-1], self.out_features)Sub-quadratic forward execution. y = ((x @ V^T) @ U^T) + bias + LoRA
def get_weight(self) ‑> torch.Tensor-
Expand source code
def get_weight(self) -> torch.Tensor: """Materialize full reconstructed weight matrix W = U @ V.""" return torch.matmul(self.U, self.V)Materialize full reconstructed weight matrix W = U @ V.