File size: 10,470 Bytes
8e35b08 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 |
# 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) |