Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import torch | |
| import torch.nn as nn | |
| import pennylane as qml | |
| import numpy as np | |
| import time | |
| import pickle | |
| from PIL import Image | |
| import torchvision.transforms as transforms | |
| import torchvision.models as models | |
| import plotly.graph_objects as go | |
| import os | |
| # ── Sayfa ayarları ────────────────────────────────────────── | |
| st.set_page_config( | |
| page_title="QuantumCare — Akıllı Teşhis", | |
| page_icon="⚛️", | |
| layout="wide" | |
| ) | |
| # ── Sabitler ───────────────────────────────────────────────── | |
| MODEL_DIR = "." | |
| DEVICE = torch.device("cpu") | |
| IMAGENET_MEAN = [0.485, 0.456, 0.406] | |
| IMAGENET_STD = [0.229, 0.224, 0.225] | |
| transform = transforms.Compose([ | |
| transforms.Resize((224, 224)), | |
| transforms.ToTensor(), | |
| transforms.Normalize(IMAGENET_MEAN, IMAGENET_STD), | |
| ]) | |
| # ── Model tanımları ────────────────────────────────────────── | |
| def make_qnode(n_qubits=4, n_layers=2): | |
| dev = qml.device("default.qubit", wires=n_qubits) | |
| def circuit(inputs, weights): | |
| for i in range(n_qubits): | |
| qml.RY(inputs[..., i], wires=i) | |
| for layer_idx in range(n_layers): | |
| qml.StronglyEntanglingLayers( | |
| weights[layer_idx:layer_idx+1], | |
| wires=range(n_qubits)) | |
| return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)] | |
| return circuit | |
| class HybridQuantumModel(nn.Module): | |
| def __init__(self, in_dim=512, n_qubits=4, n_layers=2): | |
| super().__init__() | |
| self.n_qubits = n_qubits | |
| self.n_layers = n_layers | |
| self.projection = nn.Linear(in_dim, n_qubits) | |
| self.bn = nn.BatchNorm1d(n_qubits) | |
| qnode = make_qnode(n_qubits, n_layers) | |
| weight_shapes = {"weights": (n_layers, n_qubits, 3)} | |
| self.q_layer = qml.qnn.TorchLayer(qnode, weight_shapes) | |
| self.head = nn.Linear(n_qubits, 1) | |
| def forward(self, x): | |
| x = self.projection(x) | |
| x = self.bn(x) | |
| x = self.q_layer(x) | |
| return self.head(x).squeeze(-1) | |
| class ClassicalMLP(nn.Module): | |
| def __init__(self, in_dim=512): | |
| super().__init__() | |
| self.net = nn.Sequential( | |
| nn.Linear(in_dim, 64), nn.ReLU(), nn.Dropout(0.3), | |
| nn.Linear(64, 16), nn.ReLU(), nn.Dropout(0.2), | |
| nn.Linear(16, 1) | |
| ) | |
| def forward(self, x): | |
| return self.net(x).squeeze(-1) | |
| # ── Model yükleme (cache ile) ──────────────────────────────── | |
| def load_models(): | |
| # ResNet18 | |
| extractor = models.resnet18(weights=None) | |
| extractor.fc = nn.Identity() | |
| extractor.load_state_dict( | |
| torch.load(f"{MODEL_DIR}/resnet18_extractor.pth", | |
| map_location=DEVICE)) | |
| extractor.eval() | |
| # Klasik MLP | |
| classical = ClassicalMLP() | |
| ckpt = torch.load(f"{MODEL_DIR}/classical_mlp.pth", | |
| map_location=DEVICE) | |
| classical.load_state_dict(ckpt['model_state_dict']) | |
| classical.eval() | |
| # Hibrit Kuantum | |
| hybrid = HybridQuantumModel() | |
| ckpt2 = torch.load(f"{MODEL_DIR}/hybrid_quantum.pth", | |
| map_location=DEVICE) | |
| hybrid.load_state_dict(ckpt2['model_state_dict']) | |
| hybrid.eval() | |
| # Scaler | |
| with open(f"{MODEL_DIR}/scaler.pkl", "rb") as f: | |
| sc = pickle.load(f) | |
| return extractor, classical, hybrid, sc | |
| # ── Tahmin fonksiyonu ──────────────────────────────────────── | |
| def predict(img_pil, extractor, model, scaler): | |
| t0 = time.time() | |
| x = transform(img_pil).unsqueeze(0) | |
| with torch.no_grad(): | |
| feat = extractor(x).numpy() | |
| feat_scaled = scaler.transform(feat) | |
| feat_tensor = torch.tensor(feat_scaled, dtype=torch.float32) | |
| with torch.no_grad(): | |
| logit = model(feat_tensor) | |
| prob = torch.sigmoid(logit).item() | |
| elapsed = time.time() - t0 | |
| return prob, elapsed | |
| # ── Uygulama ───────────────────────────────────────────────── | |
| def main(): | |
| # Başlık | |
| st.markdown(""" | |
| <div style='text-align:center; padding:20px 0'> | |
| <h1 style='color:#9B59B6; font-size:2.5em'>⚛️ QuantumCare</h1> | |
| <p style='font-size:1.2em; color:#666'> | |
| Hibrit Kuantum-Klasik Akciğer Röntgeni Analizi | |
| </p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| # Modelleri yükle | |
| with st.spinner("Modeller yükleniyor..."): | |
| extractor, classical, hybrid, scaler = load_models() | |
| st.success("✅ Modeller hazır!") | |
| st.markdown("---") | |
| # ── Sol: Görüntü yükleme ────────────────────────────── | |
| col_left, col_right = st.columns([1, 2]) | |
| with col_left: | |
| st.subheader("📂 Görüntü Seç") | |
| # Hazır örnek görüntüler | |
| sample_dir = "sample_images" | |
| samples = [] | |
| if os.path.exists(sample_dir): | |
| samples = [f for f in os.listdir(sample_dir) | |
| if f.endswith((".jpg",".jpeg",".png"))] | |
| mode = st.radio("Kaynak:", ["Hazır örnekler", "Kendi görüntün"]) | |
| img_pil = None | |
| if mode == "Hazır örnekler" and samples: | |
| chosen = st.selectbox("Örnek seç:", samples) | |
| img_pil = Image.open( | |
| f"{sample_dir}/{chosen}").convert("RGB") | |
| st.image(img_pil, caption=chosen, use_column_width=True) | |
| else: | |
| uploaded = st.file_uploader( | |
| "X-Ray yükle", type=["jpg","jpeg","png"]) | |
| if uploaded: | |
| img_pil = Image.open(uploaded).convert("RGB") | |
| st.image(img_pil, caption="Yüklenen görüntü", | |
| use_column_width=True) | |
| analyze = st.button("🔬 Analiz Et", type="primary", | |
| disabled=(img_pil is None), | |
| use_container_width=True) | |
| # ── Sağ: Sonuçlar ──────────────────────────────────── | |
| with col_right: | |
| if img_pil and analyze: | |
| st.subheader("📊 Karşılaştırmalı Analiz") | |
| # Her iki modeli çalıştır | |
| with st.spinner("Klasik model analiz ediyor..."): | |
| prob_cls, t_cls = predict( | |
| img_pil, extractor, classical, scaler) | |
| with st.spinner("Kuantum model analiz ediyor..."): | |
| prob_qnt, t_qnt = predict( | |
| img_pil, extractor, hybrid, scaler) | |
| label_cls = "🔴 PNEUMONİA" if prob_cls > 0.5 else "🟢 NORMAL" | |
| label_qnt = "🔴 PNEUMONİA" if prob_qnt > 0.5 else "🟢 NORMAL" | |
| # ── İki model yan yana ────────────────────── | |
| c1, c2 = st.columns(2) | |
| with c1: | |
| st.markdown(f""" | |
| <div style='background:#EBF5FB; border-radius:12px; | |
| padding:20px; text-align:center; | |
| border:2px solid #3498DB'> | |
| <h3 style='color:#3498DB'>Klasik AI Modeli</h3> | |
| <h1 style='font-size:2em'>{label_cls}</h1> | |
| <h2 style='color:#666'>%{prob_cls*100:.1f} olasılık</h2> | |
| <hr> | |
| <p>⏱️ Süre: <b>{t_cls*1000:.0f} ms</b></p> | |
| <p>💾 Model: <b>138 KB</b></p> | |
| <p>🔢 Parametre: <b>34,049</b></p> | |
| <p>⚡ Bellek: <b>~145 MB</b></p> | |
| <p>🌐 İnternet: <b>Gerekli</b></p> | |
| <p>💵 1K görüntü: <b>$0.45</b></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| with c2: | |
| st.markdown(f""" | |
| <div style='background:#F5EEF8; border-radius:12px; | |
| padding:20px; text-align:center; | |
| border:2px solid #9B59B6'> | |
| <h3 style='color:#9B59B6'>⚛️ Hibrit Kuantum</h3> | |
| <h1 style='font-size:2em'>{label_qnt}</h1> | |
| <h2 style='color:#666'>%{prob_qnt*100:.1f} olasılık</h2> | |
| <hr> | |
| <p>⏱️ Süre: <b>{t_qnt*1000:.0f} ms</b></p> | |
| <p>💾 Model: <b>12 KB</b></p> | |
| <p>🔢 Parametre: <b>2,089</b></p> | |
| <p>⚡ Bellek: <b>~11 MB</b></p> | |
| <p>🌐 İnternet: <b>Gerekmez ✅</b></p> | |
| <p>💵 1K görüntü: <b>$0.03</b></p> | |
| </div> | |
| """, unsafe_allow_html=True) | |
| st.markdown("---") | |
| # ── Maddi Etki Hesaplayıcı ─────────────────── | |
| st.subheader("💰 Yıllık Maliyet Tasarrufu Hesapla") | |
| goruntu_sayisi = st.slider( | |
| "Yıllık görüntü sayısı:", | |
| min_value=1000, | |
| max_value=10_000_000, | |
| value=100_000, | |
| step=1000, | |
| format="%d" | |
| ) | |
| klasik_maliyet = goruntu_sayisi * 0.00045 * 1000 | |
| kuantum_maliyet = goruntu_sayisi * 0.00003 * 1000 | |
| tasarruf = klasik_maliyet - kuantum_maliyet | |
| doktor_sayisi = int(tasarruf / 50000) | |
| m1, m2, m3, m4 = st.columns(4) | |
| m1.metric("Klasik AI Maliyeti", | |
| f"${klasik_maliyet:,.0f}", | |
| delta=None) | |
| m2.metric("Kuantum Maliyeti", | |
| f"${kuantum_maliyet:,.0f}", | |
| delta=f"-${klasik_maliyet-kuantum_maliyet:,.0f}", | |
| delta_color="inverse") | |
| m3.metric("Yıllık Tasarruf", | |
| f"${tasarruf:,.0f}", | |
| delta="15× daha ucuz") | |
| m4.metric("Tasarrufla finanse edilebilir", | |
| f"{doktor_sayisi} doktor", | |
| delta="Türkiye maaş ortalaması") | |
| # Pasta grafik | |
| fig = go.Figure(data=[go.Pie( | |
| labels=["Klasik AI Maliyeti", "Kuantum Tasarrufu"], | |
| values=[kuantum_maliyet, tasarruf], | |
| hole=0.4, | |
| marker_colors=["#3498DB", "#9B59B6"], | |
| textinfo="label+percent" | |
| )]) | |
| fig.update_layout( | |
| title=f"Yıllık {goruntu_sayisi:,} görüntü için maliyet dağılımı", | |
| height=350, | |
| margin=dict(t=40, b=0, l=0, r=0) | |
| ) | |
| st.plotly_chart(fig, use_container_width=True) | |
| st.markdown("---") | |
| # ── Klinik Etki ────────────────────────────── | |
| st.subheader("🏥 Klinik Etki") | |
| k1, k2, k3 = st.columns(3) | |
| k1.metric("Test setinde hasta kaçırma", | |
| "Klasik: 8 → Kuantum: 5", | |
| delta="%37 azalma", delta_color="inverse") | |
| k2.metric("AUC (Karar Güveni)", | |
| "0.947", | |
| delta="+0.019 vs Klasik") | |
| k3.metric("Kuantum çekirdeği boyutu", | |
| "144 byte", | |
| delta="Tweet'in 12'de 1") | |
| elif img_pil is None: | |
| st.info("👈 Sol taraftan bir X-Ray görüntüsü seçin " | |
| "veya yükleyin, ardından 'Analiz Et'e basın.") | |
| # ── Alt bilgi ──────────────────────────────────────────── | |
| st.markdown("---") | |
| st.markdown(""" | |
| <div style='text-align:center; color:#999; font-size:0.85em'> | |
| ⚠️ Bu uygulama araştırma amaçlıdır, klinik tanı için kullanılamaz. | | |
| ⚛️ PennyLane + PyTorch | ResNet18 + Variational Quantum Circuit | |
| </div> | |
| """, unsafe_allow_html=True) | |
| if __name__ == "__main__": | |
| main() |