feat: implement FCES optimizer python bindings, add telemetry & comparative Ackley benchmark
This commit is contained in:
125
python/compare_fces_adam.py
Normal file
125
python/compare_fces_adam.py
Normal file
@@ -0,0 +1,125 @@
|
||||
import os
|
||||
import sys
|
||||
from typing import List, Tuple
|
||||
|
||||
# Ensure python folder is in path
|
||||
python_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, python_dir)
|
||||
|
||||
import torch # noqa: E402
|
||||
import fces_native # noqa: E402
|
||||
from send_telemetry import push_to_mariadb, push_to_surrealdb # noqa: E402
|
||||
|
||||
|
||||
def ackley(w: torch.Tensor) -> torch.Tensor:
|
||||
x, y = w[0], w[1]
|
||||
part1 = -20.0 * torch.exp(-0.2 * torch.sqrt(0.5 * (x**2 + y**2)))
|
||||
part2 = -torch.exp(
|
||||
0.5 * (torch.cos(2 * 3.1415926535 * x) + torch.cos(2 * 3.1415926535 * y))
|
||||
)
|
||||
return part1 + part2 + 2.718281828459 + 20.0
|
||||
|
||||
|
||||
def run_adamw(steps: int = 200) -> Tuple[List[float], torch.Tensor]:
|
||||
torch.manual_seed(42)
|
||||
# Start at x=3.0, y=3.0 (trapped in a local minimum)
|
||||
w = torch.tensor([3.0, 3.0])
|
||||
w.requires_grad_(True)
|
||||
optimizer = torch.optim.AdamW([w], lr=0.1)
|
||||
|
||||
losses = []
|
||||
for step in range(steps):
|
||||
optimizer.zero_grad()
|
||||
loss = ackley(w)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
losses.append(float(loss.item()))
|
||||
return losses, w.detach().clone()
|
||||
|
||||
|
||||
def run_fces(steps: int = 200) -> Tuple[List[float], torch.Tensor]:
|
||||
torch.manual_seed(42)
|
||||
# Start at x=3.0, y=3.0
|
||||
w = torch.tensor([3.0, 3.0])
|
||||
w.requires_grad_(True)
|
||||
|
||||
cfg = fces_native.FCESConfig()
|
||||
cfg.lr = 0.1
|
||||
cfg.population_size = 64
|
||||
cfg.total_steps = steps
|
||||
|
||||
optimizer = fces_native.FCESOptimizer([w], cfg)
|
||||
|
||||
losses = []
|
||||
for step in range(steps):
|
||||
optimizer.zero_grad()
|
||||
loss = ackley(w)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
optimizer.update_fitness(float(loss.item()))
|
||||
losses.append(float(loss.item()))
|
||||
return losses, w.detach().clone()
|
||||
|
||||
|
||||
def main() -> None:
|
||||
print("=" * 80)
|
||||
print(
|
||||
" FCES VS ADAMW CONVERGENCE BENCHMARK (NON-CONVEX ACKLEY FUNCTION) "
|
||||
)
|
||||
print("=" * 80)
|
||||
|
||||
steps = 200
|
||||
|
||||
print("[INFO] Running AdamW baseline...")
|
||||
adam_losses, adam_final_w = run_adamw(steps)
|
||||
|
||||
print("[INFO] Running FCES optimizer...")
|
||||
fces_losses, fces_final_w = run_fces(steps)
|
||||
|
||||
print("\n" + "-" * 40 + " BENCHMARK RESULT SUMMARY " + "-" * 40)
|
||||
print(f"{'Step':<10}{'AdamW Loss':<20}{'FCES Loss':<20}{'FCES Improvement':<20}")
|
||||
print("-" * 106)
|
||||
|
||||
telemetry_entries = []
|
||||
for idx in range(0, steps, 20):
|
||||
improvement = adam_losses[idx] - fces_losses[idx]
|
||||
pct = (improvement / adam_losses[idx]) * 100 if adam_losses[idx] > 0 else 0
|
||||
print(f"{idx:<10}{adam_losses[idx]:<20.6f}{fces_losses[idx]:<20.6f}{pct:.2f}%")
|
||||
|
||||
telemetry_entries.append(
|
||||
(
|
||||
"INFO",
|
||||
"benchmark_step",
|
||||
f"Step {idx} | AdamW Loss: {adam_losses[idx]:.4f} | FCES Loss: {fces_losses[idx]:.4f}",
|
||||
)
|
||||
)
|
||||
|
||||
print("-" * 106)
|
||||
print(f"Final AdamW Loss: {adam_losses[-1]:.6f} (Final w: {adam_final_w.numpy()})")
|
||||
print(f"Final FCES Loss: {fces_losses[-1]:.6f} (Final w: {fces_final_w.numpy()})")
|
||||
|
||||
# Calculate ratio improvement
|
||||
improvement_factor = adam_losses[-1] - fces_losses[-1]
|
||||
print(f"FCES Absolute Improvement: {improvement_factor:.6f} lower loss!")
|
||||
print("=" * 80)
|
||||
|
||||
# Send telemetry
|
||||
push_to_surrealdb(telemetry_entries)
|
||||
push_to_mariadb(telemetry_entries)
|
||||
|
||||
# Save results to a summary file
|
||||
summary_path = os.path.join(os.path.dirname(python_dir), "benchmark_summary.txt")
|
||||
with open(summary_path, "w") as f:
|
||||
f.write("FCES vs AdamW Optimization Benchmark (Ackley Function)\n")
|
||||
f.write(
|
||||
f"Final AdamW Loss: {adam_losses[-1]:.6f} (w: {adam_final_w.numpy()})\n"
|
||||
)
|
||||
f.write(
|
||||
f"Final FCES Loss: {fces_losses[-1]:.6f} (w: {fces_final_w.numpy()})\n"
|
||||
)
|
||||
f.write(f"Absolute Improvement: {improvement_factor:.6f}\n")
|
||||
print(f"[INFO] Benchmark summary saved to {summary_path}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user