burt-imma / scripts /gradient_comparison.py
SNAPKITTYWEST's picture
push from SNAPKITTYWEST/burt-imma
b88c26d verified
Raw
History Blame Contribute Delete
3.23 kB
#!/usr/bin/env python3
"""
MMEP Gradient vs Backprop Gradient Comparison
Computes gradients via both MMEP (free/nudged phase difference) and
standard backpropagation, then reports cosine similarity and L2 distance.
Usage:
python scripts/gradient_comparison.py --hidden-dim 256 --num-layers 4
Contact: jessica@collectivekitty.com
"""
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
def compute_mmep_gradient(model, x, target, T_free=20, T_nudge=4,
alpha=0.5, beta=0.1):
"""Compute EP gradient via free/nudged phase difference."""
# Free phase
h = x.clone()
for _ in range(T_free):
h = (1 - alpha) * h + alpha * torch.tanh(model(h))
h_free = h.detach().clone()
# Nudged phase
h = h_free.clone()
for _ in range(T_nudge):
h = (1 - alpha) * h + alpha * torch.tanh(model(h))
h = h + beta * (target - h)
h_nudged = h.detach().clone()
# EP gradient: (1/beta) * (correlation_nudged - correlation_free)
# For weight gradient: dW ~ (1/beta) * (h_nudged @ x^T - h_free @ x^T)
grad_ep = (1.0 / beta) * (h_nudged - h_free).mean(dim=0)
return grad_ep
def compute_backprop_gradient(model, x, target):
"""Compute standard backprop gradient."""
model.zero_grad()
output = model(x)
loss = F.mse_loss(output, target)
loss.backward()
# Return gradient of first parameter as representative
for p in model.parameters():
if p.grad is not None:
return p.grad.flatten()[:target.shape[-1]]
return torch.zeros(target.shape[-1])
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--hidden-dim", type=int, default=64)
parser.add_argument("--num-layers", type=int, default=2)
parser.add_argument("--T-free", type=int, default=20)
parser.add_argument("--T-nudge", type=int, default=4)
parser.add_argument("--beta", type=float, default=0.1)
parser.add_argument("--num-samples", type=int, default=100)
args = parser.parse_args()
d = args.hidden_dim
model = nn.Linear(d, d, bias=False)
torch.manual_seed(42)
cos_sims = []
for _ in range(args.num_samples):
x = torch.randn(8, d)
target = torch.randn(8, d)
grad_ep = compute_mmep_gradient(model, x, target,
T_free=args.T_free, T_nudge=args.T_nudge,
beta=args.beta)
grad_bp = compute_backprop_gradient(model, x, target)
# Align dimensions
min_dim = min(grad_ep.shape[0], grad_bp.shape[0])
cos_sim = F.cosine_similarity(
grad_ep[:min_dim].unsqueeze(0),
grad_bp[:min_dim].unsqueeze(0)
).item()
cos_sims.append(cos_sim)
avg_cos = sum(cos_sims) / len(cos_sims)
print(f"Gradient Comparison (d={d}, L={args.num_layers})")
print(f" Mean cosine similarity: {avg_cos:.4f}")
print(f" Min: {min(cos_sims):.4f}, Max: {max(cos_sims):.4f}")
print(f" (1.0 = perfect alignment, expected to improve with more T_free)")
if __name__ == "__main__":
main()