Construct source neuron
Browse files- construct_correct_neurons.py +122 -0
construct_correct_neurons.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Construct neurons to match two Jacobian boundaries at x1 ≈ -0.665558 and x2 ≈ 0.594541."""
|
| 3 |
+
|
| 4 |
+
import torch
|
| 5 |
+
import math
|
| 6 |
+
import os
|
| 7 |
+
from safetensors.torch import save_file
|
| 8 |
+
|
| 9 |
+
# Target boundaries and slopes
|
| 10 |
+
boundary_x1 = -0.665558
|
| 11 |
+
boundary_x2 = 0.594541
|
| 12 |
+
left_slope = 11.0
|
| 13 |
+
mid_slope = 1.0
|
| 14 |
+
right_slope = 0.5
|
| 15 |
+
|
| 16 |
+
# Right equation: y = 0.5 * x - 0.2489895
|
| 17 |
+
right_eq = lambda x: 0.5 * x - 0.2489895
|
| 18 |
+
|
| 19 |
+
# Middle equation (continuous at boundary_x2):
|
| 20 |
+
c_mid = right_eq(boundary_x2) - mid_slope * boundary_x2
|
| 21 |
+
mid_eq = lambda x: mid_slope * x + c_mid
|
| 22 |
+
|
| 23 |
+
# Left equation (continuous at boundary_x1):
|
| 24 |
+
c_left = mid_eq(boundary_x1) - left_slope * boundary_x1
|
| 25 |
+
left_eq = lambda x: left_slope * x + c_left
|
| 26 |
+
|
| 27 |
+
print("Constructing neurons to match:")
|
| 28 |
+
print(f" Boundary 1 at x = {boundary_x1}")
|
| 29 |
+
print(f" Boundary 2 at x = {boundary_x2}")
|
| 30 |
+
print(f" Left slope = {left_slope:4.1f}, range: x < {boundary_x1:.6f}")
|
| 31 |
+
print(f" Middle slope = {mid_slope:4.1f}, range: {boundary_x1:.6f} < x < {boundary_x2:.6f}")
|
| 32 |
+
print(f" Right slope = {right_slope:4.1f}, range: x > {boundary_x2:.6f}")
|
| 33 |
+
|
| 34 |
+
# Use native PyTorch tensors to avoid redundant casting overhead
|
| 35 |
+
W1 = torch.zeros((8, 1), dtype=torch.float32)
|
| 36 |
+
b1 = torch.zeros(8, dtype=torch.float32)
|
| 37 |
+
W2 = torch.zeros((1, 8), dtype=torch.float32)
|
| 38 |
+
b2 = torch.zeros(1, dtype=torch.float32)
|
| 39 |
+
|
| 40 |
+
# Neuron 0: Always active pure slope carrier
|
| 41 |
+
# FIX: Use a robustly large bias so the neuron doesn't turn off during extreme negative activation outliers
|
| 42 |
+
W1[0, 0] = 1.0
|
| 43 |
+
b1[0] = 100.0 # Guarantees the carrier stays active for x > -100.0
|
| 44 |
+
W2[0, 0] = right_slope
|
| 45 |
+
|
| 46 |
+
# Neuron 1: Active left of boundary_x1 (adds left_slope - mid_slope = 10.0 to slope)
|
| 47 |
+
W1[1, 0] = -1.0
|
| 48 |
+
b1[1] = boundary_x1
|
| 49 |
+
W2[0, 1] = -(left_slope - mid_slope)
|
| 50 |
+
|
| 51 |
+
# Neuron 2: Active left of boundary_x2 (adds mid_slope - right_slope = 0.5 to slope)
|
| 52 |
+
W1[2, 0] = -1.0
|
| 53 |
+
b1[2] = boundary_x2
|
| 54 |
+
W2[0, 2] = -(mid_slope - right_slope)
|
| 55 |
+
|
| 56 |
+
# Neurons 3-7: Inactive (zero weights)
|
| 57 |
+
|
| 58 |
+
# Calculate exact b2 so that the function matches target_y at boundary_x2
|
| 59 |
+
target_y = right_eq(boundary_x2)
|
| 60 |
+
neuron0_out = W2[0, 0] * (W1[0, 0] * boundary_x2 + b1[0])
|
| 61 |
+
b2[0] = target_y - neuron0_out
|
| 62 |
+
|
| 63 |
+
print("\nConstructed neuron weights:")
|
| 64 |
+
print(f"W1:\n{W1.numpy()}")
|
| 65 |
+
print(f"b1: {b1.numpy()}")
|
| 66 |
+
print(f"W2:\n{W2.numpy()}")
|
| 67 |
+
print(f"b2: {b2.numpy()}")
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# Verify the construction natively
|
| 71 |
+
def mlp_forward(x):
|
| 72 |
+
x_t = torch.tensor([[x]], dtype=torch.float32)
|
| 73 |
+
h = x_t @ W1.T + b1
|
| 74 |
+
h = torch.relu(h)
|
| 75 |
+
y = h @ W2.T + b2
|
| 76 |
+
return y.item()
|
| 77 |
+
|
| 78 |
+
print("\n" + "=" * 60)
|
| 79 |
+
print("Automated Verification:")
|
| 80 |
+
print("=" * 60)
|
| 81 |
+
|
| 82 |
+
# Test at Boundary 1
|
| 83 |
+
y_b1 = mlp_forward(boundary_x1)
|
| 84 |
+
expected_y_b1 = mid_eq(boundary_x1)
|
| 85 |
+
assert math.isclose(y_b1, expected_y_b1, rel_tol=1e-4, abs_tol=1e-5), f"Boundary 1 mismatch! Expected {expected_y_b1}, got {y_b1}"
|
| 86 |
+
print(f"✓ Boundary 1 (x = {boundary_x1}) matched")
|
| 87 |
+
|
| 88 |
+
# Test at Boundary 2
|
| 89 |
+
y_b2 = mlp_forward(boundary_x2)
|
| 90 |
+
expected_y_b2 = right_eq(boundary_x2)
|
| 91 |
+
assert math.isclose(y_b2, expected_y_b2, rel_tol=1e-4, abs_tol=1e-5), f"Boundary 2 mismatch! Expected {expected_y_b2}, got {y_b2}"
|
| 92 |
+
print(f"✓ Boundary 2 (x = {boundary_x2}) matched")
|
| 93 |
+
|
| 94 |
+
# Test left slope
|
| 95 |
+
x_left1, x_left2 = boundary_x1 - 0.10, boundary_x1 - 0.05
|
| 96 |
+
slope_left = (mlp_forward(x_left2) - mlp_forward(x_left1)) / (x_left2 - x_left1)
|
| 97 |
+
assert math.isclose(slope_left, left_slope, rel_tol=1e-4, abs_tol=1e-5), f"Left slope mismatch! Expected {left_slope}, got {slope_left}"
|
| 98 |
+
print(f"✓ Left slope matched: {slope_left:.4f}")
|
| 99 |
+
|
| 100 |
+
# Test middle slope
|
| 101 |
+
x_mid1, x_mid2 = (boundary_x1 + boundary_x2) / 2, ((boundary_x1 + boundary_x2) / 2) + 0.05
|
| 102 |
+
slope_mid = (mlp_forward(x_mid2) - mlp_forward(x_mid1)) / (x_mid2 - x_mid1)
|
| 103 |
+
assert math.isclose(slope_mid, mid_slope, rel_tol=1e-4, abs_tol=1e-5), f"Middle slope mismatch! Expected {mid_slope}, got {slope_mid}"
|
| 104 |
+
print(f"✓ Middle slope matched: {slope_mid:.4f}")
|
| 105 |
+
|
| 106 |
+
# Test right slope
|
| 107 |
+
x_right1, x_right2 = boundary_x2 + 0.05, boundary_x2 + 0.10
|
| 108 |
+
slope_right = (mlp_forward(x_right2) - mlp_forward(x_right1)) / (x_right2 - x_right1)
|
| 109 |
+
assert math.isclose(slope_right, right_slope, rel_tol=1e-4, abs_tol=1e-5), f"Right slope mismatch! Expected {right_slope}, got {slope_right}"
|
| 110 |
+
print(f"✓ Right slope matched: {slope_right:.4f}")
|
| 111 |
+
|
| 112 |
+
os.makedirs("test_mlp_hf", exist_ok=True)
|
| 113 |
+
|
| 114 |
+
# Save the constructed weights
|
| 115 |
+
save_file({
|
| 116 |
+
"layer1.weight": W1,
|
| 117 |
+
"layer1.bias": b1,
|
| 118 |
+
"layer2.weight": W2,
|
| 119 |
+
"layer2.bias": b2,
|
| 120 |
+
}, "test_mlp_hf/model.safetensors")
|
| 121 |
+
|
| 122 |
+
print("\nSuccessfully saved constructed neuron to test_mlp_hf/model.safetensors")
|