import gradio as gr from fastai.vision.all import * import torch import torch.nn.functional as F import numpy as np import cv2 from torchvision.transforms.functional import to_pil_image, to_tensor # --- Model ve Learner Yükleme --- # model.pkl dosyasının app.py ile aynı dizinde olduğundan emin olun # veya Hugging Face Spaces'e yüklerken doğru yolda olduğundan. try: learn = load_learner("pneumonia_detector_resnet50.pkl", cpu=True) # CPU üzerinde çalıştır, Spaces genellikle CPU kullanır print("Model başarıyla yüklendi.") except Exception as e: print(f"Model yüklenirken hata oluştu: {e}") learn = None # --- Grad-CAM için Hook ve Yardımcı Fonksiyonlar --- # Bu kısım notebook'taki mantığa benzer olacak # Ancak Gradio'nun state'siz doğası için biraz daha dikkatli olmalıyız. # Her tahmin için hook'ları yeniden oluşturup kaldırmak yerine, # modeli bir kere yükleyip, hook'ları global olarak tutabiliriz. # Ya da daha basit bir yaklaşım olarak her predict çağrısında hook'ları ayarlayabiliriz. # Aşağıdaki yaklaşım, her çağrıda hook'ları yönetir, bu da state sorunlarını önler. class GradCAMHooks: def __init__(self, model, target_layer): self.model = model self.target_layer = target_layer self.activations = None self.gradients = None self._register_hooks() def _save_activation(self, module, input, output): self.activations = output.detach() def _save_gradient(self, module, grad_input, grad_output): self.gradients = grad_output[0].detach() def _register_hooks(self): self.forward_handle = self.target_layer.register_forward_hook(self._save_activation) self.backward_handle = self.target_layer.register_full_backward_hook(self._save_gradient) # full_backward_hook def remove_hooks(self): if hasattr(self, 'forward_handle') and self.forward_handle: self.forward_handle.remove() if hasattr(self, 'backward_handle') and self.backward_handle: self.backward_handle.remove() def generate_grad_cam(learn_instance, pil_image, class_idx_target=None): if learn_instance is None: return None, "Model yüklenemedi." # Görüntüyü fastai tensörüne dönüştür img_tensor = tensor(pil_image).permute(2,0,1).float()/255. # fastai'nin dls.after_item ve dls.after_batch transformasyonlarını uygulamak önemli olabilir # Şimdilik basit bir normalizasyon varsayalım veya model zaten normalize veriyle eğitilmişse # Bu kısım modelinizin eğitim şekline göre ayarlanmalı # Basit bir örnek olarak, dls.after_item ve dls.after_batch'i manuel olarak uygulamayı deneyebiliriz # Veya learn.dls.test_dl([pil_image]).one_batch()[0] kullanabiliriz. # Daha sağlam bir yol: dl = learn_instance.dls.test_dl([pil_image], num_workers=0) # num_workers=0 önemlidir x, = dl.one_batch() # x zaten doğru şekilde işlenmiş olacak # Hedef katmanı al (ResNet50 için) try: # FastAI'nin XResNet gibi modellerinde model[0] backbone'dur. # Son konvolüsyonel blok genellikle [-1] veya [-2]'dir. # Ve bu bloğun içindeki son konvolüsyonel katman. # Bu, model mimarinize göre değişebilir. learn.model[0].summary() ile kontrol edin. target_layer = learn_instance.model[0][-1][-1].conv3 # ResNet50 için yaygın bir hedef if not isinstance(target_layer, nn.Conv2d): # Bazen .conv3 yerine sadece blok olabilir target_layer = learn_instance.model[0][-1][-1] # Son bottleneck blok while not isinstance(target_layer, nn.Conv2d) and len(list(target_layer.children())) > 0: target_layer = target_layer[-1] # Son modülü al, conv olana kadar in if not isinstance(target_layer, nn.Conv2d): # Hala conv değilse, son bloğu al target_layer = learn_instance.model[0][-1][-1] except Exception as e: print(f"Grad-CAM için hedef katman alınırken hata: {e}") return None, "Hedef katman bulunamadı." cam_hooks = GradCAMHooks(learn_instance.model, target_layer) learn_instance.model.eval() # Değerlendirme modu learn_instance.model.zero_grad() output = learn_instance.model(x) # x zaten GPU'da olabilir, değilse .cuda() ekleyin if class_idx_target is None: class_idx_target = output.argmax(dim=1).item() score = output[0, class_idx_target] score.backward() gradients = cam_hooks.gradients activations = cam_hooks.activations cam_hooks.remove_hooks() # Hook'ları hemen kaldır if gradients is None or activations is None: return None, "Aktivasyon veya gradyan alınamadı." pooled_gradients = torch.mean(gradients, dim=[0, 2, 3]) # Gradyanları havuzla # Kanal ağırlıklarını aktivasyonlara uygula for i in range(activations.shape[1]): # aktivasyonlar (batch, channels, H, W) şeklinde activations[0, i, :, :] *= pooled_gradients[i] heatmap = torch.mean(activations, dim=1).squeeze().cpu().numpy() # Tek kanala indir heatmap = np.maximum(heatmap, 0) # ReLU if np.max(heatmap) > 0: heatmap /= np.max(heatmap) # Normalize et else: # Eğer max heatmap 0 ise (nadiren olur, ama gradyanlar/aktivasyonlar hep sıfırsa) heatmap = np.zeros_like(heatmap) # Heatmap'i orijinal görüntü boyutuna getir ve renklendir original_img_np = np.array(pil_image) heatmap_resized_color = cv2.applyColorMap(np.uint8(255 * heatmap), cv2.COLORMAP_JET) heatmap_resized_color = cv2.resize(heatmap_resized_color, (original_img_np.shape[1], original_img_np.shape[0])) # Görüntüleri birleştir superimposed_img = cv2.addWeighted(original_img_np, 0.6, heatmap_resized_color, 0.4, 0) superimposed_pil = Image.fromarray(superimposed_img) return superimposed_pil, None # --- Gradio Tahmin Fonksiyonu --- def predict_image(input_img): if learn is None: return {"Hata": "Model yüklenemedi"}, None try: # FastAI PILImage'e çevir img_pil = PILImage.create(input_img) # Tahmin yap pred_class, pred_idx, probs = learn.predict(img_pil) # PNEUMONIA sınıfının olasılığını al (vocablara göre) pneumonia_idx = learn.dls.vocab.o2i['PNEUMONIA'] # Sonuçları formatla confidences = {learn.dls.vocab[i]: float(probs[i]) for i in range(len(probs))} # Grad-CAM oluştur grad_cam_img, cam_error = generate_grad_cam(learn, img_pil, class_idx_target=pred_idx.item()) if cam_error: print(f"Grad-CAM hatası: {cam_error}") # Hata durumunda orijinal görüntüyü veya boş bir görüntü döndür grad_cam_img = Image.new('RGB', img_pil.size, (200, 200, 200)) return confidences, grad_cam_img except Exception as e: print(f"Tahmin sırasında hata: {e}") import traceback traceback.print_exc() return {"Hata": str(e)}, None # --- Gradio Arayüzü --- iface = gr.Interface( fn=predict_image, inputs=gr.Image(type="pil", label="Akciğer Röntgeni Yükleyin"), outputs=[ gr.Label(num_top_classes=2, label="Tahminler"), gr.Image(type="pil", label="Grad-CAM Görselleştirmesi") ], title="Akciğer X-Ray Zatürre Tespiti", description="Bir akciğer röntgeni yükleyerek zatürre olup olmadığını tahmin edin. Model ayrıca kararını verirken görüntünün hangi bölgelerine odaklandığını gösteren bir Grad-CAM haritası da üretecektir.", examples=[ # Örnek dosyaların app.py ile aynı dizinde veya alt klasörde olduğundan emin olun # Örneğin, "ornek_goruntuler/NORMAL_ornek.jpeg" # Bu örnekleri kendi veri setinizden alıp ekleyebilirsiniz. # ["ornek_goruntuler/NORMAL_sample.jpeg"], # ["ornek_goruntuler/PNEUMONIA_sample.jpeg"] # Eğer örnek dosya yolları çalışmazsa, bu satırları yorum satırı yapın. ], allow_flagging="never" ) if __name__ == '__main__': if learn is not None: iface.launch() else: print("Model yüklenemediği için Gradio uygulaması başlatılamıyor.")