Instructions to use moncefem/memory-lora-gemma4 with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use moncefem/memory-lora-gemma4 with PEFT:
Task type is invalid.
- Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python3 | |
| """Diagnose WHY a generated adapter helps or hurts, in one model load. | |
| Compares, on the same repo text and the same in-memory model: | |
| none - adapter disabled (baseline) | |
| <checkpoint> - adapter emitted by each hypernetwork checkpoint given | |
| random - random A/B matched to the trained adapter's scale | |
| zero-B - B set to zero (delta is exactly 0; sanity check that the | |
| injection path is wired correctly -- must equal `none`) | |
| Why the controls matter: if a trained adapter hurts, that is either (a) the | |
| adapter is applied incorrectly, or (b) the hypernetwork is undertrained and | |
| emitting noise. `zero-B` isolates (a): if it does not exactly reproduce the | |
| baseline, the injection path is broken. `random` calibrates (b): a trained head | |
| that is no better than random noise of the same magnitude has not learned the | |
| repo->adapter mapping yet. | |
| Usage: | |
| python diagnose_head.py --job <jobId> \ | |
| --checkpoints ../runs/sixview_v2/head.best.pt ../runs/sixview_v1/head.best.pt | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| import numpy as np | |
| import torch | |
| HERE = Path(__file__).resolve().parent | |
| sys.path.insert(0, str(HERE.parent / "engine")) | |
| import config # noqa: E402 | |
| from generate_and_merge import (TARGET_MODULES, load_head, # noqa: E402 | |
| _lora_modules_by_spec_name) | |
| from memory_lora.core import DEFAULT_ROOT_PREFIX, get_module_specs # noqa: E402 | |
| from ab_test_adapter import repo_texts, loss_on # noqa: E402 | |
| def set_adapter(lora_modules, specs, type_of, A_by_type, B_by_type): | |
| with torch.no_grad(): | |
| for sp in specs: | |
| mod = lora_modules.get(sp.full_name) | |
| if mod is None: | |
| continue | |
| t = type_of[sp.full_name] | |
| mod.lora_A["default"].weight.copy_(A_by_type[t].float()) | |
| mod.lora_B["default"].weight.copy_(B_by_type[t].float()) | |
| def main() -> None: | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--job", required=True) | |
| ap.add_argument("--checkpoints", nargs="+", | |
| default=[str(config.DEFAULT_CHECKPOINT)]) | |
| ap.add_argument("--max-chars", type=int, default=4000) | |
| ap.add_argument("--device", default="cpu") | |
| args = ap.parse_args() | |
| from peft import LoraConfig, get_peft_model | |
| from transformers import AutoModelForImageTextToText, AutoTokenizer | |
| ws = config.workspace(args.job) | |
| emb = np.load(ws / "embedding.npy").astype("float32") | |
| texts = repo_texts(ws, args.max_chars) | |
| device = config.resolve_device(args.device) | |
| # Head outputs per checkpoint (cheap: head is small, model isn't loaded yet) | |
| conditions: dict[str, tuple[dict, dict]] = {} | |
| rank = alpha = None | |
| for ck in args.checkpoints: | |
| head, cfg, a = load_head(Path(ck)) | |
| with torch.no_grad(): | |
| out = head(torch.from_numpy(emb).unsqueeze(0)) | |
| name = Path(ck).parent.name + "/" + Path(ck).stem | |
| conditions[name] = ({k: v[0] for k, v in out["A"].items()}, | |
| {k: v[0] for k, v in out["B"].items()}) | |
| rank, alpha = cfg["rank"], a | |
| # Controls derived from the first checkpoint's scale | |
| first = next(iter(conditions.values())) | |
| rnd_A, rnd_B, zero_A, zero_B = {}, {}, {}, {} | |
| g = torch.Generator().manual_seed(3407) | |
| for t, A in first[0].items(): | |
| B = first[1][t] | |
| rnd_A[t] = torch.randn(A.shape, generator=g) * A.std() | |
| rnd_B[t] = torch.randn(B.shape, generator=g) * B.std() | |
| zero_A[t] = A.clone() | |
| zero_B[t] = torch.zeros_like(B) | |
| conditions["random(matched scale)"] = (rnd_A, rnd_B) | |
| conditions["zero-B (must == none)"] = (zero_A, zero_B) | |
| print(f"loading {config.BASE_MODEL} on {device} ...", flush=True) | |
| tok = AutoTokenizer.from_pretrained(config.BASE_MODEL) | |
| if tok.pad_token is None: | |
| tok.pad_token = tok.eos_token | |
| base = AutoModelForImageTextToText.from_pretrained( | |
| config.BASE_MODEL, torch_dtype=torch.float32, low_cpu_mem_usage=True) | |
| specs = get_module_specs(base, TARGET_MODULES, root_prefix=DEFAULT_ROOT_PREFIX) | |
| type_of = {sp.full_name: sp.type for sp in specs} | |
| model = get_peft_model(base, LoraConfig( | |
| r=rank, lora_alpha=alpha, target_modules=[s.full_name for s in specs], | |
| lora_dropout=0.0, bias="none")) | |
| lora_modules = _lora_modules_by_spec_name(model) | |
| model.to(device) | |
| model.eval() | |
| print("ready.\n", flush=True) | |
| def mean_loss(disabled: bool) -> float: | |
| tot = 0.0 | |
| for _, text in texts: | |
| if disabled: | |
| with model.disable_adapter(): | |
| tot += loss_on(model, tok, text, device) | |
| else: | |
| tot += loss_on(model, tok, text, device) | |
| return tot / len(texts) | |
| baseline = mean_loss(disabled=True) | |
| print(f"{'condition':<28} {'mean loss':>10} {'vs none':>10}") | |
| print("-" * 50) | |
| print(f"{'none (base model)':<28} {baseline:10.4f} {0.0:+10.4f}") | |
| for name, (A, B) in conditions.items(): | |
| set_adapter(lora_modules, specs, type_of, A, B) | |
| m = mean_loss(disabled=False) | |
| print(f"{name[:28]:<28} {m:10.4f} {m - baseline:+10.4f}") | |
| print("\nlower is better; `zero-B` must match `none` exactly for the " | |
| "injection path to be considered correct.") | |
| if __name__ == "__main__": | |
| main() | |