File size: 4,561 Bytes
69202a6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 | """
scripts/compute_density_map.py
------------------------------
Phase 1 + 2: Collect per-head activations and compute capability density map.
Steps:
1. Load GPT-2 Medium
2. Load calibration data (WikiText-103-raw-v1, 128 seq Γ 512 tok)
3. Run calibration data through model, collect per-head attention outputs
4. For each of 384 heads: train a TopK SAE, compute Ξ², H, Ο, Ξ΄
5. Save density map to .npz
Runtime: ~15β20 min on T4 GPU (384 SAEs Γ 5 epochs each)
Usage:
python scripts/compute_density_map.py --output results/density_map.npz
"""
import argparse
import random
import os
import sys
import numpy as np
import torch
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# ββ Reproducibility βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# Note: original experiments were run without fixed seeds.
# This seed ensures approximate reproducibility across runs.
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
torch.manual_seed(SEED)
torch.cuda.manual_seed_all(SEED)
from transformers import GPT2Model, GPT2Tokenizer
from datasets import load_dataset
from cgc.density import collect_head_activations, compute_density_map
def parse_args():
p = argparse.ArgumentParser(description="Compute CGC capability density map")
p.add_argument("--output", type=str, default="results/density_map.npz",
help="Output path for density map")
p.add_argument("--n_sequences", type=int, default=128,
help="Number of calibration sequences (default: 128)")
p.add_argument("--seq_len", type=int, default=512,
help="Tokens per sequence (default: 512)")
p.add_argument("--batch_size", type=int, default=8,
help="Sequences per forward pass (default: 8)")
return p.parse_args()
def load_calibration_data(tokenizer, n_sequences, seq_len, device):
"""
Load WikiText-103-raw-v1 training split as non-overlapping token chunks.
Same dataset and split used for calibration in the CGC v1 paper.
"""
print("Loading calibration data (WikiText-103-raw-v1, train split)...")
dataset = load_dataset("Salesforce/wikitext",
"wikitext-103-raw-v1", split="train")
full_text = " ".join([
item["text"].strip()
for item in dataset
if len(item["text"].strip()) > 50
])
all_tokens = tokenizer.encode(full_text)
chunks = []
for i in range(n_sequences):
start = i * seq_len
end = start + seq_len
if end > len(all_tokens):
print(f"Warning: only {len(chunks)} sequences available.")
break
chunks.append(torch.tensor(all_tokens[start:end], dtype=torch.long))
data = torch.stack(chunks).to(device)
print(f"Calibration data: {data.shape} "
f"({data.shape[0] * data.shape[1]:,} tokens total)")
return data
def main():
args = parse_args()
os.makedirs(os.path.dirname(args.output) or ".", exist_ok=True)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(f"Device: {device}")
if device.type == "cuda":
print(f"GPU: {torch.cuda.get_device_name(0)}")
vram = torch.cuda.get_device_properties(0).total_memory / 1e9
print(f"VRAM: {vram:.1f} GB")
# Load GPT-2 Medium
print("\nLoading GPT-2 Medium...")
tokenizer = GPT2Tokenizer.from_pretrained("gpt2-medium")
tokenizer.pad_token = tokenizer.eos_token
model = GPT2Model.from_pretrained("gpt2-medium")
model = model.to(device)
model.eval()
n_params = sum(p.numel() for p in model.parameters())
print(f"Loaded. Layers={model.config.n_layer} "
f"Heads={model.config.n_head} "
f"Params={n_params:,}")
# Calibration data
cal_data = load_calibration_data(
tokenizer, args.n_sequences, args.seq_len, device
)
# Phase 1: collect per-head activations
head_acts = collect_head_activations(
model, cal_data, device, args.batch_size
)
# Phase 2: train SAEs and compute density
density_map, _ = compute_density_map(
head_acts, device, seq_len=args.seq_len
)
# Save
density_map.save(args.output)
print(f"\nDensity map saved to: {args.output}")
print("Next: python scripts/run_compression.py "
f"--density_map {args.output}")
if __name__ == "__main__":
main()
|