# quanta_classifier_v6.py import neat import numpy as np import os import logging import pickle import datetime import traceback # --- YENİ: Scikit-learn importları --- from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.metrics import accuracy_score, classification_report, confusion_matrix # --- Loglama Ayarları --- log_filename = f"quanta_log_v6_classifier_{datetime.datetime.now():%Y%m%d_%H%M%S}.log" 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("="*70) logger.info("Quanta Classifier Başlatılıyor (Sürüm 6 - Veri Seti ile Sınıflandırma)") logger.info(f"Başlangıç Zamanı: {datetime.datetime.now()}") logger.info("="*70) # --- Global Değişkenler (Veri Seti) --- # Not: Global değişkenler büyük projelerde önerilmez, ancak NEAT'in # fitness fonksiyonuna kolayca veri aktarmak için bu örnekte kullanılmıştır. # Daha büyük projelerde sınıf yapısı veya functools.partial tercih edilebilir. X_train, X_test, y_train, y_test = None, None, None, None # --- Veri Seti Oluşturma ve Hazırlama --- def prepare_data(n_samples=500, n_features=2, n_informative=2, n_redundant=0, random_state=42): """ Scikit-learn kullanarak sentetik bir sınıflandırma veri seti oluşturur ve böler. """ global X_train, X_test, y_train, y_test logger.info(f"{n_samples} örnekli, {n_features} özellikli sentetik veri seti oluşturuluyor...") X, y = make_classification( n_samples=n_samples, n_features=n_features, n_informative=n_informative, n_redundant=n_redundant, n_clusters_per_class=1, # Sınıf başına küme sayısı flip_y=0.05, # Gürültü ekle (etiketlerin %5'ini ters çevir) random_state=random_state ) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.3, random_state=random_state, stratify=y # Sınıf oranlarını koru ) logger.info(f"Veri seti bölündü: {len(X_train)} eğitim, {len(X_test)} test örneği.") logger.info(f"Girdi Boyutu (Özellik Sayısı): {X_train.shape[1]}") logger.info(f"Sınıf Dağılımı (Eğitim): {np.bincount(y_train)}") # --- NEAT Fitness Fonksiyonu (Sınıflandırma için) --- def eval_genomes_classification(genomes, config): """ Genomları eğitim verisi üzerindeki sınıflandırma doğruluğuna göre değerlendirir. """ global X_train, y_train # Global eğitim verisini kullan if X_train is None or y_train is None: logger.error("Eğitim verisi yüklenmemiş! Fitness hesaplanamıyor.") # Tüm genomlara düşük fitness ver for genome_id, genome in genomes: genome.fitness = -1.0 return for genome_id, genome in genomes: genome.fitness = 0.0 # Başlangıç fitness try: net = neat.nn.FeedForwardNetwork.create(genome, config) except Exception as e: logger.error(f"Genome {genome_id} ağ oluşturma hatası: {e}") genome.fitness = -1.0 # Ceza continue correct_predictions = 0 # Eğitim verisi üzerinde tahmin yap for xi, yi in zip(X_train, y_train): try: output = net.activate(xi) # Binary sınıflandırma: Tek sigmoid çıkışı 0.5 eşiği ile yorumla prediction = 1 if output[0] >= 0.5 else 0 if prediction == yi: correct_predictions += 1 except Exception as e: # logger.warning(f"G:{genome_id} Aktivasyon hatası: {e}") pass # Hatalı aktivasyonları sayma # Fitness = Doğruluk Oranı (Accuracy) accuracy = correct_predictions / len(y_train) genome.fitness = accuracy # logger.debug(f"Genome {genome_id}: Accuracy = {accuracy:.4f}, Fitness = {genome.fitness:.4f}") # --- NEAT Çalıştırma Fonksiyonu --- def run_neat(config_file, generations=100): """ NEAT evrimini başlatır, yönetir ve sonucu değerlendirir. """ logger.info(f"NEAT yapılandırması yükleniyor: {config_file}") config = None try: config = neat.Config(neat.DefaultGenome, neat.DefaultReproduction, neat.DefaultSpeciesSet, neat.DefaultStagnation, config_file) logger.info(f"Yapılandırma: Giriş={config.genome_config.num_inputs}, Çıkış={config.genome_config.num_outputs}, Pop={config.pop_size}, Eşik={config.fitness_threshold}") # Yapılandırmadaki girdi sayısı ile veri setinin özellik sayısını kontrol et if config.genome_config.num_inputs != X_train.shape[1]: logger.error(f"Yapılandırma hatası: Config'deki girdi sayısı ({config.genome_config.num_inputs}) veri özelliği sayısı ({X_train.shape[1]}) ile eşleşmiyor!") return None # Binary sınıflandırma için çıktı sayısını kontrol et if config.genome_config.num_outputs != 1: logger.warning(f"Yapılandırma uyarısı: Binary sınıflandırma için çıktı sayısı ({config.genome_config.num_outputs}) genellikle 1 olmalıdır.") except Exception as e: logger.critical(f"Yapılandırma dosyası yüklenemedi: {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)) # Konsola detaylı raporlama stats = neat.StatisticsReporter() # İstatistikleri toplar (ileride görselleştirme için) p.add_reporter(stats) checkpoint_prefix = 'neat-checkpoint-v6-' p.add_reporter(neat.Checkpointer(generation_interval=20, filename_prefix=checkpoint_prefix)) # 20 nesilde bir checkpoint logger.info(f"Checkpoint dosyaları '{checkpoint_prefix}*' olarak kaydedilecek.") logger.info(f"Evrim başlıyor (Maksimum {generations} nesil)...") winner = None try: # Fitness fonksiyonunu (eval_genomes_classification) çalıştır winner = p.run(eval_genomes_classification, generations) logger.info(' ' + "="*30 + " Evrim Tamamlandı " + "="*30) except Exception as e: logger.critical(f"Evrim sırasında kritik bir hata oluştu: {e}") logger.error(traceback.format_exc()) if not winner and p and p.best_genome: logger.warning("Evrim hata ile durdu, bulunan son en iyi genom kullanılıyor.") winner = p.best_genome # En iyi genomu işle if winner: logger.info(f'En iyi genom bulundu/kullanıldı (Fitness - Eğitim Doğruluğu: {winner.fitness:.6f}):') num_nodes = len(winner.nodes) num_connections = len(winner.connections) logger.info(f'Kazanan Karmaşıklığı: {num_nodes} Düğüm, {num_connections} Bağlantı') # En iyi genomu kaydet winner_filename = "winner_classifier_v6.pkl" 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 Genomun TEST SETİ Üzerinde Değerlendirilmesi --- logger.info(" " + "="*20 + " En İyi Genomun Test Seti Performansı " + "="*20) try: if config is None: logger.error("Yapılandırma yüklenemediği için final test atlanıyor.") raise RuntimeError("Config object is None.") winner_net = neat.nn.FeedForwardNetwork.create(winner, config) # Test seti üzerinde tahmin yap y_pred = [] logger.info(f"Test seti ({len(X_test)} örnek) üzerinde tahminler yapılıyor...") for xi in X_test: output = winner_net.activate(xi) prediction = 1 if output[0] >= 0.5 else 0 y_pred.append(prediction) # Performans metriklerini hesapla ve raporla accuracy_test = accuracy_score(y_test, y_pred) conf_matrix = confusion_matrix(y_test, y_pred) class_report = classification_report(y_test, y_pred) logger.info(f"\n--- Test Seti Sonuçları ---") logger.info(f"Doğruluk (Accuracy): {accuracy_test:.4f}") logger.info(f"\nKarışıklık Matrisi (Confusion Matrix):\n{conf_matrix}") logger.info(f"\nSınıflandırma Raporu:\n{class_report}") logger.info("---------------------------\n") except Exception as e: logger.error(f"En iyi genom test edilirken hata oluştu: {e}") logger.error(traceback.format_exc()) else: logger.warning("Evrim sonunda test edilecek bir kazanan genom bulunamadı.") logger.info("="*70) logger.info("Quanta Classifier Adım 6 (Veri Seti ile Sınıflandırma) tamamlandı.") logger.info(f"Bitiş Zamanı: {datetime.datetime.now()}") logger.info("="*70) # return winner if __name__ == '__main__': # Betiğin bulunduğu dizini al local_dir = os.path.dirname(os.path.abspath(__file__)) # V6 config dosyasının tam yolunu oluştur config_path = os.path.join(local_dir, 'config-classification-v6.txt') if not os.path.exists(config_path): logger.critical(f"Yapılandırma dosyası bulunamadı: {config_path}") else: try: # 1. Veri setini hazırla prepare_data(n_samples=1000, n_features=2, n_informative=2, random_state=123) # 2. NEAT evrimini çalıştır (Config'deki eşiğe ulaşana kadar veya max nesil) # MAX_GENERATIONS sabitini burada da tanımlayabiliriz: MAX_GENERATIONS_RUN = 150 # Deneme için 150 nesil, gerekirse artırılır run_neat(config_path, generations=MAX_GENERATIONS_RUN) except ImportError as ie: logger.critical(f"Gerekli kütüphane bulunamadı: {ie}") logger.critical("Lütfen 'pip install scikit-learn numpy neat-python' komutu ile kurun.") except Exception as main_e: logger.critical(f"Ana program akışında beklenmedik bir hata oluştu: {main_e}") logger.error(traceback.format_exc())