Module neuralops.attentions.full_attention
Standard Multi-Head Scaled Dot-Product Attention (Vaswani et al., 2017).
Computes pairwise token interactions across multiple representation subspaces with frozen base weights, trainable LoRA residual adapters, multi-architecture weight transfer, GQA, RoPE, and causal decoding.
Functions
def apply_rotary_pos_emb(q: torch.Tensor,
k: torch.Tensor,
cos: torch.Tensor,
sin: torch.Tensor,
position_ids: torch.Tensor | None = None) ‑> tuple[torch.Tensor, torch.Tensor]-
Expand source code
def apply_rotary_pos_emb( q: torch.Tensor, k: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor, position_ids: torch.Tensor | None = None, ) -> tuple[torch.Tensor, torch.Tensor]: if cos is None or sin is None: return q, k if cos.dim() == 2: cos = cos.unsqueeze(0).unsqueeze(0) sin = sin.unsqueeze(0).unsqueeze(0) elif cos.dim() == 3: cos = cos.unsqueeze(1) sin = sin.unsqueeze(1) while cos.dim() < q.dim(): cos = cos.unsqueeze(0) sin = sin.unsqueeze(0) if cos.shape[-2] != q.shape[-2]: cos = cos[..., : q.shape[-2], :] sin = sin[..., : q.shape[-2], :] rotary_dim = cos.shape[-1] head_dim = q.shape[-1] if rotary_dim <= head_dim: q_rot, q_pass = q[..., :rotary_dim], q[..., rotary_dim:] k_rot, k_pass = k[..., :rotary_dim], k[..., rotary_dim:] q_rot_embed = (q_rot * cos.to(q_rot.dtype)) + (_rotate_half(q_rot) * sin.to(q_rot.dtype)) k_rot_embed = (k_rot * cos.to(k_rot.dtype)) + (_rotate_half(k_rot) * sin.to(k_rot.dtype)) q = torch.cat([q_rot_embed, q_pass], dim=-1) if q_pass.numel() > 0 else q_rot_embed k = torch.cat([k_rot_embed, k_pass], dim=-1) if k_pass.numel() > 0 else k_rot_embed else: cos_cut, sin_cut = cos[..., :head_dim], sin[..., :head_dim] q = (q * cos_cut.to(q.dtype)) + (_rotate_half(q) * sin_cut.to(q.dtype)) k = (k * cos_cut.to(k.dtype)) + (_rotate_half(k) * sin_cut.to(k.dtype)) return q, k def inject_full_attention(layer: Any = None,
model: Any = None,
model_config: Any = None,
comp_config: Any = None,
layer_idx: int = 0,
total_layers: int = 12,
old_attn: nn.Module | None = None,
**kwargs: Any)-
Expand source code
@ATTENTION_REGISTRY.register("full_attention_inject") def inject_full_attention( layer: Any = None, model: Any = None, model_config: Any = None, comp_config: Any = None, layer_idx: int = 0, total_layers: int = 12, old_attn: nn.Module | None = None, **kwargs: Any, ): target = layer if layer is not None else model if target is None: target = kwargs.get("model", kwargs.get("layer")) if target is None: return None cfg = model_config if model_config is not None else getattr(target, "config", None) # ---- Whole-model traversal ---- layer_list = None if hasattr(target, "distilbert") and hasattr(target.distilbert, "transformer"): layer_list = target.distilbert.transformer.layer elif hasattr(target, "roberta") and hasattr(target.roberta, "encoder"): layer_list = target.roberta.encoder.layer elif hasattr(target, "transformer") and hasattr(target.transformer, "h"): layer_list = target.transformer.h elif hasattr(target, "h") and isinstance(target.h, (torch.nn.ModuleList, list)): layer_list = target.h elif hasattr(target, "model") and hasattr(target.model, "layers"): layer_list = target.model.layers elif hasattr(target, "encoder") and hasattr(target.encoder, "layer"): layer_list = target.encoder.layer elif ( hasattr(target, "bert") and hasattr(target.bert, "encoder") and hasattr(target.bert.encoder, "layer") ): layer_list = target.bert.encoder.layer elif hasattr(target, "layers") and isinstance(target.layers, (torch.nn.ModuleList, list)): layer_list = target.layers if layer_list is not None: num_injected = 0 tot = len(layer_list) for idx, lyr in enumerate(layer_list): res = inject_full_attention( layer=lyr, model_config=cfg, comp_config=( comp_config if "comp_config" in locals() or "comp_config" in kwargs else None ), layer_idx=idx, total_layers=tot, **kwargs, ) if res is not None: num_injected += 1 return target if num_injected > 0 else None # ---- Single-layer injection ---- layer = target # FIX-3: single initialization (removed duplicate) if model_config is None: model_config = type("Cfg", (), {"hidden_size": 64, "num_heads": 4, "rank": 32})() # ---- FIX-1: Discover source attention across all architecture variants ---- src_attn = old_attn if src_attn is None: for attr in ("attn", "attention", "self_attn", "self"): candidate = getattr(layer, attr, None) if candidate is None: continue # FIX-4: idempotency guard if isinstance(candidate, FullAttention): logger.debug( "inject_full_attention: %s.%s is already FullAttention, skipping", type(layer).__name__, attr, ) return None # RoBERTa/BERT nested: layer.attention.self has .query if hasattr(candidate, "self") and hasattr(candidate.self, "query"): src_attn = candidate.self else: src_attn = candidate break if src_attn is None: # FIX-5: explicit error logging logger.warning( "inject_full_attention: no source attention found on layer %s " "(checked: attn, attention, self_attn, self)", type(layer).__name__, ) return None # ---- Build FullAttention with copied weights ---- try: new_attn = FullAttention( config=model_config, pretrained_attn=src_attn, ) except Exception as e: logger.error("FullAttention construction failed: %s", e) return None # ---- FIX-2: Replace attention across all architecture variants ---- replaced = False if hasattr(layer, "attn"): # GPT-2, GPT-Neo, GPT-J layer.attn = new_attn replaced = True elif hasattr(layer, "attention"): if hasattr(layer.attention, "self") and hasattr(layer.attention.self, "query"): # RoBERTa / BERT nested encoder layer.attention.self = new_attn else: layer.attention = new_attn replaced = True elif hasattr(layer, "self_attn"): layer.self_attn = new_attn replaced = True elif hasattr(layer, "self"): layer.self = new_attn replaced = True if not replaced: logger.error( "inject_full_attention: no writable attention attribute on layer %s", type(layer).__name__, ) return None return new_attn
Classes
class FullAttention (config: Any,
rank: int | None = None,
lora_rank: int = 16,
pretrained_attn: nn.Module | None = None,
**kwargs: Any)-
Expand source code
@ATTENTION_REGISTRY.register( "full", supports_causal=True, supports_bidirectional=True, supports_gqa=True ) @ATTENTION_REGISTRY.register( "full_attention", supports_causal=True, supports_bidirectional=True, supports_gqa=True, ) class FullAttention(BaseAttention): supports_causal: bool = True supports_bidirectional: bool = True supports_gqa: bool = True supports_rope: bool = True is_custom_attention: bool = True def __init__( self, config: Any, rank: int | None = None, lora_rank: int = 16, pretrained_attn: nn.Module | None = None, **kwargs: Any, ) -> None: super().__init__(config, rank) self.adapter = ModelAdapter(config) self.hidden_size: int = self.adapter.hidden_size or kwargs.get( "hidden_size", kwargs.get("d_model", 768) ) self.num_heads: int = self.adapter.num_heads or kwargs.get( "num_heads", kwargs.get("num_attention_heads", 12) ) self.num_kv_heads: int = self.adapter.num_key_value_heads or kwargs.get( "num_kv_heads", kwargs.get("num_key_value_heads", self.num_heads) ) self.head_dim: int = self.adapter.head_dim or (self.hidden_size // max(1, self.num_heads)) self.all_head_size: int = self.num_heads * self.head_dim self.kv_head_size: int = self.num_kv_heads * self.head_dim self.kv_repeat: int = max(1, self.num_heads // max(1, self.num_kv_heads)) self.is_decoder: bool = getattr(config, "is_decoder", False) self.is_nested_encoder: bool = False self.lora_rank = getattr(config, "lora_rank", lora_rank) self.query = LoRALinear( self.hidden_size, self.all_head_size, bias=True, lora_rank=self.lora_rank, freeze_base=True, ) self.key = LoRALinear( self.hidden_size, self.kv_head_size, bias=True, lora_rank=self.lora_rank, freeze_base=True, ) self.value = LoRALinear( self.hidden_size, self.kv_head_size, bias=True, lora_rank=self.lora_rank, freeze_base=True, ) self.out_proj = LoRALinear( self.all_head_size, self.hidden_size, bias=True, lora_rank=self.lora_rank, freeze_base=True, ) dropout_p = ( getattr( config, "dropout", getattr( config, "hidden_dropout_prob", getattr(config, "attention_probs_dropout_prob", 0.1), ), ) if config else 0.1 ) self.dropout: nn.Dropout = nn.Dropout(dropout_p) self.rotary_emb = getattr(pretrained_attn, "rotary_emb", None) if pretrained_attn is not None: self.copy_weights(pretrained_attn) @property def q_proj(self) -> LoRALinear: return self.query @property def k_proj(self) -> LoRALinear: return self.key @property def v_proj(self) -> LoRALinear: return self.value @property def q_lin(self) -> LoRALinear: return self.query @property def k_lin(self) -> LoRALinear: return self.key @property def v_lin(self) -> LoRALinear: return self.value @property def out_lin(self) -> LoRALinear: return self.out_proj @property def dense(self) -> LoRALinear: return self.out_proj @property def o_proj(self) -> LoRALinear: return self.out_proj @property def c_proj(self) -> LoRALinear: return self.out_proj def copy_weights(self, src: Any) -> None: from neuralops.core.adapters import UniversalAdapter extracted: dict[str, tuple[torch.Tensor | None, torch.Tensor | None]] = { "q": (None, None), "k": (None, None), "v": (None, None), "o": (None, None), } # 1. UniversalAdapter extraction for proj in ["q", "k", "v", "o"]: try: w, b = UniversalAdapter.get_weight_bias(src, proj) if w is not None: extracted[proj] = (w, b) except Exception as e: logger.debug("UniversalAdapter fallback failed: %s", e) # 2. GPT-2 Fused c_attn Conv1D handling if hasattr(src, "c_attn") and ( extracted["q"][0] is None or extracted["k"][0] is None or extracted["v"][0] is None ): weight = src.c_attn.weight.data bias = getattr(src.c_attn, "bias", None) bias_data = bias.data if bias is not None else None if weight.dim() == 2 and weight.shape[1] == 3 * self.hidden_size: weight = weight.t().contiguous() split_size = weight.shape[0] // 3 q_w = weight[:split_size, :] k_w = weight[split_size : 2 * split_size, :] v_w = weight[2 * split_size :, :] q_b = bias_data[:split_size] if bias_data is not None else None k_b = bias_data[split_size : 2 * split_size] if bias_data is not None else None v_b = bias_data[2 * split_size :] if bias_data is not None else None extracted["q"] = (q_w, q_b) extracted["k"] = (k_w, k_b) extracted["v"] = (v_w, v_b) if hasattr(src, "c_proj") and extracted["o"][0] is None: w_o = src.c_proj.weight.data if ( w_o.dim() == 2 and w_o.shape[0] != self.hidden_size and w_o.shape[1] == self.hidden_size ): w_o = w_o.t().contiguous() b_o = getattr(src.c_proj, "bias", None) extracted["o"] = (w_o, b_o.data if b_o is not None else None) # 3. RoBERTa / BERT Nested Encoder handling if hasattr(src, "self") and ( extracted["q"][0] is None or extracted["k"][0] is None or extracted["v"][0] is None ): s = src.self if hasattr(s, "query"): extracted["q"] = (s.query.weight.data, getattr(s.query, "bias", None)) if hasattr(s, "key"): extracted["k"] = (s.key.weight.data, getattr(s.key, "bias", None)) if hasattr(s, "value"): extracted["v"] = (s.value.weight.data, getattr(s.value, "bias", None)) if hasattr(src, "output") and hasattr(src.output, "dense") and extracted["o"][0] is None: dense = src.output.dense extracted["o"] = (dense.weight.data, getattr(dense, "bias", None)) # 4. Modern Decoders (Qwen3.5, LLaMA, Mistral) & DistilBERT named_map = { "q": ["q_proj", "q_lin", "query"], "k": ["k_proj", "k_lin", "key"], "v": ["v_proj", "v_lin", "value"], "o": ["o_proj", "out_proj", "out_lin", "dense"], } for proj, names in named_map.items(): if extracted[proj][0] is None: for name in names: if hasattr(src, name): layer = getattr(src, name) if hasattr(layer, "weight"): extracted[proj] = ( layer.weight.data, getattr(layer, "bias", None), ) break # Detect nested encoder architecture cls_name = src.__class__.__name__ has_out_proj = any( hasattr(src, attr) for attr in ( "dense", "out_proj", "c_proj", "out_lin", "o_proj", "wo", "o", "proj", ) ) if ( ( hasattr(src, "self") and hasattr(src.self, "query") and hasattr(src, "output") and hasattr(src.output, "dense") ) or ( cls_name in ("BertSelfAttention", "RobertaSelfAttention") or (cls_name.endswith("SelfAttention") and not has_out_proj) ) and not has_out_proj ): self.is_nested_encoder = True else: self.is_nested_encoder = False # Check required weights missing = [k for k in ["q", "k", "v"] if extracted[k][0] is None] if not self.is_nested_encoder and extracted["o"][0] is None: missing.append("o") if missing: logger.warning( "FullAttention copy_weights warning. Proceeding with initialized weights." ) # Frobenius norm validation for proj_key in ["q", "k", "v"]: w = extracted[proj_key][0] if w is not None: norm = torch.linalg.norm(w.float()) if norm < 1e-4: logger.warning( "FullAttention extracted weight for %s has near-zero Frobenius norm (%.6f)", proj_key, norm.item(), ) if extracted["o"][0] is not None: norm_o = torch.linalg.norm(extracted["o"][0].float()) if norm_o < 1e-4: logger.warning( "FullAttention extracted weight for output projection has near-zero Frobenius norm (%.6f)", norm_o.item(), ) def _orient(w: torch.Tensor, in_dim: int) -> torch.Tensor: if w.dim() == 2 and w.shape[1] != in_dim and w.shape[0] == in_dim: return w.t().contiguous() return w # Copy into projection modules with torch.no_grad(): for proj_key, target in [ ("q", self.query), ("k", self.key), ("v", self.value), ("o", self.out_proj), ]: w, b = extracted[proj_key] if w is not None: in_dim = self.all_head_size if proj_key == "o" else self.hidden_size w_oriented = _orient(w, in_dim) target_w = target.weight out_d, in_d = target_w.shape src_out, src_in = w_oriented.shape target_w.data[: min(out_d, src_out), : min(in_d, src_in)].copy_( w_oriented[: min(out_d, src_out), : min(in_d, src_in)] ) if b is not None and target.bias is not None: b_val = b.data if isinstance(b, nn.Parameter) else b target.bias.data[: min(len(target.bias), len(b_val))].copy_( b_val[: min(len(target.bias), len(b_val))] ) elif proj_key == "o" and self.is_nested_encoder: min_d = min(target.weight.shape[0], target.weight.shape[1]) target.weight.data.zero_() target.weight.data[:min_d, :min_d].fill_diagonal_(1.0) if target.bias is not None: target.bias.data.zero_() target.freeze_base_weights() if hasattr(src, "is_decoder"): self.is_decoder = bool(src.is_decoder) def forward( self, hidden_states: torch.Tensor, attention_mask: torch.Tensor | None = None, head_mask: torch.Tensor | None = None, encoder_hidden_states: torch.Tensor | None = None, encoder_attention_mask: torch.Tensor | None = None, past_key_value: Any | None = None, output_attentions: bool | None = False, output_hidden_states: bool | None = False, return_dict: bool | None = True, *args: Any, **kwargs: Any, ) -> tuple[torch.Tensor, torch.Tensor | None]: key_value_states = kwargs.get("key_value_states", encoder_hidden_states) is_cross_attention = key_value_states is not None kv_input = key_value_states if is_cross_attention else hidden_states B, N_q, D = hidden_states.shape N_k = kv_input.shape[1] H, d = self.num_heads, self.head_dim device = hidden_states.device dtype = hidden_states.dtype Q = self.query(hidden_states).view(B, N_q, H, d).transpose(1, 2).contiguous() K = self.key(kv_input).view(B, N_k, self.num_kv_heads, d).transpose(1, 2).contiguous() V = self.value(kv_input).view(B, N_k, self.num_kv_heads, d).transpose(1, 2).contiguous() # Apply RoPE position_embeddings = kwargs.get("position_embeddings") if ( position_embeddings is not None and isinstance(position_embeddings, (tuple, list)) and len(position_embeddings) >= 2 ): cos, sin = position_embeddings[0], position_embeddings[1] Q, K = apply_rotary_pos_emb(Q, K, cos, sin, kwargs.get("position_ids")) # GQA repeat if self.kv_repeat > 1: K = repeat_kv(K, self.kv_repeat) V = repeat_kv(V, self.kv_repeat) scale = d**-0.5 scores = torch.matmul(Q, K.transpose(-2, -1).contiguous()) * scale mask = attention_mask if attention_mask is not None else kwargs.get("mask") if mask is not None: if mask.dim() == 4: scores = scores + mask.to(scores.dtype) else: valid_m = get_token_valid_mask(mask, B, N_k, device) if valid_m is not None: scores = scores.masked_fill((~valid_m).unsqueeze(1).unsqueeze(2), -10000.0) # Explicit Causal Masking for decoders is_decoder = ( self.is_decoder or getattr(self.config, "is_decoder", False) or getattr(self.config, "model_type", "") in ("gpt2", "qwen", "qwen2", "qwen3_5", "llama") ) if is_decoder: causal_tril = ( torch.tril(torch.ones((N_q, N_k), device=device, dtype=torch.bool)) .unsqueeze(0) .unsqueeze(0) ) scores = scores.masked_fill(~causal_tril, -10000.0) attn = torch.nan_to_num(F.softmax(scores.float(), dim=-1), nan=0.0).to(dtype) attn = self.dropout(attn) if head_mask is not None: if head_mask.dim() == 1: head_mask = head_mask.view(1, -1, 1, 1) elif head_mask.dim() == 2: head_mask = head_mask.unsqueeze(1).unsqueeze(-1) elif head_mask.dim() == 3: head_mask = head_mask.unsqueeze(-1) attn = attn * head_mask context = torch.matmul(attn, V) context = context.transpose(1, 2).contiguous().view(B, N_q, self.all_head_size) if self.is_nested_encoder: output = context else: output = self.out_proj(context) return output, (attn if output_attentions else None)Every custom attention module must inherit from this.
Initialize internal Module state, shared by both nn.Module and ScriptModule.
Ancestors
- BaseAttention
- torch.nn.modules.module.Module
Class variables
var is_custom_attention : bool-
The type of the None singleton.
var supports_bidirectional : bool-
The type of the None singleton.
var supports_causal : bool-
The type of the None singleton.
var supports_gqa : bool-
The type of the None singleton.
var supports_rope : bool-
The type of the None singleton.
Instance variables
prop c_proj : LoRALinear-
Expand source code
@property def c_proj(self) -> LoRALinear: return self.out_proj prop dense : LoRALinear-
Expand source code
@property def dense(self) -> LoRALinear: return self.out_proj prop k_lin : LoRALinear-
Expand source code
@property def k_lin(self) -> LoRALinear: return self.key prop k_proj : LoRALinear-
Expand source code
@property def k_proj(self) -> LoRALinear: return self.key prop o_proj : LoRALinear-
Expand source code
@property def o_proj(self) -> LoRALinear: return self.out_proj prop out_lin : LoRALinear-
Expand source code
@property def out_lin(self) -> LoRALinear: return self.out_proj prop q_lin : LoRALinear-
Expand source code
@property def q_lin(self) -> LoRALinear: return self.query prop q_proj : LoRALinear-
Expand source code
@property def q_proj(self) -> LoRALinear: return self.query prop v_lin : LoRALinear-
Expand source code
@property def v_lin(self) -> LoRALinear: return self.value prop v_proj : LoRALinear-
Expand source code
@property def v_proj(self) -> LoRALinear: return self.value
Inherited members
class LoRALinear (in_features: int,
out_features: int,
bias: bool = True,
lora_rank: int = 16,
lora_alpha: float = 32.0,
freeze_base: bool = False,
device: torch.device | None = None,
dtype: torch.dtype | None = None)-
Expand source code
class LoRALinear(nn.Module): def __init__( self, in_features: int, out_features: int, bias: bool = True, lora_rank: int = 16, lora_alpha: float = 32.0, freeze_base: bool = False, device: torch.device | None = None, dtype: torch.dtype | None = None, ) -> None: super().__init__() factory_kwargs = {"device": device, "dtype": dtype} self.in_features = in_features self.out_features = out_features self.lora_rank = lora_rank self.lora_alpha = lora_alpha self.lora_scaling = (lora_alpha / lora_rank) if lora_rank > 0 else 1.0 self.freeze_base = freeze_base self.weight = nn.Parameter(torch.empty((out_features, in_features), **factory_kwargs)) if bias: self.bias = nn.Parameter(torch.empty(out_features, **factory_kwargs)) else: self.register_parameter("bias", None) if self.lora_rank > 0: self.lora_A = nn.Parameter(torch.empty((lora_rank, in_features), **factory_kwargs)) self.lora_B = nn.Parameter(torch.empty((out_features, lora_rank), **factory_kwargs)) else: self.register_parameter("lora_A", None) self.register_parameter("lora_B", None) self.reset_parameters() if freeze_base: self.freeze_base_weights() def reset_parameters(self) -> None: nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) if self.bias is not None: fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight) bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0 nn.init.uniform_(self.bias, -bound, bound) if self.lora_rank > 0 and self.lora_A is not None: nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) self.lora_A.requires_grad = True if self.lora_rank > 0 and self.lora_B is not None: nn.init.zeros_(self.lora_B) self.lora_B.requires_grad = True def freeze_base_weights(self) -> None: self.weight.requires_grad = False if self.bias is not None: self.bias.requires_grad = False if self.lora_rank > 0: if self.lora_A is not None: self.lora_A.requires_grad = True if self.lora_B is not None: self.lora_B.requires_grad = True def forward(self, x: torch.Tensor) -> torch.Tensor: w = self.weight.to(x.dtype) if self.weight.dtype != x.dtype else self.weight b = ( self.bias.to(x.dtype) if (self.bias is not None and self.bias.dtype != x.dtype) else self.bias ) out = F.linear(x, w, b) if self.lora_rank > 0 and self.lora_A is not None and self.lora_B is not None: a = self.lora_A.to(x.dtype) if self.lora_A.dtype != x.dtype else self.lora_A bb = self.lora_B.to(x.dtype) if self.lora_B.dtype != x.dtype else self.lora_B out = out + F.linear(F.linear(x, a), bb) * self.lora_scaling 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
Methods
def forward(self, x: torch.Tensor) ‑> torch.Tensor-
Expand source code
def forward(self, x: torch.Tensor) -> torch.Tensor: w = self.weight.to(x.dtype) if self.weight.dtype != x.dtype else self.weight b = ( self.bias.to(x.dtype) if (self.bias is not None and self.bias.dtype != x.dtype) else self.bias ) out = F.linear(x, w, b) if self.lora_rank > 0 and self.lora_A is not None and self.lora_B is not None: a = self.lora_A.to(x.dtype) if self.lora_A.dtype != x.dtype else self.lora_A bb = self.lora_B.to(x.dtype) if self.lora_B.dtype != x.dtype else self.lora_B out = out + F.linear(F.linear(x, a), bb) * self.lora_scaling return outDefine 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 freeze_base_weights(self) ‑> None-
Expand source code
def freeze_base_weights(self) -> None: self.weight.requires_grad = False if self.bias is not None: self.bias.requires_grad = False if self.lora_rank > 0: if self.lora_A is not None: self.lora_A.requires_grad = True if self.lora_B is not None: self.lora_B.requires_grad = True def reset_parameters(self) ‑> None-
Expand source code
def reset_parameters(self) -> None: nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5)) if self.bias is not None: fan_in, _ = nn.init._calculate_fan_in_and_fan_out(self.weight) bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0 nn.init.uniform_(self.bias, -bound, bound) if self.lora_rank > 0 and self.lora_A is not None: nn.init.kaiming_uniform_(self.lora_A, a=math.sqrt(5)) self.lora_A.requires_grad = True if self.lora_rank > 0 and self.lora_B is not None: nn.init.zeros_(self.lora_B) self.lora_B.requires_grad = True