Files
2026-07-13 12:09:03 +08:00

274 lines
8.4 KiB
Python

import math
import random
def zero_init(fan_in, fan_out):
return [[0.0 for _ in range(fan_in)] for _ in range(fan_out)]
def random_init(fan_in, fan_out, scale=1.0):
return [[random.gauss(0, scale) for _ in range(fan_in)] for _ in range(fan_out)]
def xavier_init(fan_in, fan_out):
std = math.sqrt(2.0 / (fan_in + fan_out))
return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]
def kaiming_init(fan_in, fan_out):
std = math.sqrt(2.0 / fan_in)
return [[random.gauss(0, std) for _ in range(fan_in)] for _ in range(fan_out)]
def sigmoid(x):
x = max(-500, min(500, x))
return 1.0 / (1.0 + math.exp(-x))
def tanh_act(x):
return math.tanh(x)
def relu(x):
return max(0.0, x)
def forward_deep(init_fn, activation_fn, n_layers=50, width=64, n_samples=100):
random.seed(42)
layer_magnitudes = []
inputs = [[random.gauss(0, 1) for _ in range(width)] for _ in range(n_samples)]
for layer_idx in range(n_layers):
weights = init_fn(width, width)
biases = [0.0] * width
new_inputs = []
for sample in inputs:
output = []
for neuron_idx in range(width):
z = sum(weights[neuron_idx][j] * sample[j] for j in range(width)) + biases[neuron_idx]
output.append(activation_fn(z))
new_inputs.append(output)
inputs = new_inputs
magnitudes = []
for sample in inputs:
magnitudes.append(sum(abs(v) for v in sample) / width)
mean_mag = sum(magnitudes) / len(magnitudes)
layer_magnitudes.append(mean_mag)
return layer_magnitudes
def magnitude_report(name, magnitudes):
print(f"\n{name}:")
for i, mag in enumerate(magnitudes):
if i % 5 == 0 or i == len(magnitudes) - 1:
if mag > 1e6:
bar = "X" * 50 + " EXPLODED"
elif mag < 1e-6:
bar = "." + " VANISHED"
else:
bar_len = min(50, max(1, int(mag * 10)))
bar = "#" * bar_len
print(f" Layer {i+1:3d}: {bar} ({mag:.6f})")
def symmetry_demo():
weights = zero_init(2, 4)
biases = [0.0] * 4
inputs = [0.5, -0.3]
outputs = []
for neuron_idx in range(4):
z = sum(weights[neuron_idx][j] * inputs[j] for j in range(2)) + biases[neuron_idx]
outputs.append(sigmoid(z))
print("Symmetry Demo (4 neurons, zero init):")
for i, out in enumerate(outputs):
print(f" Neuron {i}: output = {out:.6f}")
all_same = all(abs(outputs[i] - outputs[0]) < 1e-10 for i in range(len(outputs)))
print(f" All identical: {all_same}")
print(f" Effective parameters: 1 (not {len(weights) * len(weights[0])})")
def variance_analysis():
fan_in = 64
n_trials = 10000
configs = [
("Random N(0,1)", 1.0),
("Random N(0,0.01)", 0.01),
("Xavier std", math.sqrt(2.0 / (fan_in + fan_in))),
("Kaiming std", math.sqrt(2.0 / fan_in)),
]
print("\nVariance Analysis (fan_in=64, single layer):")
print(f" {'Strategy':<25} {'Weight Var':>12} {'Output Var':>12} {'Ratio':>10}")
print(" " + "-" * 60)
for name, std in configs:
random.seed(42)
output_vars = []
for _ in range(n_trials):
inputs = [random.gauss(0, 1) for _ in range(fan_in)]
weights = [random.gauss(0, std) for _ in range(fan_in)]
z = sum(w * x for w, x in zip(weights, inputs))
output_vars.append(z * z)
mean_output_var = sum(output_vars) / len(output_vars)
weight_var = std * std
ratio = mean_output_var
print(f" {name:<25} {weight_var:>12.6f} {mean_output_var:>12.4f} {ratio:>10.4f}")
def run_experiment():
configs = [
("Zero + Sigmoid", lambda fi, fo: zero_init(fi, fo), sigmoid),
("Random N(0,1) + ReLU", lambda fi, fo: random_init(fi, fo, 1.0), relu),
("Random N(0,0.01) + ReLU", lambda fi, fo: random_init(fi, fo, 0.01), relu),
("Xavier + Sigmoid", xavier_init, sigmoid),
("Xavier + Tanh", xavier_init, tanh_act),
("Kaiming + ReLU", kaiming_init, relu),
]
print(f"\n{'Strategy':<30} {'L1':>10} {'L5':>10} {'L10':>10} {'L25':>10} {'L50':>10}")
print("-" * 80)
all_results = {}
for name, init_fn, act_fn in configs:
mags = forward_deep(init_fn, act_fn)
all_results[name] = mags
row = f"{name:<30}"
for idx in [0, 4, 9, 24, 49]:
val = mags[idx]
if val > 1e6:
row += f" {'EXPLODED':>10}"
elif val < 1e-6:
row += f" {'VANISHED':>10}"
else:
row += f" {val:>10.4f}"
print(row)
return all_results
def training_comparison():
random.seed(42)
data = []
for _ in range(200):
x = random.uniform(-2, 2)
y = random.uniform(-2, 2)
label = 1.0 if x * x + y * y < 1.5 else 0.0
data.append(([x, y], label))
def train_with_init(init_name, init_scale, activation_fn, activation_deriv):
random.seed(0)
hidden_size = 8
lr = 0.1
if init_name == "xavier":
std_w1 = math.sqrt(2.0 / (2 + hidden_size))
std_w2 = math.sqrt(2.0 / (hidden_size + 1))
elif init_name == "kaiming":
std_w1 = math.sqrt(2.0 / 2)
std_w2 = math.sqrt(2.0 / hidden_size)
else:
std_w1 = init_scale
std_w2 = init_scale
w1 = [[random.gauss(0, std_w1) for _ in range(2)] for _ in range(hidden_size)]
b1 = [0.0] * hidden_size
w2 = [random.gauss(0, std_w2) for _ in range(hidden_size)]
b2 = 0.0
losses = []
for epoch in range(300):
total_loss = 0
correct = 0
for x, target in data:
z1 = []
h = []
for i in range(hidden_size):
z = w1[i][0] * x[0] + w1[i][1] * x[1] + b1[i]
z1.append(z)
h.append(activation_fn(z))
z2 = sum(w2[i] * h[i] for i in range(hidden_size)) + b2
out = sigmoid(z2)
error = out - target
d_out = error * out * (1 - out)
for i in range(hidden_size):
d_h = d_out * w2[i] * activation_deriv(z1[i])
w2[i] -= lr * d_out * h[i]
for j in range(2):
w1[i][j] -= lr * d_h * x[j]
b1[i] -= lr * d_h
b2 -= lr * d_out
total_loss += (out - target) ** 2
if (out >= 0.5) == (target >= 0.5):
correct += 1
losses.append(total_loss / len(data))
return losses
def sigmoid_d(x):
s = sigmoid(x)
return s * (1 - s)
def relu_d(x):
return 1.0 if x > 0 else 0.0
configs = [
("Random(0.01) + Sigmoid", "random", 0.01, sigmoid, sigmoid_d),
("Random(1.0) + Sigmoid", "random", 1.0, sigmoid, sigmoid_d),
("Xavier + Sigmoid", "xavier", 0, sigmoid, sigmoid_d),
("Random(0.01) + ReLU", "random", 0.01, relu, relu_d),
("Random(1.0) + ReLU", "random", 1.0, relu, relu_d),
("Kaiming + ReLU", "kaiming", 0, relu, relu_d),
]
print("\nTraining Comparison (300 epochs, circle dataset):")
print(f" {'Config':<30} {'Start Loss':>12} {'End Loss':>12} {'Improvement':>12}")
print(" " + "-" * 66)
for name, init_name, scale, act_fn, act_d_fn in configs:
losses = train_with_init(init_name, scale, act_fn, act_d_fn)
start = losses[0]
end = losses[-1]
improvement = (1 - end / start) * 100 if start > 0 else 0
print(f" {name:<30} {start:>12.6f} {end:>12.6f} {improvement:>11.1f}%")
if __name__ == "__main__":
print("=" * 70)
print("STEP 1: Symmetry Problem -- Zero Init")
print("=" * 70)
symmetry_demo()
print("\n" + "=" * 70)
print("STEP 2: Variance Analysis")
print("=" * 70)
variance_analysis()
print("\n" + "=" * 70)
print("STEP 3: 50-Layer Forward Pass Experiment")
print("=" * 70)
all_results = run_experiment()
print("\n" + "=" * 70)
print("STEP 4: Layer-by-Layer Magnitude Reports")
print("=" * 70)
for name, mags in all_results.items():
magnitude_report(name, mags)
print("\n" + "=" * 70)
print("STEP 5: Training Comparison")
print("=" * 70)
training_comparison()