{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# HÜCRE 1: Konfigürasyon, Kütüphaneler ve Sabitler\n# ============================================================\nimport os\nimport random\nimport cv2\nimport pydicom\nimport numpy as np\nimport pandas as pd\nfrom tqdm import tqdm\nfrom sklearn.model_selection import train_test_split\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\nimport torch\nimport torch.nn as nn\nfrom torch.utils.data import Dataset, DataLoader\nfrom torch.optim import AdamW\nfrom torch.optim.lr_scheduler import CosineAnnealingLR\nimport timm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.35679Z","iopub.execute_input":"2026-09-15T14:04:55.357057Z","iopub.status.idle":"2026-09-15T14:04:55.36199Z","shell.execute_reply.started":"2026-09-15T14:04:55.357036Z","shell.execute_reply":"2026-09-15T14:04:55.361166Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# BT TARAMALARINDAKİ DICOMLAR 512X512 pixelmiş eğitim süresine bağlı değişebilir\nIMG_SIZE1 = 512\nIMG_SIZE = 384\n\nSEED = 42","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.363246Z","iopub.execute_input":"2026-09-15T14:04:55.36378Z","iopub.status.idle":"2026-09-15T14:04:55.381853Z","shell.execute_reply.started":"2026-09-15T14:04:55.363756Z","shell.execute_reply":"2026-09-15T14:04:55.38057Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kesinleşen yollar\nREAL_BASE = \"/kaggle/input/competitions/rsna-intracranial-hemorrhage-detection/rsna-intracranial-hemorrhage-detection\"\nLABELS_CSV = os.path.join(REAL_BASE, \"stage_2_train.csv\")\nTRAIN_DICOM_PATH = os.path.join(REAL_BASE, \"stage_2_train\")\nSAVE_DIR = \"/kaggle/working/processed_data_v384_webp\"\nIMG_DIR = os.path.join(SAVE_DIR, \"images\")\nos.makedirs(IMG_DIR, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.38293Z","iopub.execute_input":"2026-09-15T14:04:55.383205Z","iopub.status.idle":"2026-09-15T14:04:55.402486Z","shell.execute_reply.started":"2026-09-15T14:04:55.383182Z","shell.execute_reply":"2026-09-15T14:04:55.401472Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"LABELS = [\n    'epidural',\n    'intraparenchymal',\n    'intraventricular',\n    'subarachnoid',\n    'subdural',\n    'any'\n]\nSUBTYPES = ['epidural', 'intraparenchymal', 'intraventricular', 'subarachnoid', 'subdural']\nALL_LABELS = LABELS","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.405142Z","iopub.execute_input":"2026-09-15T14:04:55.405448Z","iopub.status.idle":"2026-09-15T14:04:55.419127Z","shell.execute_reply.started":"2026-09-15T14:04:55.405426Z","shell.execute_reply":"2026-09-15T14:04:55.418079Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# default seed conf\ndef seed_everything(seed):\n    random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    np.random.seed(seed)\n    torch.manual_seed(seed)\n    if torch.cuda.is_available():\n        torch.cuda.manual_seed_all(seed)\n\nseed_everything(SEED)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.420154Z","iopub.execute_input":"2026-09-15T14:04:55.420472Z","iopub.status.idle":"2026-09-15T14:04:55.442306Z","shell.execute_reply.started":"2026-09-15T14:04:55.420442Z","shell.execute_reply":"2026-09-15T14:04:55.441361Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# HÜCRE 2: Veri Ön Hazırlığı ve Dengeli 500 Görsel Seçimi\n# ============================================================\nprint(\"📊 CSV yükleniyor ve pivot tablosu hazırlanıyor...\")\n\n# CSV'yi okuma adımı (df tanımlandı)\ndf = pd.read_csv(LABELS_CSV)\n\n# idler böyle = ID_5c8b5d701_epidural -> id_no + tip diye 2ye böldük\n\n# 2. ID Parçalama (ID_xxxxxx_subtype -> Image: ID_xxxxxx, Type: subtype)\ndf['Image'] = df['ID'].apply(lambda x: '_'.join(x.split('_')[:2]))\ndf['Type'] = df['ID'].apply(lambda x: '_'.join(x.split('_')[2:]))\n\n# 3. Pivot Tablo (Her satıra bir görsel, her kanama tipine bir sütun)\nprint(\"🔄 Pivot tablosu oluşturuluyor...\")\ndf_pivot = df.pivot_table(\n    index=\"Image\", \n    columns=\"Type\", \n    values=\"Label\", \n    aggfunc=\"max\"\n).fillna(0).reset_index()\n\n# 4. 6 Sınıfın Eksiksiz Sıralaması\n# 'any' sütunu CSV'de yoksa alt tiplerden üret, varsa doğrudan kullan\nif 'any' not in df_pivot.columns:\n    df_pivot['any'] = (df_pivot[SUBTYPES].sum(axis=1) > 0).astype(int)\n\ndf_pivot = df_pivot[['Image'] + ALL_LABELS]\n\nprint(\"\\n✅ Pivot başarıyla tamamlandı!\")\nprint(f\"Toplam benzersiz kesit sayısı: {len(df_pivot):,}\")\nprint(df_pivot.head())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:04:55.443283Z","iopub.execute_input":"2026-09-15T14:04:55.443566Z","iopub.status.idle":"2026-09-15T14:05:04.809681Z","shell.execute_reply.started":"2026-09-15T14:04:55.443543Z","shell.execute_reply":"2026-09-15T14:05:04.808859Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"TARGET_TOTAL = 500\n\nfinal_list = []\n\n# 1. Her 5 kanama alt türünden 80'er pozitif örnek çek (Toplam 400)\nfor label in SUBTYPES:\n    type_df = df_pivot[df_pivot[label] == 1]\n    n_sample = min(80, len(type_df))\n    final_list.append(type_df.sample(n=n_sample, random_state=SEED))\n\n# 2. Hiçbir kanaması olmayan (tamamen sağlıklı) 100 vaka ekle\nnormal_cases = df_pivot[df_pivot[SUBTYPES].sum(axis=1) == 0].sample(n=100, random_state=SEED)\nfinal_list.append(normal_cases)\n\n# 3. Birleştir, mükerrer kesitleri düşür, karıştır ve 500'e sınırla\ndf_final = pd.concat(final_list).drop_duplicates(subset='Image')\n\n# Çoklu etiket kesişimleri yüzünden 500'ün altına düşerse rastgele tamamla\nif len(df_final) < TARGET_TOTAL:\n    remaining_needed = TARGET_TOTAL - len(df_final)\n    pool = df_pivot[~df_pivot['Image'].isin(df_final['Image'])]\n    fill_sample = pool.sample(n=remaining_needed, random_state=SEED)\n    df_final = pd.concat([df_final, fill_sample])\n\ndf_final = df_final.head(TARGET_TOTAL).sample(frac=1, random_state=SEED).reset_index(drop=True)\n\n# 4. %90 Train, %10 Val ayrımı\ntrain_df, val_df = train_test_split(df_final, test_size=0.10, random_state=SEED)\n\nprint(f\"✅ Toplam örnek: {len(df_final)}\")\nprint(f\"Train boyutu: {len(train_df)} | Val boyutu: {len(val_df)}\")\nprint(\"\\nSınıf Dağılımı (Tüm Seçilen Veride):\")\nprint(df_final[ALL_LABELS].sum())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:04.810777Z","iopub.execute_input":"2026-09-15T14:05:04.811035Z","iopub.status.idle":"2026-09-15T14:05:05.228746Z","shell.execute_reply.started":"2026-09-15T14:05:04.811005Z","shell.execute_reply":"2026-09-15T14:05:05.227209Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"CH1: Brain Window (Merkez 40, Genişlik 80)\n    Aralık: [0, 80] HU\n\n    Görevi: Beyin dokusunu (gri ve beyaz cevheri) en net gösteren penceredir. Doku içi ödemi, tümörleri ve parankim içi kanamaları (intraparenchymal) ortaya çıkarır.\n\n\nCH2: Subdural / Blood Window (Merkez 80, Genişlik 250)\n    Aralık: [-45, 205] HU\n\n    Görevi: Taze kan pıhtıları ile çevre yumuşak doku ve dura zarı arasındaki kontrastı maksimuma çıkarır. Özellikle kafatası sınırına yakın duran subdural, epidural ve sulkuslara sızan subarachnoid kanamaların parlamasını sağlar.\n    \n\nCH3: Bone Window (Merkez 600, Genişlik 2800)\n    Aralık: [-800, 2000] HU\n\n    Görevi: Beyin dokusunu tamamen siyaha düşürüp sadece kafatası kemiğini (skull) ve kemik kırıklarını odaklar. Epidural kanamalar sıklıkla kafatası kırığıyla birlikte görüldüğü için modelin kemik bütünlüğünü ve kafa sınırını referans almasını sağlar.\n\n\nASLINDA BU YÖNTEM İLE 1 FOTOĞRAFTAN 3 FARKLI DOKU ÖNE CIKARILIR.","metadata":{}},{"cell_type":"code","source":"# HÜCRE 3: ETL Fonksiyonları ve Diske Kayıt Döngüsü\n# ============================================================\n# (-1000(HAVA) ~ +3000(KEMİK DOKUSU))\n# KAN VE GRİ MADDE YAKLASIK AYNI DEĞERE SAHİP BU FARKI YAKALAMAK İÇİN\n\ndef window_image(img, center, width):\n    min_val = center - width // 2\n    max_val = center + width // 2\n    img = np.clip(img, min_val, max_val)\n    # İSTENİLEN DOKU DIŞI DOKULARIN KAPATILMASI\n    # gürültü, aykırı değerlerin ayıklanması\n    return (img - min_val) / (max_val - min_val) # [0,1] arası normallaştirme\n\ndef get_medical_stack(img):\n    # CH1: Brain (40, 80)\n    ch1 = window_image(img, 40, 80)\n    # CH2: Subdural (80, 250)\n    ch2 = window_image(img, 80, 250)\n    # CH3: Bone (600, 2800)\n    ch3 = window_image(img, 600, 2800)\n    stacked = np.stack([ch1, ch2, ch3], axis=-1)\n    return stacked.astype(np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:05.229984Z","iopub.execute_input":"2026-09-15T14:05:05.230354Z","iopub.status.idle":"2026-09-15T14:05:05.235866Z","shell.execute_reply.started":"2026-09-15T14:05:05.230291Z","shell.execute_reply":"2026-09-15T14:05:05.234994Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6. Fonksiyon ve Augmentation Tanımı\n# ============================================================\n# DÖNDÜRME SİMETRİ ZOOM İN-OUT ROTASYON VB İŞLEMLER \naugmenter_save_only = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.Affine(\n        translate_percent={\"x\": (-0.1, 0.1), \"y\": (-0.1, 0.1)},\n        scale=(0.9, 1.1),\n        rotate=(-15, 15),\n        p=0.8\n    ),\n])\n\ndef process_and_save(row, is_train=True):\n    img_id = row['Image'] \n    dcm_path = os.path.join(TRAIN_DICOM_PATH, f\"{img_id}.dcm\")\n    results = []\n    \n    try:\n        # TOMOGRAFİDEKİ YAYGIN HU ÖLCEĞİNE GEÇİŞ PARAMETRELERİ\n        dcm = pydicom.dcmread(dcm_path)\n        img = dcm.pixel_array.astype(np.float32)\n        \n        # Güvenli HU ölçekleme (Header kontrolleri ile)\n        slope = float(dcm.RescaleSlope) if hasattr(dcm, \"RescaleSlope\") else 1.0\n        intercept = float(dcm.RescaleIntercept) if hasattr(dcm, \"RescaleIntercept\") else 0.0\n        #FORMÜL\n        img = img * slope + intercept\n\n        # PENCERE VE BOYUTLANDIRMA (#1,2)\n        # 1. Windowing & Resize\n        img_stacked = cv2.resize(get_medical_stack(img), (IMG_SIZE, IMG_SIZE))\n        \n        # 2. 8-bit formata çevir (0-255)\n        img_to_save = (img_stacked * 255).astype(np.uint8)\n\n        # 3.  #ID_xxxxxx.webp FORMATINDA WEbP olrk kayıt eder (PNG GİBİ KALİTELİ VE HAFİF)\n        fname = f\"{img_id}.webp\"\n        cv2.imwrite(os.path.join(IMG_DIR, fname), img_to_save, [int(cv2.IMWRITE_WEBP_QUALITY), 95])\n        results.append({**row.to_dict(), 'file_path': fname})\n\n        # ÖRNEK SAYISI  AZ OLAN DATALARI ÇOĞALTMA\n        # 4. Nadir sınıf (epidural) için eğitim setinde offline augmentation\n        if is_train and row['epidural'] == 1:\n            for i in range(3):\n                aug_img = augmenter_save_only(image=img_to_save)['image'] \n                aug_fname = f\"{img_id}_epi_aug_{i}.webp\"\n                cv2.imwrite(os.path.join(IMG_DIR, aug_fname), aug_img, [int(cv2.IMWRITE_WEBP_QUALITY), 95])\n                results.append({**row.to_dict(), 'file_path': aug_fname})\n                \n        return results\n        #500 foto arasında bozuk olan varsa bos döndürüp bozmaz\n    except Exception as e:\n        return []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:05.237063Z","iopub.execute_input":"2026-09-15T14:05:05.237349Z","iopub.status.idle":"2026-09-15T14:05:05.266041Z","shell.execute_reply.started":"2026-09-15T14:05:05.237302Z","shell.execute_reply":"2026-09-15T14:05:05.264265Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 7. Görselleri İşleme ve Diske Kaydetme Döngüsü\n# ============================================================\n\n# 1. Train Setini İşleme\nfinal_train_meta = []\nprint(f\"🚀 Eğitim görselleri işleniyor ({len(train_df)} adet)...\")\n\nfor _, row in tqdm(train_df.iterrows(), total=len(train_df), desc=\"Train ETL\"):\n    res = process_and_save(row, is_train=True)\n    if res:\n        final_train_meta.extend(res)\n\n# 2. Validation Setini İşleme (Augmentation kapalı)\nfinal_val_meta = []\nprint(f\"\\n🧪 Doğrulama görselleri işleniyor ({len(val_df)} adet)...\")\n\nfor _, row in tqdm(val_df.iterrows(), total=len(val_df), desc=\"Val ETL\"):\n    res = process_and_save(row, is_train=False)\n    if res:\n        final_val_meta.extend(res)\n\n# 8. Nihai Meta Veri CSV'lerini Kaydetme\n# ============================================================\ntrain_ready_df = pd.DataFrame(final_train_meta)\nval_ready_df = pd.DataFrame(final_val_meta)\n\ntrain_csv_path = os.path.join(SAVE_DIR, \"train_ready.csv\")\nval_csv_path = os.path.join(SAVE_DIR, \"val_ready.csv\")\n\ntrain_ready_df.to_csv(train_csv_path, index=False)\nval_ready_df.to_csv(val_csv_path, index=False)\n\n# Raporlama\ntotal_disk_images = len(os.listdir(IMG_DIR))\nprint(\"\\n\" + \"=\" * 50)\nprint(\"✅ ETL İŞLEMİ BAŞARIYLA TAMAMLANDI!\")\nprint(f\"📁 Kayıt Klasörü         : {IMG_DIR}\")\nprint(f\"🖼️ Klasördeki Toplam Resim: {total_disk_images}\")\nprint(f\"📊 Train CSV Satır Sayısı : {len(train_ready_df)} (Augmentation dahil)\")\nprint(f\"📊 Val CSV Satır Sayısı   : {len(val_ready_df)}\")\nprint(\"=\" * 50)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:05.268637Z","iopub.execute_input":"2026-09-15T14:05:05.268905Z","iopub.status.idle":"2026-09-15T14:05:30.234219Z","shell.execute_reply.started":"2026-09-15T14:05:05.26888Z","shell.execute_reply":"2026-09-15T14:05:30.233545Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# HÜCRE 4: PyTorch Dataset ve DataLoader\n# ============================================================\nIMAGENET_MEAN = [0.485, 0.456, 0.406]\nIMAGENET_STD = [0.229, 0.224, 0.225]\n\ntrain_transforms = A.Compose([\n    A.HorizontalFlip(p=0.5),\n    A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.05, rotate_limit=15, p=0.6, border_mode=cv2.BORDER_CONSTANT),\n    A.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),\n    ToTensorV2()\n])\n\nval_transforms = A.Compose([\n    A.Normalize(mean=IMAGENET_MEAN, std=IMAGENET_STD),\n    ToTensorV2()\n])\n\nclass RSNADataset(Dataset):\n    def __init__(self, df, img_dir, targets, transforms=None):\n        self.df = df.reset_index(drop=True)\n        self.img_dir = img_dir\n        self.targets = targets\n        self.transforms = transforms\n        self.labels = self.df[self.targets].values.astype(np.float32)\n        self.file_paths = self.df['file_path'].values\n\n    def __len__(self):\n        return len(self.df)\n\n    def __getitem__(self, idx):\n        img_path = os.path.join(self.img_dir, self.file_paths[idx])\n        image = cv2.imread(img_path)\n        if image is None:\n            raise FileNotFoundError(f\"Görsel açılamadı: {img_path}\")\n            \n        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\n        if self.transforms:\n            augmented = self.transforms(image=image)\n            image = augmented['image']\n\n        label = torch.tensor(self.labels[idx], dtype=torch.float32)\n        return image, label\n\nBATCH_SIZE = 16\nNUM_WORKERS = 2\n\ntrain_dataset = RSNADataset(train_ready_df, IMG_DIR, ALL_LABELS, transforms=train_transforms)\nval_dataset = RSNADataset(val_ready_df, IMG_DIR, ALL_LABELS, transforms=val_transforms)\n\ntrain_loader = DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=NUM_WORKERS, pin_memory=True, drop_last=True)\nval_loader = DataLoader(val_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=NUM_WORKERS, pin_memory=True)\n\n# DataLoader Test Kontrolü\nsample_imgs, sample_labels = next(iter(train_loader))\nprint(\"✅ DataLoader başarıyla hazırlandı!\")\nprint(f\"Görsel Tensör Boyutu : {sample_imgs.shape}\")\nprint(f\"Etiket Tensör Boyutu : {sample_labels.shape}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:30.235096Z","iopub.execute_input":"2026-09-15T14:05:30.235378Z","iopub.status.idle":"2026-09-15T14:05:30.545873Z","shell.execute_reply.started":"2026-09-15T14:05:30.235349Z","shell.execute_reply":"2026-09-15T14:05:30.544965Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# HÜCRE 5: Model Mimarisi (CustomModel)\n# ============================================================\nclass CustomModel(nn.Module):\n    def __init__(self, model_name=\"tf_efficientnet_b0.ns_jft_in1k\", num_classes=6, pretrained=True):\n        super().__init__()\n        self.backbone = timm.create_model(\n            model_name, \n            pretrained=pretrained, \n            num_classes=0, \n            global_pool='avg'\n        )\n        n_features = self.backbone.num_features\n        \n        self.head = nn.Sequential(\n            nn.Linear(n_features, 512),\n            nn.BatchNorm1d(512),\n            nn.GELU(),\n            nn.Dropout(0.3),\n            nn.Linear(512, num_classes)  # 6 sınıf için ham logit değerleri\n        )\n\n    def forward(self, x):\n        features = self.backbone(x)\n        logits = self.head(features)\n        return logits\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\nmodel = CustomModel(model_name=\"tf_efficientnet_b0.ns_jft_in1k\", num_classes=6, pretrained=True).to(device)\n\nprint(f\"✅ Model ({device}) üzerinde kuruldu.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:30.546909Z","iopub.execute_input":"2026-09-15T14:05:30.547135Z","iopub.status.idle":"2026-09-15T14:05:30.781759Z","shell.execute_reply.started":"2026-09-15T14:05:30.547112Z","shell.execute_reply":"2026-09-15T14:05:30.780976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# HÜCRE 6: RSNA Weighted Loss ve Eğitim / Doğrulama Döngüsü\n# ============================================================\nEPOCHS = 5\nLR = 3e-4\nWEIGHT_DECAY = 1e-2\n\n# 6 hedef sınıf sırası: ['epidural', 'intraparenchymal', 'intraventricular', 'subarachnoid', 'subdural', 'any']\n# RSNA kuralı: 'any' sınıfı 2x ağırlıklı, diğer alt türler 1x\nclass_weights = torch.tensor([1.0, 1.0, 1.0, 1.0, 1.0, 2.0], device=device)\n\n# ============================================================\n# 2. RSNA Yarışmasına Özel Ağırlıklı BCE Kayıp Fonksiyonu\n# ============================================================\nclass RSNAWeightedLoss(nn.Module):\n    def __init__(self, weights):\n        super().__init__()\n        self.weights = weights\n        # reduction='none' ile her sınıfın kaybını tek tek alır, ağırlıkla çarparız\n        self.bce = nn.BCEWithLogitsLoss(reduction='none')\n\n    def forward(self, logits, targets):\n        loss = self.bce(logits, targets)\n        weighted_loss = loss * self.weights\n        return weighted_loss.mean()\n\n\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:30.782675Z","iopub.execute_input":"2026-09-15T14:05:30.782929Z","iopub.status.idle":"2026-09-15T14:05:30.788987Z","shell.execute_reply.started":"2026-09-15T14:05:30.782903Z","shell.execute_reply":"2026-09-15T14:05:30.788306Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"criterion = RSNAWeightedLoss(class_weights)\n\noptimizer = AdamW(model.parameters(), lr=LR, weight_decay=WEIGHT_DECAY)\nscheduler = CosineAnnealingLR(optimizer, T_max=EPOCHS, eta_min=1e-6)\n\n# ============================================================\n# 3. Eğitim ve Doğrulama Fonksiyonları\n# ============================================================\ndef train_one_epoch(model, loader, optimizer, criterion, device):\n    model.train()\n    running_loss = 0.0\n\n    for images, targets in tqdm(loader, desc=\"Training\", leave=False):\n        images = images.to(device, non_blocking=True)\n        targets = targets.to(device, non_blocking=True)\n\n        optimizer.zero_grad()\n        logits = model(images)\n        loss = criterion(logits, targets)\n        loss.backward()\n        optimizer.step()\n\n        running_loss += loss.item() * images.size(0)\n\n    return running_loss / len(loader.dataset)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:30.790088Z","iopub.execute_input":"2026-09-15T14:05:30.790355Z","iopub.status.idle":"2026-09-15T14:05:30.812667Z","shell.execute_reply.started":"2026-09-15T14:05:30.790302Z","shell.execute_reply":"2026-09-15T14:05:30.811736Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@torch.no_grad()\ndef validate(model, loader, criterion, device):\n    model.eval()\n    running_loss = 0.0\n\n    for images, targets in tqdm(loader, desc=\"Validation\", leave=False):\n        images = images.to(device, non_blocking=True)\n        targets = targets.to(device, non_blocking=True)\n\n        logits = model(images)\n        loss = criterion(logits, targets)\n\n        running_loss += loss.item() * images.size(0)\n\n    return running_loss / len(loader.dataset)\n\n# ============================================================\n# 4. Ana Eğitim Döngüsü\n# ============================================================\nbest_val_loss = float('inf')\nBEST_MODEL_PATH = os.path.join(SAVE_DIR, \"best_rsna_efficientnet.pth\")\n\nprint(f\"🚀 Eğitim Başlıyor! Toplam Epoch: {EPOCHS} | Cihaz: {device}\\n\")\n\nfor epoch in range(1, EPOCHS + 1):\n    train_loss = train_one_epoch(model, train_loader, optimizer, criterion, device)\n    val_loss = validate(model, val_loader, criterion, device)\n    scheduler.step()\n\n    print(f\"Epoch [{epoch:02d}/{EPOCHS:02d}] | Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f} | LR: {scheduler.get_last_lr()[0]:.6f}\")\n\n    # En iyi model ağırlığını kaydet\n    if val_loss < best_val_loss:\n        best_val_loss = val_loss\n        torch.save({\n            'epoch': epoch,\n            'model_state_dict': model.state_dict(),\n            'best_loss': best_val_loss,\n            'labels': ALL_LABELS\n        }, BEST_MODEL_PATH)\n        print(f\"  ⭐ En iyi model güncellendi ve kaydedildi! (Val Loss: {best_val_loss:.4f})\")\n\nprint(f\"\\n🎉 Eğitim tamamlandı! En iyi ağırlık: {BEST_MODEL_PATH}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:05:30.813743Z","iopub.execute_input":"2026-09-15T14:05:30.814037Z","iopub.status.idle":"2026-09-15T14:23:10.907545Z","shell.execute_reply.started":"2026-09-15T14:05:30.814007Z","shell.execute_reply":"2026-09-15T14:23:10.906287Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport torch\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\ndef evaluate_random_case_with_gt(val_df, img_dir, model, device, threshold=0.5):\n    # 1. Validation setinden rastgele 1 vaka seç\n    sample_row = val_df.sample(n=1).iloc[0]\n    img_name = sample_row['file_path']\n    img_path = os.path.join(img_dir, img_name)\n    \n    # 2. Görseli oku ve modele gönder\n    img_bgr = cv2.imread(img_path)\n    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)\n    \n    transformed = eval_transform(image=img_rgb)['image']\n    input_tensor = transformed.unsqueeze(0).to(device)\n    \n    with torch.no_grad():\n        logits = model(input_tensor)\n        probs = torch.sigmoid(logits).cpu().numpy()[0]\n    \n    # 3. Kıyaslama Tablosunu Hazırla\n    comparison_data = []\n    labels = ['epidural', 'intraparenchymal', 'intraventricular', 'subarachnoid', 'subdural', 'any']\n    \n    for i, label in enumerate(labels):\n        gt = int(sample_row[label])          # Gerçek değer (0 veya 1)\n        prob = probs[i]                      # Modelin verdiği olasılık\n        pred = int(prob >= threshold)        # Eşik değere göre tahmin (0 veya 1)\n        \n        # Karar durumu: True Positive, True Negative, False Positive, False Negative\n        if gt == 1 and pred == 1:\n            status = \"✅ BAŞARILI (Pozitif Yakalandı)\"\n        elif gt == 0 and pred == 0:\n            status = \"✅ DOĞRU (Negatif Onaylandı)\"\n        elif gt == 0 and pred == 1:\n            status = \"❌ YANLIŞ ALARM (False Positive)\"\n        else:\n            status = \"⚠️ KAÇIRILDI (False Negative)\"\n            \n        comparison_data.append({\n            \"Kanama Tipi\": label.upper(),\n            \"Gerçek (GT)\": gt,\n            \"Model Olasılığı\": f\"%{prob * 100:5.2f}\",\n            \"Model Kararı\": pred,\n            \"Sonuç\": status\n        })\n    \n    comp_df = pd.DataFrame(comparison_data)\n    \n    # 4. Raporu Yazdır\n    print(\"=\" * 70)\n    print(f\"🔬 VAKA ANALİZİ: {sample_row['Image']} ({img_name})\")\n    print(\"=\" * 70)\n    print(comp_df.to_string(index=False))\n    print(\"=\" * 70)\n    \n    # Genel Teşhis Uyumu\n    real_types = [l.upper() for l in SUBTYPES if sample_row[l] == 1]\n    pred_types = [labels[i].upper() for i in range(5) if probs[i] >= threshold]\n    \n    print(f\"📋 DOKTOR TEŞHİSİ : {' + '.join(real_types) if real_types else 'NORMAL (Sağlıklı)'}\")\n    print(f\"🤖 MODEL TAHMİNİ  : {' + '.join(pred_types) if pred_types else 'NORMAL (Sağlıklı)'}\")\n    \n    match = (set(real_types) == set(pred_types))\n    print(f\"🎯 TEŞHİS UYUMU   : {'KUSURSUZ EŞLEŞME' if match else 'FARK VAR'}\")\n    print(\"=\" * 70)\n\n    # 5. Tomografi Kesitini Çizdir\n    plt.figure(figsize=(6, 6))\n    plt.imshow(img_bgr[:, :, 0], cmap='gray')\n    title_color = 'green' if match else 'red'\n    plt.title(f\"GT: {', '.join(real_types) if real_types else 'NORMAL'}\\nPred: {', '.join(pred_types) if pred_types else 'NORMAL'}\", \n              color=title_color, fontsize=12, fontweight='bold')\n    plt.axis('off')\n    plt.show()\n\n# Çalıştırmak için:\nevaluate_random_case_with_gt(val_ready_df, IMG_DIR, infer_model, device, threshold=0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:53:34.958637Z","iopub.execute_input":"2026-09-15T14:53:34.958961Z","iopub.status.idle":"2026-09-15T14:53:35.15008Z","shell.execute_reply.started":"2026-09-15T14:53:34.958934Z","shell.execute_reply":"2026-09-15T14:53:35.148966Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ============================================================\n# HÜCRE: Çoklu Kanama (Multi-Bleed) Vakalarını Görselleştirme\n# ============================================================\nimport os\nimport cv2\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# 1. Yollar ve Veri Okuma\nVAL_CSV = os.path.join(SAVE_DIR, \"val_ready.csv\")\nval_df = pd.read_csv(VAL_CSV)\n\n# 2. Birden fazla kanama içeren vakaları filtrele (Komorbid / Multi-bleed)\n# Toplamı 1'den büyük olanlar birden fazla kanama türü taşır\nmulti_bleed_df = val_df[val_df[SUBTYPES].sum(axis=1) > 1].copy().reset_index(drop=True)\nprint(f\"📊 Validation setinde birden fazla kanama içeren toplam görsel: {len(multi_bleed_df)}\")\n\n# 3. İlk 5 Karmaşık Vakayı Çizdir\nnum_samples = min(5, len(multi_bleed_df))\n\nif num_samples > 0:\n    plt.figure(figsize=(20, 10))\n\n    for i in range(num_samples):\n        row = multi_bleed_df.iloc[i]\n        img_path = os.path.join(IMG_DIR, row['file_path'])\n        \n        # WebP görselini oku\n        img = cv2.imread(img_path)\n        if img is None: \n            continue\n        \n        # Doğal Tomografi Görünümü: 0. Kanal (Brain Window: 40, 80)\n        img_gray = img[:, :, 0] \n        \n        # Gerçekte var olan kanama etiketleri (Reality)\n        active_labels = [label.upper() for label in SUBTYPES if row[label] == 1]\n        reality_text = \" + \\n\".join(active_labels)\n        \n        # Subplot çizimi\n        plt.subplot(1, num_samples, i + 1)\n        plt.imshow(img_gray, cmap='gray')\n        plt.title(f\"REALITY (Multi):\\n{reality_text}\", fontsize=11, color='red', fontweight='bold')\n        plt.axis('off')\n\n    plt.tight_layout()\n    plt.show()\n\n    # İlgili satırların CSV dökümü\n    print(\"\\n📋 Validation Setindeki Çoklu Kanama Satırları:\")\n    display(multi_bleed_df[['Image', 'file_path'] + ALL_LABELS].head(num_samples))\nelse:\n    print(\"❌ Validation setinde birden fazla kanama türü içeren vaka bulunamadı.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:34:54.577445Z","iopub.execute_input":"2026-09-15T14:34:54.580198Z","iopub.status.idle":"2026-09-15T14:34:55.870959Z","shell.execute_reply.started":"2026-09-15T14:34:54.580093Z","shell.execute_reply":"2026-09-15T14:34:55.869311Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport cv2\nimport torch\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport albumentations as A\nfrom albumentations.pytorch import ToTensorV2\n\n# ============================================================\n# 1. Modeli ve Eğitilen Ağırlıkları Yükle\n# ============================================================\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\ninfer_model = CustomModel(model_name=\"tf_efficientnet_b0.ns_jft_in1k\", num_classes=6, pretrained=False)\ncheckpoint_path = os.path.join(SAVE_DIR, \"best_rsna_efficientnet.pth\")\ncheckpoint = torch.load(checkpoint_path, map_location=device, weights_only=False)\n\ninfer_model.load_state_dict(checkpoint['model_state_dict'])\ninfer_model.to(device)\ninfer_model.eval()\n\n# Normalizasyon\neval_transform = A.Compose([\n    A.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),\n    ToTensorV2()\n])\n\n# ============================================================\n# 2. Tek Görsel Teşhis Fonksiyonu\n# ============================================================\ndef predict_single_image(image_path, threshold=0.5):\n    # 1. Resmi oku ve tensöre çevir\n    img_bgr = cv2.imread(image_path)\n    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)\n    \n    transformed = eval_transform(image=img_rgb)['image']\n    input_tensor = transformed.unsqueeze(0).to(device)  # (1, 3, 384, 384)\n\n    # 2. Modelden ham logitleri al ve Sigmoid ile olasılığa çevir\n    with torch.no_grad():\n        logits = infer_model(input_tensor)\n        probs = torch.sigmoid(logits).cpu().numpy()[0]\n\n    # 3. Sonuçları listele\n    labels = ['epidural', 'intraparenchymal', 'intraventricular', 'subarachnoid', 'subdural', 'any']\n    \n    detected = []\n    print(\"\\n\" + \"=\"*40)\n    print(f\"🩺 TEŞHİS RAPORU: {os.path.basename(image_path)}\")\n    print(\"=\"*40)\n    for label, prob in zip(labels, probs):\n        is_positive = prob >= threshold\n        status = \"POZİTİF (+)\" if is_positive else \"Negatif (-)\"\n        print(f\"{label.upper():<18}: %{prob*100:5.1f} -> {status}\")\n        if is_positive and label != 'any':\n            detected.append(label.upper())\n\n    # Genel Karar\n    print(\"-\"*40)\n    if probs[-1] >= threshold or len(detected) > 0:\n        print(f\"🚨 SONUÇ: KANAMA TESPİT EDİLDİ -> {' + '.join(detected) if detected else 'BELİRSİZ TİP'}\")\n    else:\n        print(\"✅ SONUÇ: KANAMA SAPTANMADI (NORMAL BEYİN KESİTİ)\")\n    print(\"=\"*40)\n\n    # Ekrana tomografi kesitini çizdir\n    plt.figure(figsize=(5, 5))\n    plt.imshow(img_bgr[:, :, 0], cmap='gray')  # Brain window\n    plt.title(f\"Tahmin: {', '.join(detected) if detected else 'NORMAL'}\", color='red' if detected else 'green')\n    plt.axis('off')\n    plt.show()\n\n# ============================================================\n# 3. Örnek Test: Validation'dan Rastgele Bir Görsel Seç ve Dene\n# ============================================================\ntest_image_name = val_ready_df.iloc[0]['file_path']\ntest_image_full_path = os.path.join(IMG_DIR, test_image_name)\n\npredict_single_image(test_image_full_path, threshold=0.5)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-09-15T14:35:58.558682Z","iopub.execute_input":"2026-09-15T14:35:58.559702Z","iopub.status.idle":"2026-09-15T14:35:58.813488Z","shell.execute_reply.started":"2026-09-15T14:35:58.559669Z","shell.execute_reply":"2026-09-15T14:35:58.812633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}