Cicikush's picture
Update app.py
82d1a16 verified
raw
history blame
8.31 kB
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.")