doc-to-lora/src/ctx_to_lora/modeling/hypernet.py
2026-06-15 04:31:47 +00:00

958 lines
35 KiB
Python

import logging
from collections.abc import Iterable
from dataclasses import dataclass
from functools import partial
from math import sqrt
from typing import Any
import torch
from einops import unpack
from einops.layers.torch import EinMix as Mix
from jaxtyping import Float, Integer
from peft import (
LoraConfig,
LoraRuntimeConfig,
PeftConfig,
PeftModel,
)
from peft.tuners._buffer_dict import BufferDict
from peft.tuners.tuners_utils import BaseTunerLayer, check_target_module_exists
from peft.utils import PeftType, TaskType
from torch import Tensor, nn
from transformers import (
PretrainedConfig,
PreTrainedModel,
)
from transformers.modeling_outputs import ModelOutput
from transformers.models.modernbert.modeling_modernbert import ModernBertModel
from ctx_to_lora.configs import (
AggregatorArguments,
CtxEncoderArguments,
HypernetArguments,
)
from ctx_to_lora.data.processing import tokenize_ctx_text
from ctx_to_lora.lora_stats import DenseLoraMagnitudeAggregator
from ctx_to_lora.model_loading import (
get_model,
get_tokenizer,
)
from ctx_to_lora.modeling.aggregator import (
AGGREGATOR_CLS,
AggregatorConfig,
get_aggregator_config,
)
from ctx_to_lora.modeling.ctx_encoder import CTX_ENCODER_CLS, CTX_ENCODER_TYPE
from ctx_to_lora.modeling.lora_layer import (
apply_lora_to_layers,
lora_forward,
lora_forward_packed,
)
from ctx_to_lora.modeling.lora_merger import combine_lora
from ctx_to_lora.utils import (
get_layers,
get_num_layers,
get_peft_in_out_features,
get_peft_modules,
)
logger = logging.getLogger()
@dataclass
class HypernetConfig:
latent_size: int
use_light_weight_lora: bool
light_weight_latent_size: int
per_rank_gen: bool
use_per_rank_bias: bool
use_bias: bool
per_layer_processing: bool
use_token_mixing: bool
num_pre_head_layers: int
dropout_rate: float
lora_config: LoraConfig
extra_modules: list[str] | None
base_hidden_size: int
layer_indices: Iterable[int]
feature_sizes: tuple[dict[str, int], dict[str, int]]
aggregator_config: AggregatorConfig
def get_hypernet_config(
model: PreTrainedModel,
ctx_encoder_model_config: PretrainedConfig,
hypernet_args: HypernetArguments,
aggregator_args: AggregatorArguments,
ctx_encoder_args: CtxEncoderArguments,
):
num_modules = 0
lora_config = getattr(model, "peft_config", None)
if lora_config is not None:
lora_config = lora_config["default"]
num_modules += len(lora_config.target_modules)
num_extra_modules = len(hypernet_args.extra_modules or [])
indices = torch.arange(get_num_layers(model), device=model.device)
return HypernetConfig(
**vars(hypernet_args),
base_hidden_size=model.config.hidden_size,
lora_config=lora_config,
layer_indices=indices,
feature_sizes=get_peft_in_out_features(model, peft_config=lora_config),
aggregator_config=get_aggregator_config(
model,
ctx_encoder_model_config,
ctx_encoder_args.ctx_encoder_type == CTX_ENCODER_TYPE.PER_LAYER_ACTIVATIONS,
hypernet_args.latent_size,
num_modules,
num_extra_modules,
lora_config.r,
hypernet_args.per_rank_gen,
aggregator_args,
),
)
def get_init_peft_weights(model: PeftModel, peft_config: PeftConfig = None):
if peft_config is None:
peft_config = model.peft_config["default"]
peft_weights = {module_name: dict() for module_name in peft_config.target_modules}
adapter_name = "default"
for module_name, module in model.named_modules():
if not check_target_module_exists(peft_config, module_name):
continue
if not isinstance(module, BaseTunerLayer):
continue
# support just Linear layer for now
# all modules should be a leave module that is Linear layer
assert isinstance(module.base_layer, nn.Linear), (
"all modules should be a leave module that is Linear layer"
)
# this should always pass
name = module_name.split(".")[-1]
assert name in peft_config.target_modules
for submodule_name, submodule in module.named_modules():
if not isinstance(submodule, (nn.ModuleDict, nn.ParameterDict, BufferDict)):
continue
if adapter_name not in submodule:
continue
if submodule_name not in peft_weights[name]:
peft_weights[name][submodule_name] = submodule[adapter_name]
else:
smod1 = peft_weights[name][submodule_name]
smod2 = submodule[adapter_name]
assert type(smod1) == type(smod2)
return peft_weights
class ResMLPBlock(nn.Module):
def __init__(
self,
input_size: int,
hidden_size: int,
output_size: int,
dropout_rate: float = 0,
):
super().__init__()
layers = []
layers = [
nn.LayerNorm(input_size),
nn.Dropout(dropout_rate),
nn.Linear(input_size, hidden_size),
nn.SiLU(),
nn.Dropout(dropout_rate),
nn.Linear(hidden_size, output_size),
nn.LayerNorm(output_size),
]
self.mlp = nn.Sequential(*layers)
def forward(self, x):
return x + self.mlp(x)
class ResMLPBlockPerLayer(nn.Module):
def __init__(
self,
n_layers: int,
input_size: int,
hidden_size: int,
output_size: int,
):
super().__init__()
layers = [
nn.LayerNorm(input_size),
Mix(
"bs n_layers n_modules r d_in -> bs n_layers n_modules r d_hid",
weight_shape="n_layers d_in d_hid",
bias_shape="n_layers d_hid",
n_layers=n_layers,
d_in=input_size,
d_hid=hidden_size,
),
nn.SiLU(),
Mix(
"bs n_layers n_modules r d_hid -> bs n_layers n_modules r d_out",
weight_shape="n_layers d_hid d_out",
bias_shape="n_layers d_out",
n_layers=n_layers,
d_hid=hidden_size,
d_out=output_size,
),
nn.LayerNorm(output_size),
]
self.layers = nn.Sequential(*layers)
def forward(self, x):
return x + self.layers(x)
class HyperLoRA(nn.Module):
def __init__(self, config: HypernetConfig):
super().__init__()
# aggregator output [bs, n_layers, n_modules, feature_dim]
# by mixing the pooled features with layer embs and module embs (for pooling)
# or via a perceiver w/ bottleneck size = n_modules * n_layers
self.config = config
logger.debug(f"HyperLoRA config: {self.config}")
self.iterative_mode = False
self._init_model()
def _init_model(self):
self.agg_config = self.config.aggregator_config
self.aggregator = AGGREGATOR_CLS[self.agg_config.aggregator_type](
**vars(self.agg_config)
)
self.lora_config = self.config.lora_config
self.r = self.lora_config.r
self.target_modules = (
tuple(sorted(self.lora_config.target_modules)) if self.lora_config else None
)
self.num_modules = len(self.target_modules) if self.target_modules else 0
self.extra_modules = (
self.config.extra_modules if self.config.extra_modules else None
)
self.num_extra_modules = len(self.extra_modules) if self.extra_modules else 0
self.layer_indices = self.config.layer_indices
self.n_layers = len(self.layer_indices)
self.d_in, self.d_out = self.config.feature_sizes
self.d_latent = self.config.latent_size
if self.target_modules:
if self.config.per_layer_processing:
layers = [
ResMLPBlockPerLayer(
self.n_layers,
self.d_latent,
self.d_latent * 4,
self.d_latent,
)
for _ in range(self.config.num_pre_head_layers)
]
else:
layers = [
ResMLPBlock(
input_size=self.config.latent_size,
hidden_size=self.config.latent_size * 4,
output_size=self.config.latent_size,
dropout_rate=getattr(self.config, "dropout_rate", 0),
)
for _ in range(self.config.num_pre_head_layers)
]
self.layers = nn.Sequential(*layers)
self.d_lora = max(self.d_in[m] + self.d_out[m] for m in self.target_modules)
self.bias_A = nn.ParameterDict(
{
m: nn.Parameter(
torch.normal(
0,
0.2 / (self.d_in[m] * self.r) ** 0.5,
(self.n_layers, self.r, self.d_in[m]),
)
)
for m in self.target_modules
}
)
self.bias_B = nn.ParameterDict(
{
m: nn.Parameter(torch.zeros((self.n_layers, self.r, self.d_out[m])))
for m in self.target_modules
}
)
self.scaler_A = nn.ParameterDict(
{
m: nn.Parameter(torch.ones((1, self.n_layers, self.r, 1)))
for m in self.target_modules
}
)
self.scaler_B = nn.ParameterDict(
{
m: nn.Parameter(torch.zeros((1, self.n_layers, self.r, 1)))
for m in self.target_modules
}
)
n_modules = len(self.target_modules)
# have to do this otherwise doesnt work with adamw_torch_fused
# has something to do with the bias shape (n_modules r d_lora)
# when n_modules == 1, adamw_torch_fused complains about device/layout
# but when n_modules > 1, it works fine
if n_modules == 1:
self.head = Mix(
"bs n_layers n_modules r d_latent -> bs n_layers n_modules r d_lora",
weight_shape="n_layers d_latent d_lora",
bias_shape=None, # no bias
n_layers=len(self.layer_indices),
d_latent=self.config.latent_size,
r=self.config.lora_config.r,
d_lora=self.d_lora,
)
else:
self.head = Mix(
"bs n_layers n_modules r d_latent -> bs n_layers n_modules r d_lora",
weight_shape="n_layers n_modules d_latent d_lora",
bias_shape=None, # no bias
n_layers=len(self.layer_indices),
n_modules=n_modules,
d_latent=self.config.latent_size,
r=self.config.lora_config.r,
d_lora=self.d_lora,
)
def get_head_bias(self):
bias_dict = dict()
for module in self.target_modules:
bias_A = self.bias_A[module]
bias_B = self.bias_B[module]
bias_dict[module] = dict(A=bias_A, B=bias_B)
return bias_dict
def _to_lora_dict(
self, flat_loras: Float[Tensor, "bs n_layers n_modules r max_io_dim"]
) -> dict[str, dict[str, Float[Tensor, "bs n_layers r _"]]]:
if self.target_modules is None:
return None
# list of [bs, n_layers, r, in_d_outim]
# and in_d_outim might vary across modules
loras = unpack(
flat_loras,
[[] for _ in range(len(self.target_modules))],
"bs n_layers * r max_io_dim",
)
# dict of {module:
# {A: [bs, n_layers, r, d_inim],
# B: [bs, n_layers, r, d_outim]}}
lora_dict = dict()
for module, lora in zip(self.target_modules, loras):
A, B = unpack(
lora[..., : self.d_in[module] + self.d_out[module]],
[[self.d_in[module]], [self.d_out[module]]],
"bs n_layers r *",
)
# apparently doing A * self.scaler_A is slow due to broadcasting
A = torch.einsum("ijkl,ijkl->ijkl", A, self.scaler_A[module])
B = torch.einsum("ijkl,ijkl->ijkl", B, self.scaler_B[module])
lora_dict[module] = dict(A=A, B=B)
return lora_dict
def _to_layernorm_dict(
self, flat_layernorms: Float[Tensor, "bs n_layers n_modules hidden_size"]
) -> dict[str, Float[Tensor, "bs n_layers hidden_size"]]:
if self.extra_modules is None:
return None
layernorms = unpack(
flat_layernorms,
[[] for _ in range(len(self.extra_modules))],
"bs n_layers * hidden_size",
)
return {k: v for k, v in zip(self.extra_modules, layernorms)}
def enable_iterative_mode(self, x: bool):
self.iterative_mode = x
self.aggregator.enable_iterative_mode(x)
def forward(
self,
features: Float[Tensor, "bs seq_len feature_dim"],
attn_mask: Integer[Tensor, "bs seq_len"] | None = None,
position_ids: Integer[Tensor, "bs seq_len"] | None = None,
n_ctx_chunks: Integer[Tensor, "n_ctx"] | None = None,
):
# [bs, n_layers, n_total_modules, r, feature_dim]
with torch.autocast(device_type="cuda", dtype=torch.bfloat16):
if self.aggregator.layer_to_layer and self.iterative_mode:
# iterative inference
# features: [bs num_layers seq_len feature_dim]
bs, n_layers = features.shape[0:2]
lora_emb = torch.empty(
(bs, n_layers, self.num_modules, self.r, self.config.latent_size),
device=features.device,
)
for i in range(n_layers):
lora_emb[:, i], _ = self.aggregator(
features[:, i], attn_mask, position_ids
)
else:
# batched inference
lora_emb, _ = self.aggregator(features, attn_mask, position_ids)
# [bs, n_layers, n_modules, r, max_in_d_outim]
flat_loras = None
if self.target_modules:
lora_emb = self.layers(lora_emb)
norm = torch.norm(lora_emb, dim=-1, keepdim=True)
norm_lora_emb = lora_emb / norm
flat_loras = self.head(norm_lora_emb)
flat_layernorms = None
return flat_loras, flat_layernorms
def generate_weights(
self,
features: Float[Tensor, "bs seq_len feature_dim"],
attn_mask: Integer[Tensor, "bs seq_len"] | None = None,
position_ids: Integer[Tensor, "bs seq_len"] | None = None,
):
flat_loras, flat_layernorms = self.forward(features, attn_mask, position_ids)
return self._to_lora_dict(flat_loras), self._to_layernorm_dict(flat_layernorms)
class ModulatedPretrainedModel(nn.Module):
def __init__(
self,
base_model: PeftModel,
hypernet_config: HypernetConfig,
ctx_encoder_args: CtxEncoderArguments,
use_base_input_as_ctx: bool = False,
# need non-packed inputs for generation
use_sequence_packing: bool = True,
user_defined_scaling: float = 1,
inp_compressor=None,
):
assert not use_base_input_as_ctx
super().__init__()
self.device = base_model.device
self.peft_config = base_model.peft_config["default"]
self.hypernet_config = hypernet_config
self.ctx_encoder_args = ctx_encoder_args
self.use_base_input_as_ctx = use_base_input_as_ctx
self.use_sequence_packing = use_sequence_packing
self.user_defined_scaling = user_defined_scaling
self.inp_compressor = inp_compressor
self.model_accepts_loss_kwargs = True
self.generated_loras = None
self.log_dense_lora_magnitude = False
self.dense_lora_magnitude = DenseLoraMagnitudeAggregator(
scaling=self.peft_config.lora_alpha
)
self.register_module("base_model", base_model)
self._init_model()
self._bias_hyper_init()
@classmethod
def from_state_dict(
cls,
state_dict: dict,
train: bool = True,
base_model_kwargs: dict = None,
use_flash_attn: bool = True,
**kwargs: Any,
):
lora_config = state_dict["hypernet_config"].lora_config
print(f"lora_config: {lora_config}")
model_name_or_path = state_dict["base_model_name_or_path"]
base_model = get_model(
model_name_or_path,
train=train,
requires_grad=False,
peft_config=lora_config,
model_kwargs=base_model_kwargs,
use_flash_attn=use_flash_attn,
)
hypernet_config = state_dict["hypernet_config"]
if getattr(hypernet_config, "num_pre_head_layers", None) is None:
hypernet_config.num_pre_head_layers = 4
if getattr(hypernet_config, "use_per_rank_bias", None) is None:
hypernet_config.use_per_rank_bias = False
if getattr(hypernet_config, "use_bias", None) is None:
hypernet_config.use_bias = True
ctx_encoder_args = state_dict["ctx_encoder_args"]
model = cls(base_model, hypernet_config, ctx_encoder_args, **kwargs)
model.load_state_dict(state_dict)
return model
def patch_lora_forward(self):
layers = get_layers(self.base_model)
lora_forward_fn = (
lora_forward_packed if self.use_sequence_packing else lora_forward
)
for layer_idx in self.hypernet.layer_indices:
for module_info in get_peft_modules(layers[layer_idx], self.peft_config):
name = module_info["name"]
module = module_info["module"]
if getattr(module, "patched_forward", False):
continue
logger.debug(f"Applying LoRA forward to {name}")
module.forward_orig = module.forward
module.patched_forward = True
module.forward = partial(
lora_forward_fn,
self=module,
lora_dropout_p=self.peft_config.lora_dropout,
scaling=self.peft_config.lora_alpha,
)
def _init_model(self):
# disable adapter of the base model
# this only works with LoRA(?)
# we disable to avoid peft lora computation
self.base_model.disable_adapter_layers()
self.hypernet = (
HyperLoRA(self.hypernet_config).to(self.device).to(torch.float32)
)
self.patch_lora_forward()
ctx_model_name = self.ctx_encoder_args.ctx_encoder_model_name_or_path
if ctx_model_name is None:
ctx_model_name = self.base_model.config.name_or_path
# use an explicit copy of the base model
# for using with "modules_to_save"
base_model_attn_impl = self.base_model.config._attn_implementation
logger.debug(f"ctx_model_name: {ctx_model_name}")
logger.debug(f"base_model.config._attn_implementation: {base_model_attn_impl}")
encoder_model = get_model(
ctx_model_name,
train=self.base_model.training,
requires_grad=False,
use_flash_attn=base_model_attn_impl == "flash_attention_2",
use_q_lora=self.ctx_encoder_args.quantize_ctx_encoder,
)
self.ctx_encoder = CTX_ENCODER_CLS[self.ctx_encoder_args.ctx_encoder_type](
encoder_model, self.ctx_encoder_args
)
# delegate to base_model
@property
def config(self):
return self.base_model.config
@property
def generation_config(self):
return self.base_model.generation_config
@property
def vocab_size(self):
return self.base_model.vocab_size
def get_input_embeddings(self):
return self.base_model.get_input_embeddings()
@torch.no_grad()
def _bias_hyper_init(self):
if self.hypernet.extra_modules:
self.hypernet.extra_head.weight.data[:] = 0
self.hypernet.extra_head.bias.data[:] = 0
if self.hypernet.target_modules:
peft_weights = get_init_peft_weights(
self.base_model, self.hypernet.lora_config
)
logger.debug(f"peft_weights: {peft_weights}")
r = self.hypernet_config.lora_config.r
nn.init.normal_(
self.hypernet.head.weight,
mean=0,
std=0.5
/ sqrt(self.hypernet.config.latent_size + self.hypernet.d_lora * r),
# the head outputs per rank lora --> divide by r to scale down grad
)
def state_dict(self, *args, **kwargs):
# we assume ctx_encoder and base model is frozen here
if len([p for p in self.ctx_encoder.parameters() if p.requires_grad]):
raise ValueError("ctx_encoder contains trainable parameters")
if len([p for p in self.base_model.parameters() if p.requires_grad]):
raise ValueError("base model contains trainable parameters")
state_dict = self.hypernet.state_dict(*args, **kwargs)
state_dict["base_model_name_or_path"] = self.base_model.name_or_path
state_dict["hypernet_config"] = self.hypernet_config
state_dict["ctx_encoder_args"] = self.ctx_encoder_args
return state_dict
def load_state_dict(self, state_dict: dict, *args, **kwargs):
self.base_model_name_or_path = state_dict.pop("base_model_name_or_path")
self.hypernet_config = state_dict.pop("hypernet_config")
self.ctx_encoder_args = state_dict.pop("ctx_encoder_args")
if self.base_model_name_or_path != self.base_model.name_or_path:
raise ValueError(
f"Base model name or path mismatch. "
f"The base model given is: {self.base_model.name_or_path}, "
f"but the loaded name is: {self.base_model_name_or_path}"
)
self._init_model()
def remove_compile_prefix(sd: dict[str, Tensor]) -> dict[str, Tensor]:
COMPILED_PREFIX = "_orig_mod."
for k in list(sd.keys()):
if k.startswith(COMPILED_PREFIX):
sd[k[len(COMPILED_PREFIX) :]] = sd.pop(k)
return sd
load_result = self.hypernet.load_state_dict(
remove_compile_prefix(state_dict),
strict=True, # , *args, **kwargs
)
logger.info(f"load result: {load_result}")
return load_result
def generate_weights(
self,
ctx_ids: Integer[Tensor, "bs ctx_len"],
ctx_attn_mask: Integer[Tensor, "bs ctx_len"] | None = None,
ctx_position_ids: Integer[Tensor, "bs ctx_len"] | None = None,
**kwargs: Any,
):
with torch.no_grad():
ctx_encoder_kwargs = dict(
input_ids=ctx_ids,
attention_mask=ctx_attn_mask,
position_ids=ctx_position_ids,
)
if isinstance(self.ctx_encoder.base_model, ModernBertModel):
position_ids = ctx_position_ids.flatten()
indices = torch.arange(
position_ids.size(0), device=position_ids.device, dtype=torch.int32
)
# [bsz + 1]
cu_seqlens = torch.cat(
(
indices[position_ids == 0],
torch.tensor(
position_ids.size(),
device=position_ids.device,
dtype=torch.int32,
),
)
)
ctx_encoder_kwargs = dict(
input_ids=ctx_ids.squeeze(0),
cu_seqlens=cu_seqlens,
max_seqlen=position_ids.max() + 1,
attention_mask=-1,
seq_len=-1,
batch_size=-1,
)
ctx_features = self.ctx_encoder(**ctx_encoder_kwargs, **kwargs)
if isinstance(self.ctx_encoder.base_model, ModernBertModel):
ctx_features = ctx_features.unsqueeze(0)
if self.user_defined_scaling == 1:
return self.hypernet.generate_weights(
ctx_features, ctx_attn_mask, ctx_position_ids
)
lora_dict, _ = self.hypernet.generate_weights(
ctx_features, ctx_attn_mask, ctx_position_ids
)
for module in lora_dict:
lora_dict[module]["A"] = lora_dict[module]["A"] * self.user_defined_scaling
lora_dict[module]["B"] = lora_dict[module]["B"] * self.user_defined_scaling
return lora_dict, None
def enable_iterative_mode(self, x: bool):
self.hypernet.enable_iterative_mode(x)
def enable_dense_lora_magnitude_logging(
self, x: bool, out_chunk_size: int = 8
) -> None:
self.log_dense_lora_magnitude = x
self.dense_lora_magnitude.scaling = self.peft_config.lora_alpha
self.dense_lora_magnitude.out_chunk_size = out_chunk_size
self.dense_lora_magnitude.reset()
def reset_dense_lora_magnitude_stats(self) -> None:
self.dense_lora_magnitude.reset()
def get_dense_lora_magnitude_stats(self) -> dict[str, object]:
return self.dense_lora_magnitude.to_dict()
def _maybe_track_dense_lora_magnitude(
self, generated_loras: dict[str, dict[str, Tensor]] | None
) -> None:
if not self.log_dense_lora_magnitude or generated_loras is None:
return
self.dense_lora_magnitude.update(generated_loras)
def forward(
self,
ctx_ids: Integer[Tensor, "n_ctx ctx_len"] | None = None,
ctx_attn_mask: Integer[Tensor, "n_ctx ctx_len"] | None = None,
ctx_position_ids: Integer[Tensor, "n_ctx ctx_len"] | None = None,
n_ctx_chunks: Integer[Tensor, "n_ctx"] | None = None,
n_queries: Integer[Tensor, "n_ctx"] | None = None,
return_generated_lora: bool | None = False,
*model_inputs_args: Any,
**model_inputs_kwargs: dict[str, Any],
) -> tuple | ModelOutput:
"""Forward pass of the modulated model."""
generated_loras = None
generated_layernorms = None
if ctx_ids is None and not self.use_base_input_as_ctx:
logger.warning(
(
"*" * 100,
"\n\nNo ctx_features provided, using the base model for forward pass\n\n",
"*" * 100,
)
)
else:
if self.use_base_input_as_ctx:
ctx_ids = (
model_inputs_kwargs["input_ids"]
if "input_ids" in model_inputs_kwargs
else model_inputs_args[0]
)
ctx_attn_mask = (
model_inputs_kwargs["attention_mask"]
if "attention_mask" in model_inputs_kwargs
else None
)
ctx_position_ids = (
model_inputs_kwargs["position_ids"]
if "position_ids" in model_inputs_kwargs
else None
)
generated_loras, generated_layernorms = self.generate_weights(
ctx_ids, ctx_attn_mask, ctx_position_ids
)
if generated_loras is not None:
generated_loras = combine_lora(
generated_loras,
n_ctx_chunks,
lora_bias=self.hypernet.get_head_bias()
if self.hypernet.config.use_bias
else None,
)
self._maybe_track_dense_lora_magnitude(generated_loras)
# input_ids in model_inputs_kwargs contains only
# prompt + response (for hypernet training)
position_ids = (
model_inputs_kwargs["position_ids"]
if "position_ids" in model_inputs_kwargs
else None
)
if n_queries is None:
if ctx_position_ids is None:
n_queries = torch.ones(
ctx_ids.shape[0], dtype=torch.int32, device=self.device
)
else:
# quite redundant (we do cu_seqlens many places)
# TODO: compute cu_seqlens here and propagate that
n_queries = torch.ones(
(ctx_position_ids == 0).sum(),
dtype=torch.int32,
device=self.device,
)
apply_lora_to_layers(
self.base_model,
self.hypernet.layer_indices,
generated_loras,
n_queries,
position_ids,
)
model_outputs = self.base_model(*model_inputs_args, **model_inputs_kwargs)
if return_generated_lora:
return model_outputs, (generated_loras, generated_layernorms)
else:
return model_outputs
def combine_lora(self, *args, **kwargs):
# for timing
return combine_lora(*args, **kwargs)
def apply_lora_to_layers(self, *args, **kwargs):
# for timing
return apply_lora_to_layers(*args, **kwargs)
# for simple api usage
def internalize(self, ctx_str: str):
ctx_tokenizer = get_tokenizer(self.ctx_encoder.base_model.name_or_path)
ctx_ids = tokenize_ctx_text(dict(context=[ctx_str]), ctx_tokenizer)["ctx_ids"]
return self._internalize_from_ids(torch.tensor(ctx_ids, device=self.device))
def _internalize_from_ids(
self,
ctx_ids: Integer[Tensor, "n_ctx ctx_len"] | None = None,
ctx_attn_mask: Integer[Tensor, "n_ctx ctx_len"] | None = None,
ctx_position_ids: Integer[Tensor, "n_ctx ctx_len"] | None = None,
):
self.patch_lora_forward()
if ctx_attn_mask is None and ctx_position_ids is None:
assert ctx_ids.shape[0] == 1
ctx_attn_mask = torch.ones_like(ctx_ids)
generated_loras, generated_layernorms = self.generate_weights(
ctx_ids, ctx_attn_mask, ctx_position_ids
)
self.generated_loras = generated_loras
def reset(self):
self.generated_loras = None
layers = get_layers(self.base_model)
for layer_idx in self.hypernet.layer_indices:
for module_info in get_peft_modules(layers[layer_idx], self.peft_config):
name = module_info["name"]
module = module_info["module"]
logger.debug(f"Resetting forward for {name}")
module.forward = module.forward_orig
module.patched_forward = False
@torch.inference_mode()
def generate(
self,
ctx_ids: Integer[Tensor, "n_chunks ctx_length"] | None = None,
ctx_attn_mask: Integer[Tensor, "n_chunks ctx_length"] | None = None,
ctx_position_ids: Integer[Tensor, "n_chunks ctx_length"] | None = None,
n_ctx_chunks: Integer[Tensor, "n_ctx"] | None = None,
n_queries: Integer[Tensor, "n_ctx"] | None = None,
scalers: Float[Tensor, "n_ctx"] | None = None,
bias_scaler: float | None = None,
*model_inputs_args: Any,
**model_inputs_kwargs: dict[str, Any],
):
generated_loras = None
generated_layernorms = None
if (
ctx_ids is None
and not self.generated_loras
and not self.use_base_input_as_ctx
):
print(
"*" * 100
+ "\n\nNo ctx_ids provided, using the base model for generation\n\n"
+ "*" * 100
)
elif ctx_ids is None and self.generated_loras:
generated_loras = self.generated_loras
if n_ctx_chunks is None:
n_ctx_chunks = torch.tensor((1,), device=self.device)
print(
"*" * 100
+ "\n\nUsing internalized LoRAs for generation\n\n"
+ "*" * 100
)
else:
if self.use_base_input_as_ctx:
ctx_ids = (
model_inputs_kwargs["input_ids"]
if "input_ids" in model_inputs_kwargs
else model_inputs_args[0]
)
ctx_attn_mask = (
model_inputs_kwargs["attention_mask"]
if "attention_mask" in model_inputs_kwargs
else None
)
ctx_position_ids = (
model_inputs_kwargs["position_ids"]
if "position_ids" in model_inputs_kwargs
else None
)
generated_loras, generated_layernorms = self.generate_weights(
ctx_ids, ctx_attn_mask, ctx_position_ids
)
if generated_loras is not None:
generated_loras = self.combine_lora(
generated_loras,
n_ctx_chunks,
lora_bias=self.hypernet.get_head_bias()
if self.hypernet.config.use_bias
else None,
scalers=scalers,
bias_scaler=bias_scaler,
)
self._maybe_track_dense_lora_magnitude(generated_loras)
# apply lora hook to the base model
# TODO: we dont this position_ids for generation?
position_ids = (
model_inputs_kwargs["position_ids"]
if "position_ids" in model_inputs_kwargs
else None
)
if n_queries is None:
if ctx_position_ids is None:
n_queries = torch.ones(
model_inputs_kwargs["input_ids"].shape[0],
dtype=torch.int32,
device=self.device,
)
else:
# quite redundant (we do cu_seqlens many places)
# TODO: compute cu_seqlens here and propagate that
n_queries = torch.ones(
(ctx_position_ids == 0).sum(),
dtype=torch.int32,
device=self.device,
)
apply_lora_to_layers(
self.base_model,
self.hypernet.layer_indices,
generated_loras,
n_queries,
position_ids,
)
model_outputs = self.base_model.generate(
*model_inputs_args, **model_inputs_kwargs
)
return model_outputs
# needed for loading model from checkpoint
# see https://github.com/huggingface/transformers/pull/34632
torch.serialization.add_safe_globals(
[
AggregatorConfig,
LoraConfig,
HypernetConfig,
PeftType,
TaskType,
LoraRuntimeConfig,
set, # for real?
]
)