| from __future__ import annotations |
|
|
| import os |
| import shutil |
| import time |
| from pathlib import Path |
|
|
| import numpy as np |
| import torch |
| import torch.nn.functional as F |
| from transformers import AutoModelForCausalLM, AutoTokenizer |
|
|
|
|
| ROOT = Path(__file__).resolve().parents[1] |
| EVAL_DIR = ROOT / "eval" |
| DUMP_PATH = EVAL_DIR / "probe_dump.npz" |
| CANDIDATE_MODELS = [ |
| "nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16", |
| "Qwen/Qwen2.5-3B-Instruct", |
| "openbmb/MiniCPM3-4B", |
| ] |
| TRANSCRIPT_CHUNKS = [ |
| "so basically", |
| "our startup uses", |
| "ai to help", |
| "small businesses", |
| "manage inventory", |
| "and we think", |
| "the market is huge", |
| "and honestly", |
| "we already have", |
| "like a thousand", |
| "users and", |
| "growing fast", |
| ] |
| MIN_MODEL_DOWNLOAD_FREE_BYTES = 6 * 1024**3 |
|
|
|
|
| def configure_local_caches() -> None: |
| os.environ.setdefault("HF_HOME", str(ROOT / ".hf-cache")) |
| os.environ.setdefault("TRANSFORMERS_CACHE", str(ROOT / ".hf-cache" / "transformers")) |
| os.environ.setdefault("TORCH_HOME", str(ROOT / ".torch-cache")) |
|
|
|
|
| def cuda_summary() -> torch.device: |
| print(f"torch.__version__ = {torch.__version__}") |
| print(f"torch.version.cuda = {torch.version.cuda}") |
| print(f"torch.cuda.is_available() = {torch.cuda.is_available()}") |
| if torch.cuda.is_available(): |
| print(f"torch.cuda.get_device_name(0) = {torch.cuda.get_device_name(0)}") |
| return torch.device("cuda:0") |
|
|
| print("LOUD CUDA FALLBACK: CUDA/Blackwell is not available in this torch environment; using CPU.") |
| return torch.device("cpu") |
|
|
|
|
| def common_prefix_len(previous: list[int], current: list[int]) -> int: |
| length = 0 |
| for left, right in zip(previous, current): |
| if left != right: |
| break |
| length += 1 |
| return length |
|
|
|
|
| def save_failure(failure: str) -> None: |
| EVAL_DIR.mkdir(parents=True, exist_ok=True) |
| np.savez( |
| DUMP_PATH, |
| nll_series=np.asarray([], dtype=np.float32), |
| hidden_states=np.empty((0, 0), dtype=np.float32), |
| update_ms=np.asarray([], dtype=np.float32), |
| added_text=np.asarray([], dtype=object), |
| model=np.asarray("", dtype=object), |
| device=np.asarray("cpu", dtype=object), |
| dtype=np.asarray("", dtype=object), |
| failure=np.asarray(failure, dtype=object), |
| ) |
|
|
|
|
| def load_first_model(device: torch.device) -> tuple[object, object, str, float, float] | None: |
| free_bytes = shutil.disk_usage(ROOT).free |
| local_files_only = free_bytes < MIN_MODEL_DOWNLOAD_FREE_BYTES |
| if local_files_only: |
| free_gib = free_bytes / 1024**3 |
| needed_gib = MIN_MODEL_DOWNLOAD_FREE_BYTES / 1024**3 |
| print( |
| "LOUD MODEL DOWNLOAD SKIP: only " |
| f"{free_gib:.2f} GiB free; need at least {needed_gib:.1f} GiB to attempt these 3B/4B model downloads. " |
| "Trying repo-local cache only." |
| ) |
|
|
| if device.type == "cuda": |
| dtype = torch.bfloat16 if torch.cuda.is_bf16_supported() else torch.float16 |
| else: |
| dtype = torch.float32 |
|
|
| failures: list[str] = [] |
| for model_id in CANDIDATE_MODELS: |
| print(f"Attempting model: {model_id}") |
| if device.type == "cuda": |
| torch.cuda.empty_cache() |
| torch.cuda.reset_peak_memory_stats(0) |
|
|
| start = time.perf_counter() |
| try: |
| tokenizer = AutoTokenizer.from_pretrained( |
| model_id, |
| trust_remote_code=True, |
| local_files_only=local_files_only, |
| ) |
| load_kwargs = { |
| "trust_remote_code": True, |
| "torch_dtype": dtype, |
| "low_cpu_mem_usage": True, |
| "local_files_only": local_files_only, |
| } |
| if device.type == "cuda": |
| load_kwargs["device_map"] = {"": 0} |
|
|
| model = AutoModelForCausalLM.from_pretrained(model_id, **load_kwargs) |
| if device.type == "cpu": |
| model.to(device) |
| model.eval() |
|
|
| load_seconds = time.perf_counter() - start |
| actual_device = next(model.parameters()).device |
| actual_dtype = next(model.parameters()).dtype |
| vram_gib = 0.0 |
| if device.type == "cuda": |
| torch.cuda.synchronize() |
| vram_gib = torch.cuda.memory_allocated(0) / 1024**3 |
| print( |
| "LOADED " |
| f"model={model_id} device={actual_device} dtype={actual_dtype} " |
| f"load_seconds={load_seconds:.2f} vram_used_gib={vram_gib:.2f}" |
| ) |
| return tokenizer, model, model_id, load_seconds, vram_gib |
| except Exception as exc: |
| elapsed = time.perf_counter() - start |
| message = f"{model_id} failed after {elapsed:.2f}s: {type(exc).__name__}: {exc}" |
| print(message) |
| failures.append(message) |
|
|
| failure = "No candidate model loaded. " + " | ".join(failures) |
| print(f"LOUD PROBE FAILURE: {failure}") |
| save_failure(failure) |
| return None |
|
|
|
|
| def run_updates(tokenizer: object, model: object, model_id: str) -> None: |
| device = next(model.parameters()).device |
| previous_ids: list[int] = [] |
| prefixes: list[str] = [] |
| running = "" |
| for chunk in TRANSCRIPT_CHUNKS: |
| running = f"{running} {chunk}".strip() |
| prefixes.append(running) |
|
|
| nll_series: list[float] = [] |
| hidden_rows: list[np.ndarray] = [] |
| update_ms: list[float] = [] |
| added_text: list[str] = [] |
|
|
| print("step | added_text | mean_NLL | hidden_dim | update_ms") |
| print("-----|------------|----------|------------|----------") |
| for step, (chunk, prefix) in enumerate(zip(TRANSCRIPT_CHUNKS, prefixes), start=1): |
| if device.type == "cuda": |
| torch.cuda.synchronize() |
| start = time.perf_counter() |
|
|
| encoded = tokenizer(prefix, return_tensors="pt", add_special_tokens=False) |
| current_ids = encoded["input_ids"][0].tolist() |
| new_start = common_prefix_len(previous_ids, current_ids) |
| inputs = {name: tensor.to(device) for name, tensor in encoded.items()} |
|
|
| with torch.inference_mode(): |
| outputs = model(**inputs, output_hidden_states=True) |
|
|
| input_ids = inputs["input_ids"] |
| logits = outputs.logits[:, :-1, :].float() |
| targets = input_ids[:, 1:] |
| token_nll = F.cross_entropy( |
| logits.reshape(-1, logits.shape[-1]), |
| targets.reshape(-1), |
| reduction="none", |
| ).reshape(targets.shape) |
|
|
| nll_start = max(new_start, 1) - 1 |
| new_nll = token_nll[0, nll_start:] |
| mean_nll = float(new_nll.mean().detach().cpu()) if new_nll.numel() else float("nan") |
|
|
| last_hidden = outputs.hidden_states[-1][0] |
| new_hidden = last_hidden[new_start:, :] |
| mean_hidden = new_hidden.float().mean(dim=0).detach().cpu() |
|
|
| if device.type == "cuda": |
| torch.cuda.synchronize() |
| elapsed_ms = (time.perf_counter() - start) * 1000.0 |
|
|
| hidden_vec = mean_hidden.numpy().astype(np.float32) |
| nll_series.append(mean_nll) |
| hidden_rows.append(hidden_vec) |
| update_ms.append(elapsed_ms) |
| added_text.append(chunk) |
| previous_ids = current_ids |
|
|
| print(f"{step:>4} | {chunk} | {mean_nll:.4f} | {hidden_vec.shape[0]} | {elapsed_ms:.2f}") |
|
|
| EVAL_DIR.mkdir(parents=True, exist_ok=True) |
| hidden_matrix = np.vstack(hidden_rows).astype(np.float32) |
| actual_dtype = str(next(model.parameters()).dtype) |
| np.savez( |
| DUMP_PATH, |
| nll_series=np.asarray(nll_series, dtype=np.float32), |
| hidden_states=hidden_matrix, |
| update_ms=np.asarray(update_ms, dtype=np.float32), |
| added_text=np.asarray(added_text, dtype=object), |
| model=np.asarray(model_id, dtype=object), |
| device=np.asarray(str(device), dtype=object), |
| dtype=np.asarray(actual_dtype, dtype=object), |
| failure=np.asarray("", dtype=object), |
| ) |
| print(f"Saved {DUMP_PATH}") |
|
|
|
|
| def main() -> None: |
| configure_local_caches() |
| EVAL_DIR.mkdir(parents=True, exist_ok=True) |
| device = cuda_summary() |
| loaded = load_first_model(device) |
| if loaded is None: |
| return |
|
|
| tokenizer, model, model_id, _load_seconds, _vram_gib = loaded |
| run_updates(tokenizer, model, model_id) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|