Spaces:
Sleeping
Sleeping
| 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.") |