File size: 10,790 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_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()) |