| import numpy as np |
| import torch |
| from sklearn.neural_network import MLPRegressor |
| from pathlib import Path |
| import sys |
| import json |
| import os |
| import shutil |
| from typing import Any, Optional |
|
|
| |
| repo_root = Path(__file__).parent.parent.parent |
| sys.path.append(str(repo_root / 'pykan')) |
|
|
| from kan import * |
| |
| try: |
| from .gmm_dataset import GeneralizedGaussianMixture |
| except ImportError: |
| from gmm_dataset import GeneralizedGaussianMixture |
|
|
| def train_and_evaluate(dataset: GeneralizedGaussianMixture, |
| save_dir: Path, |
| kan_config: Optional[dict[str, Any]] = None, |
| random_state: int = 42) -> dict[str, Any]: |
| """训练和评估不同模型""" |
| save_dir.mkdir(parents=True, exist_ok=True) |
| |
| |
| X_train, y_train = dataset.generate_samples(N=1000) |
| X_test, y_test = dataset.generate_samples(N=200) |
| |
| device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') |
| torch.set_default_dtype(torch.float64) |
| |
| |
| train_data = { |
| 'train_input': torch.FloatTensor(X_train).to(device), |
| 'train_label': torch.FloatTensor(y_train).reshape(-1, 1).to(device), |
| 'test_input': torch.FloatTensor(X_test).to(device), |
| 'test_label': torch.FloatTensor(y_test).reshape(-1, 1).to(device) |
| } |
| |
| |
| np.savez(save_dir / f'data_{random_state}.npz', |
| X_train=X_train, y_train=y_train, |
| X_test=X_test, y_test=y_test) |
| |
| |
| if kan_config is None: |
| kan_config = { |
| 'width': [dataset.D, 5, 1], |
| 'grid': 5, |
| 'k': 3 |
| } |
| |
| |
| kan_model = KAN(**kan_config, seed=random_state, device=str(device)) |
| kan_model = kan_model.to(device) |
| results = kan_model.fit(train_data, opt="LBFGS", steps=50, lamb=0.001) |
| |
| |
| mlp = MLPRegressor( |
| hidden_layer_sizes=(10, 5), |
| max_iter=1000, |
| random_state=random_state |
| ) |
| mlp.fit(X_train, y_train) |
| |
| |
| grid_x = np.linspace(X_train.min(), X_train.max(), 100) |
| grid_y = np.linspace(X_train.min(), X_train.max(), 100) |
| XX, YY = np.meshgrid(grid_x, grid_y) |
| grid_points = np.column_stack((XX.ravel(), YY.ravel())) |
| |
| with torch.no_grad(): |
| kan_pred = kan_model(torch.FloatTensor(grid_points).to(device)).cpu().numpy() |
| mlp_pred = mlp.predict(grid_points) |
| true_density = dataset.pdf(grid_points) |
| |
| |
| kan_test_rmse = np.sqrt(np.mean((kan_model(train_data['test_input']).cpu().numpy() - y_test.reshape(-1, 1))**2)) |
| mlp_test_rmse = np.sqrt(np.mean((mlp.predict(X_test).reshape(-1, 1) - y_test.reshape(-1, 1))**2)) |
| |
| evaluation = { |
| 'random_state': random_state, |
| 'kan_test_rmse': float(kan_test_rmse), |
| 'mlp_test_rmse': float(mlp_test_rmse), |
| 'training_history': results |
| } |
| |
| |
| np.savez(save_dir / f'predictions_{random_state}.npz', |
| grid_points=grid_points, |
| kan_pred=kan_pred, |
| mlp_pred=mlp_pred, |
| true_density=true_density) |
| |
| |
| with open(save_dir / f'evaluation_{random_state}.json', 'w') as f: |
| json.dump(evaluation, f) |
| |
| return evaluation |
|
|
| def run_experiments(save_dir: Path, n_experiments: int = 5) -> dict[str, float]: |
| """进行多次随机实验""" |
| save_dir.mkdir(parents=True, exist_ok=True) |
| |
| all_results = [] |
| base_seed = 42 |
| |
| for i in range(n_experiments): |
| print(f"Running experiment {i+1}/{n_experiments}") |
| random_state = base_seed + i |
| |
| |
| dataset = GeneralizedGaussianMixture( |
| D=2, |
| K=3, |
| p=2.0, |
| centers=np.array([[-2, -2], [0, 0], [2, 2]]), |
| scales=np.array([[0.3, 0.3], [0.2, 0.2], [0.4, 0.4]]), |
| weights=np.array([0.3, 0.4, 0.3]), |
| seed=random_state |
| ) |
| |
| |
| result = train_and_evaluate(dataset, save_dir / str(random_state), random_state=random_state) |
| all_results.append(result) |
| |
| |
| with open(save_dir / 'all_results.json', 'w') as f: |
| json.dump(all_results, f) |
| |
| |
| kan_rmses = [r['kan_test_rmse'] for r in all_results] |
| mlp_rmses = [r['mlp_test_rmse'] for r in all_results] |
| |
| statistics = { |
| 'kan_mean_rmse': float(np.mean(kan_rmses)), |
| 'kan_std_rmse': float(np.std(kan_rmses)), |
| 'mlp_mean_rmse': float(np.mean(mlp_rmses)), |
| 'mlp_std_rmse': float(np.std(mlp_rmses)), |
| } |
| |
| with open(save_dir / 'statistics.json', 'w') as f: |
| json.dump(statistics, f) |
| |
| return statistics |
|
|
| if __name__ == '__main__': |
| |
| results_dir = Path(__file__).parent / 'results' |
| stats = run_experiments(results_dir) |
| print("\nExperiment Statistics:") |
| print(f"KAN Test RMSE: {stats['kan_mean_rmse']:.4f} ± {stats['kan_std_rmse']:.4f}") |
| print(f"MLP Test RMSE: {stats['mlp_mean_rmse']:.4f} ± {stats['mlp_std_rmse']:.4f}") |