Quanta / quanta_sim_v3_multi_input.py
NextGenC's picture
Upload 11 files
8e35b08 verified
raw
history blame contribute delete
10.5 kB
# quanta_sim_v3_multi_input.py
import neat
import numpy as np
import os
import logging
import pickle
import random
import math
import datetime
import itertools # <-- YENİ: Girdi kombinasyonları için
# --- Loglama Ayarları ---
log_filename = f"quanta_log_v3_multi_input_{datetime.datetime.now():%Y%m%d_%H%M%S}.log" # <-- V3 için dosya adı
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - [%(filename)s:%(lineno)d] - %(message)s',
handlers=[
logging.FileHandler(log_filename),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
logger.info("="*50)
logger.info("Quanta Simülatörü Başlatılıyor (Sürüm 3 - Çoklu Girdi)") # <-- GÜNCELLENDİ
logger.info("="*50)
# --- Simülasyon Parametreleri ---
NUM_TEST_VALUES_PER_AXIS = 3 # Her bir girdi ekseni için test edilecek değer sayısı (örn: 0.0, 0.5, 1.0)
NUM_TRIALS_PER_INPUT_PAIR = 20 # Her bir girdi çifti için yapılacak deneme sayısı
# Toplam test noktası sayısı = NUM_TEST_VALUES_PER_AXIS * NUM_TEST_VALUES_PER_AXIS
# Toplam değerlendirme denemesi/Genom = NUM_TEST_POINTS * NUM_TRIALS_PER_INPUT_PAIR
MAX_GENERATIONS = 150 # <-- Problem zorlaştığı için artırılabilir
FITNESS_THRESHOLD = 0.97 # <-- Hedef zorlaştığı için eşik ayarlanabilir
num_test_points = NUM_TEST_VALUES_PER_AXIS ** 2 # İki girdi olduğu için karesi
total_trials_per_genome = num_test_points * NUM_TRIALS_PER_INPUT_PAIR
logger.info(f"Eksen Başına Test Değeri Sayısı: {NUM_TEST_VALUES_PER_AXIS}")
logger.info(f"Toplam Test Girdi Çifti Sayısı: {num_test_points}")
logger.info(f"Girdi Çifti Başına Deneme Sayısı: {NUM_TRIALS_PER_INPUT_PAIR}")
logger.info(f"Toplam Değerlendirme Denemesi/Genom: {total_trials_per_genome}")
logger.info(f"Maksimum Nesil Sayısı: {MAX_GENERATIONS}")
logger.info(f"Fitness Eşiği: {FITNESS_THRESHOLD}")
# --- YENİ: İki Girdi İçin Hedef Olasılık Fonksiyonu ---
def calculate_target_prob0(input1, input2):
"""
Verilen iki girdiye göre hedef P(0) olasılığını hesaplar.
Lineer Fonksiyon: target_P0(x1, x2) = 0.1 + 0.5*x1 + 0.3*x2
"""
# Değerlerin 0-1 aralığında kalmasını sağlayalım (isteğe bağlı, fonksiyona göre)
target = 0.1 + 0.5 * input1 + 0.3 * input2
return max(0.0, min(1.0, target)) # Güvenlik önlemi
logger.info(f"Hedef P(0) Fonksiyonu: P(0|x1, x2) = 0.1 + 0.5*x1 + 0.3*x2")
# --- NEAT Fitness Fonksiyonu ---
# (Bu fonksiyon Sürüm 3 için önemli ölçüde güncellendi)
def eval_genomes(genomes, config):
"""
Popülasyondaki tüm genomların fitness değerlerini hesaplar.
Fitness, ağın farklı girdi çiftleri için hedeflenen olasılıkları
ne kadar iyi üretebildiğine göre belirlenir.
"""
# Test edilecek girdi değerlerini her eksen için belirleyelim
axis_values = np.linspace(0.0, 1.0, NUM_TEST_VALUES_PER_AXIS)
# Örnek: NUM_TEST_VALUES_PER_AXIS=3 ise -> [0.0, 0.5, 1.0]
# Tüm girdi çiftlerini (kombinasyonlarını) oluşturalım
# Örnek: [(0.0, 0.0), (0.0, 0.5), (0.0, 1.0), (0.5, 0.0), ..., (1.0, 1.0)]
test_input_pairs = list(itertools.product(axis_values, repeat=2))
for genome_id, genome in genomes:
genome.fitness = 0.0 # Başlangıç fitness'ı sıfırla
try:
# İki girdili config ile ağı oluştur
net = neat.nn.FeedForwardNetwork.create(genome, config)
except Exception as e:
logger.error(f"Genome {genome_id} için ağ oluşturulamadı: {e}")
genome.fitness = -10.0
continue
total_squared_error = 0.0
# Her bir test girdi çifti için ağı değerlendir
for input_pair in test_input_pairs:
x1, x2 = input_pair # Girdi çiftini ayır
target_prob_0 = calculate_target_prob0(x1, x2) # Bu çift için hedef P(0)
count_0 = 0
# Her girdi çifti için N kez test et
for _ in range(NUM_TRIALS_PER_INPUT_PAIR):
try:
# Ağı iki girdi ile aktive et
output = net.activate(input_pair)
# Ağın çıktısını yorumla (< 0.5 ise 0)
if output[0] < 0.5:
count_0 += 1
except Exception as e:
logger.warning(f"Genome {genome_id}, Input {input_pair} ağ aktivasyonunda hata: {e}")
pass
# Gözlemlenen olasılığı hesapla
if NUM_TRIALS_PER_INPUT_PAIR > 0:
observed_prob_0 = count_0 / NUM_TRIALS_PER_INPUT_PAIR
else:
observed_prob_0 = 0.5
# Bu girdi çifti için hatanın karesini hesapla
error = (observed_prob_0 - target_prob_0) ** 2
total_squared_error += error
# Ortalama Karesel Hatayı (Mean Squared Error - MSE) hesapla
average_squared_error = total_squared_error / len(test_input_pairs)
# Fitness'ı hesapla (1 - RMSE)
fitness = max(0.0, 1.0 - math.sqrt(average_squared_error))
genome.fitness = fitness
# logger.info(f"Genome {genome_id}: AvgError^2 = {average_squared_error:.4f}, Fitness = {fitness:.4f}")
# --- NEAT Çalıştırma Fonksiyonu ---
def run_neat(config_file):
"""
NEAT evrimini başlatır ve yönetir.
"""
logger.info(f"NEAT yapılandırması yükleniyor: {config_file}")
try:
# V3 config dosyasını kullanarak config nesnesini oluştur
config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
neat.DefaultSpeciesSet, neat.DefaultStagnation,
config_file) # <-- config_file şimdi v3 dosyasını gösteriyor
config.fitness_threshold = FITNESS_THRESHOLD
logger.info(f"Yapılandırma yüklendi. Giriş Sayısı: {config.genome_config.num_inputs}, Fitness Eşiği: {config.fitness_threshold}")
except Exception as e:
logger.critical(f"Yapılandırma dosyası yüklenemedi veya geçersiz: {config_file} - Hata: {e}")
return None
logger.info("Yeni popülasyon oluşturuluyor...")
p = neat.Population(config)
# Raporlayıcılar
p.add_reporter(neat.StdOutReporter(True))
# Checkpoint
checkpoint_prefix = 'neat-checkpoint-v3-' # <-- V3 için prefix
p.add_reporter(neat.Checkpointer(15, filename_prefix=checkpoint_prefix)) # Daha seyrek kaydedilebilir
logger.info(f"Checkpoint dosyaları '{checkpoint_prefix}*' olarak kaydedilecek.")
logger.info(f"Evrim başlıyor (Maksimum {MAX_GENERATIONS} nesil)...")
try:
winner = p.run(eval_genomes, MAX_GENERATIONS)
logger.info(' ' + "="*20 + " Evrim Tamamlandı " + "="*20)
except Exception as e:
logger.critical(f"Evrim sırasında kritik bir hata oluştu: {e}")
return None
# En iyi genomu işle
if winner:
logger.info(f'En iyi genom bulundu (Fitness: {winner.fitness:.6f}):')
logger.info(f' {winner}')
# En iyi genomu kaydet
winner_filename = "winner_genome_v3.pkl" # <-- V3 için dosya adı
try:
with open(winner_filename, 'wb') as f:
pickle.dump(winner, f)
logger.info(f"En iyi genom '{winner_filename}' dosyasına başarıyla kaydedildi.")
except Exception as e:
logger.error(f"En iyi genom kaydedilemedi: {e}")
# --- YENİ: En İyi Genomu Çoklu Girdi İle Detaylı Test Etme ---
logger.info(" " + "="*20 + " En İyi Genom Detaylı Testi (Çoklu Girdi) " + "="*20)
try:
winner_net = neat.nn.FeedForwardNetwork.create(winner, config)
test_trials_final = 500 # Final test için deneme sayısı
logger.info(f"En iyi ağ, farklı girdi çiftleriyle {test_trials_final} kez test ediliyor...")
# Daha hassas bir girdi ızgarası oluşturalım (örn: 5x5 = 25 nokta)
final_axis_values = np.linspace(0.0, 1.0, 5)
final_test_pairs = list(itertools.product(final_axis_values, repeat=2))
final_total_error_sq = 0.0
logger.info(f"{'Input (x1,x2)': <15} {'Target P(0)': <12} {'Observed P(0)': <14} {'Error^2': <10}")
logger.info("-" * 60)
for input_pair in final_test_pairs:
x1, x2 = input_pair
target_p0 = calculate_target_prob0(x1, x2)
count_0 = 0
for _ in range(test_trials_final):
output = winner_net.activate(input_pair)
if output[0] < 0.5:
count_0 += 1
observed_p0 = count_0 / test_trials_final
error_sq = (observed_p0 - target_p0) ** 2
final_total_error_sq += error_sq
logger.info(f"({x1:.2f}, {x2:.2f}) {target_p0:<12.3f} {observed_p0:<14.3f} {error_sq:<10.5f}")
final_avg_error_sq = final_total_error_sq / len(final_test_pairs)
final_rmse = math.sqrt(final_avg_error_sq)
logger.info("-" * 60)
logger.info(f"Final Ortalama Karesel Hata (MSE): {final_avg_error_sq:.6f}")
logger.info(f"Final Kök Ortalama Karesel Hata (RMSE): {final_rmse:.6f}")
logger.info(f"Final Fitness Yaklaşımı (1 - RMSE): {1.0 - final_rmse:.6f}")
logger.info("-" * 60)
except Exception as e:
logger.error(f"En iyi genom test edilirken hata oluştu: {e}")
else:
logger.warning("Test edilecek bir kazanan genom bulunamadı.")
logger.info("="*50)
logger.info("Quanta Simülatörü Adım 3 (Çoklu Girdi) tamamlandı.")
logger.info("="*50)
return winner
if __name__ == '__main__':
# V3 için yeni yapılandırma dosyasının yolu
local_dir = os.path.dirname(__file__)
config_path = os.path.join(local_dir, 'config-feedforward-v3.txt') # <-- YENİ Config Dosyası
if not os.path.exists(config_path):
logger.critical(f"Yapılandırma dosyası bulunamadı: {config_path}")
logger.critical("Lütfen 'config-feedforward-v3.txt' dosyasını Python betiğiyle aynı klasöre koyun.")
else:
# NEAT'i çalıştır
run_neat(config_path)