phammminhhieu/SHINE_LR_V3 / models /parametric_encoder.py
phammminhhieu's picture
download
raw
4.76 kB
# models/parametric_encoder.py
import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple
class ParametricEncoder(nn.Module):
"""
Compresses LoRA weight matrices into compact feature vectors using 1D-CNN.
Captures local spatial patterns in weight matrices for efficient processing.
Architecture:
Input: (B, N, r, d) -> Reshape to (B*N, r, d)
-> Conv1D + BatchNorm1D layers -> AdaptiveAvgPool1D -> Linear projection
Output: (B, N, s_param)
"""
def __init__(
self,
rank: int,
hidden_dim: int,
state_dim: int = 256,
num_layers: int = 8
):
super().__init__()
self.rank = rank
self.hidden_dim = hidden_dim
self.state_dim = state_dim
self.num_layers = num_layers
# 1D-CNN architecture with BatchNorm1d (standard for Conv1d)
self.cnn = nn.Sequential(
# First conv layer: extract local patterns
nn.Conv1d(
in_channels=rank,
out_channels=64,
kernel_size=3,
padding=1,
bias=False # Bias not needed before BatchNorm
),
nn.BatchNorm1d(64),
nn.GELU(),
# Second conv layer: higher-level patterns
nn.Conv1d(
in_channels=64,
out_channels=128,
kernel_size=3,
padding=1,
bias=False
),
nn.BatchNorm1d(128),
nn.GELU(),
# Third conv layer: abstract features
nn.Conv1d(
in_channels=128,
out_channels=256,
kernel_size=3,
padding=1,
bias=False
),
nn.BatchNorm1d(256),
nn.GELU(),
# Global average pooling: compress to single vector
nn.AdaptiveAvgPool1d(1)
)
# Final projection to desired state dimension
self.projection = nn.Sequential(
nn.Linear(256, state_dim),
nn.LayerNorm(state_dim), # ✅ LayerNorm OK here (after Linear)
nn.GELU()
)
self._init_weights()
print(f"✅ Parametric Encoder initialized:")
print(f" - Input: ({rank}, {hidden_dim}) per layer")
print(f" - Output: {state_dim} features per layer")
print(f" - Total params: {sum(p.numel() for p in self.parameters()) / 1e6:.2f}M")
def _init_weights(self):
"""Initialize weights using appropriate initialization strategies"""
for m in self.modules():
if isinstance(m, nn.Conv1d):
# Xavier works well with GELU
nn.init.xavier_normal_(m.weight)
if hasattr(m, 'bias') and m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, nn.Linear):
nn.init.xavier_normal_(m.weight)
if m.bias is not None:
nn.init.zeros_(m.bias)
elif isinstance(m, (nn.BatchNorm1d, nn.LayerNorm)):
nn.init.ones_(m.weight)
nn.init.zeros_(m.bias)
def forward(self, lora_weights: torch.Tensor) -> torch.Tensor:
"""
Compress LoRA weight matrices into feature vectors.
Args:
lora_weights: Tensor of shape (B, N, r, d) or (N, r, d)
Returns:
features: Tensor of shape (B, N, s_param) or (N, s_param)
"""
# Handle both 3D and 4D inputs
if lora_weights.dim() == 3:
batch_size = 1
N, r, d = lora_weights.shape
x = lora_weights.unsqueeze(0)
else:
batch_size, N, r, d = lora_weights.shape
x = lora_weights
# Validate dimensions
assert r == self.rank, f"Expected rank {self.rank}, got {r}"
assert d == self.hidden_dim, f"Expected hidden_dim {self.hidden_dim}, got {d}"
assert N == self.num_layers, f"Expected num_layers {self.num_layers}, got {N}"
# Reshape: (B, N, r, d) -> (B*N, r, d) for Conv1d
x = x.reshape(batch_size * N, r, d)
# Pass through CNN
x = self.cnn(x) # (B*N, 256, 1)
# Squeeze last dimension
x = x.squeeze(-1) # (B*N, 256)
# Project to state dimension
x = self.projection(x) # (B*N, s_param)
# Reshape back: (B*N, s_param) -> (B, N, s_param)
x = x.reshape(batch_size, N, self.state_dim)
if lora_weights.dim() == 3:
x = x.squeeze(0)
return x
def get_output_dim(self) -> int:
"""Get output feature dimension"""
return self.state_dim

Xet Storage Details

Size:
4.76 kB
·
Xet hash:
d7fbf2f5d9ae4f9bc7e832ac0fcbde7a56b7eff79e2a14a56e9bbd7771b3a4e3

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.