{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":99552,"databundleVersionId":13441085}],"dockerImageVersionId":31089,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"## Import Liberary","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nimport pydicom\nimport tensorflow as tf\nfrom tensorflow.keras import layers, Model\nfrom tensorflow.keras.applications import DenseNet201, EfficientNetB3\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.optimizers import Adam\nfrom sklearn.model_selection import train_test_split\nimport cv2\nimport glob\nfrom tqdm import tqdm","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:16.491521Z","iopub.execute_input":"2025-09-02T09:52:16.491843Z","iopub.status.idle":"2025-09-02T09:52:29.624584Z","shell.execute_reply.started":"2025-09-02T09:52:16.491816Z","shell.execute_reply":"2025-09-02T09:52:29.623943Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Constants","metadata":{}},{"cell_type":"code","source":"DATA_DIR = '/kaggle/input/rsna-intracranial-aneurysm-detection'\nTRAIN_CSV_PATH = os.path.join(DATA_DIR, 'train.csv')\nSERIES_DIR = os.path.join(DATA_DIR, 'series')\nTEST_DIR = os.path.join(DATA_DIR, 'test')\n\nLABEL_COLUMNS = [\n    'Left Infraclinoid Internal Carotid Artery',\n    'Right Infraclinoid Internal Carotid Artery',\n    'Left Supraclinoid Internal Carotid Artery',\n    'Right Supraclinoid Internal Carotid Artery',\n    'Left Middle Cerebral Artery',\n    'Right Middle Cerebral Artery',\n    'Anterior Communicating Artery',\n    'Left Anterior Cerebral Artery',\n    'Right Anterior Cerebral Artery',\n    'Left Posterior Communicating Artery',\n    'Right Posterior Communicating Artery',\n    'Basilar Tip',\n    'Other Posterior Circulation',\n    'Aneurysm Present'\n]\n\nIMG_SIZE = (300, 300)  # EfficientNet-B3 default\nBATCH_SIZE = 16\nEPOCHS = 2\nLEARNING_RATE = 0.0001","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.62595Z","iopub.execute_input":"2025-09-02T09:52:29.626591Z","iopub.status.idle":"2025-09-02T09:52:29.631735Z","shell.execute_reply.started":"2025-09-02T09:52:29.626569Z","shell.execute_reply":"2025-09-02T09:52:29.631025Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Load Data","metadata":{}},{"cell_type":"code","source":"train_df = pd.read_csv(TRAIN_CSV_PATH)\ntrain_df[LABEL_COLUMNS] = train_df[LABEL_COLUMNS].fillna(0)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.632641Z","iopub.execute_input":"2025-09-02T09:52:29.633396Z","iopub.status.idle":"2025-09-02T09:52:29.699973Z","shell.execute_reply.started":"2025-09-02T09:52:29.633369Z","shell.execute_reply":"2025-09-02T09:52:29.699344Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## DICOM Preprocessing\n","metadata":{}},{"cell_type":"code","source":"class BrainAneurysmDataGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df, series_dir, batch_size=BATCH_SIZE, img_size=IMG_SIZE, shuffle=True):\n        self.df = df\n        self.series_dir = series_dir\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.shuffle = shuffle\n        self.on_epoch_end()\n    \n    def __len__(self):\n        return int(np.ceil(len(self.df) / self.batch_size))\n    \n    def __getitem__(self, idx):\n        batch_df = self.df.iloc[idx * self.batch_size:(idx + 1) * self.batch_size]\n        X, y = [], []\n        for _, row in batch_df.iterrows():\n            series_id = row['SeriesInstanceUID']\n            series_path = os.path.join(self.series_dir, series_id)\n            dcm_files = glob.glob(os.path.join(series_path, \"*.dcm\"))\n            if len(dcm_files) > 0:\n                num_slices = min(10, len(dcm_files))\n                selected_files = np.random.choice(dcm_files, num_slices, replace=False)\n                imgs = [preprocess_dicom(f) for f in selected_files]\n                series_img = np.mean(imgs, axis=0)\n            else:\n                series_img = np.zeros((*self.img_size, 3), dtype=np.float32)\n            X.append(series_img)\n            y.append(row[LABEL_COLUMNS].values)\n        X = np.array(X, dtype=np.float32) / 255.0  # normalize\n        y = np.array(y, dtype=np.float32)\n        return X, y\n    \n    def on_epoch_end(self):\n        if self.shuffle:\n            self.df = self.df.sample(frac=1).reset_index(drop=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.700796Z","iopub.execute_input":"2025-09-02T09:52:29.701081Z","iopub.status.idle":"2025-09-02T09:52:29.70976Z","shell.execute_reply.started":"2025-09-02T09:52:29.701052Z","shell.execute_reply":"2025-09-02T09:52:29.70896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def preprocess_dicom(dcm_path):\n    try:\n        ds = pydicom.dcmread(dcm_path)\n        img = ds.pixel_array.astype(np.float32)\n        img = img - np.min(img)\n        if np.max(img) != 0:\n            img = img / np.max(img)\n        img = cv2.resize(img, IMG_SIZE)\n        # 3-channel\n        if len(img.shape) == 2:\n            img = np.stack([img, img, img], axis=-1)\n        if img.shape != (*IMG_SIZE, 3):\n            img = np.zeros((*IMG_SIZE, 3), dtype=np.float32)\n        return img\n    except:\n        return np.zeros((*IMG_SIZE, 3), dtype=np.float32)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.711752Z","iopub.execute_input":"2025-09-02T09:52:29.712161Z","iopub.status.idle":"2025-09-02T09:52:29.726587Z","shell.execute_reply.started":"2025-09-02T09:52:29.712142Z","shell.execute_reply":"2025-09-02T09:52:29.725938Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Generator","metadata":{}},{"cell_type":"code","source":"class BrainAneurysmDataGenerator(tf.keras.utils.Sequence):\n    def __init__(self, df, series_dir, batch_size=BATCH_SIZE, img_size=IMG_SIZE, shuffle=True):\n        self.df = df\n        self.series_dir = series_dir\n        self.batch_size = batch_size\n        self.img_size = img_size\n        self.shuffle = shuffle\n        self.on_epoch_end()\n    \n    def __len__(self):\n        return int(np.ceil(len(self.df) / self.batch_size))\n    \n    def __getitem__(self, idx):\n        batch_df = self.df.iloc[idx * self.batch_size:(idx + 1) * self.batch_size]\n        X, y = [], []\n        for _, row in batch_df.iterrows():\n            series_id = row['SeriesInstanceUID']\n            series_path = os.path.join(self.series_dir, series_id)\n            dcm_files = glob.glob(os.path.join(series_path, \"*.dcm\"))\n            if len(dcm_files) > 0:\n                num_slices = min(10, len(dcm_files))\n                selected_files = np.random.choice(dcm_files, num_slices, replace=False)\n                imgs = [preprocess_dicom(f) for f in selected_files]\n                series_img = np.mean(imgs, axis=0)\n            else:\n                series_img = np.zeros((*self.img_size, 3), dtype=np.float32)\n            X.append(series_img)\n            y.append(row[LABEL_COLUMNS].values)\n        X = np.array(X, dtype=np.float32) / 255.0  # normalize\n        y = np.array(y, dtype=np.float32)\n        return X, y\n    \n    def on_epoch_end(self):\n        if self.shuffle:\n            self.df = self.df.sample(frac=1).reset_index(drop=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.727234Z","iopub.execute_input":"2025-09-02T09:52:29.727503Z","iopub.status.idle":"2025-09-02T09:52:29.744552Z","shell.execute_reply.started":"2025-09-02T09:52:29.72748Z","shell.execute_reply":"2025-09-02T09:52:29.743861Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Train/Validation Split\n","metadata":{}},{"cell_type":"code","source":"train_data, val_data = train_test_split(\n    train_df, test_size=0.2, random_state=42, stratify=train_df['Aneurysm Present']\n)\n\ntrain_generator = BrainAneurysmDataGenerator(train_data, SERIES_DIR, batch_size=BATCH_SIZE)\nval_generator = BrainAneurysmDataGenerator(val_data, SERIES_DIR, batch_size=BATCH_SIZE, shuffle=False)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.745227Z","iopub.execute_input":"2025-09-02T09:52:29.745499Z","iopub.status.idle":"2025-09-02T09:52:29.769219Z","shell.execute_reply.started":"2025-09-02T09:52:29.745474Z","shell.execute_reply":"2025-09-02T09:52:29.768655Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Creation","metadata":{}},{"cell_type":"code","source":"def create_model(base_model_class):\n    base_model = base_model_class(\n        weights='imagenet', include_top=False, input_shape=(*IMG_SIZE, 3)\n    )\n    base_model.trainable = False\n    x = layers.GlobalAveragePooling2D()(base_model.output)\n    x = layers.Dense(512, activation='relu')(x)\n    x = layers.Dropout(0.5)(x)\n    x = layers.Dense(256, activation='relu')(x)\n    x = layers.Dropout(0.3)(x)\n    predictions = layers.Dense(len(LABEL_COLUMNS), activation='sigmoid')(x)\n    model = Model(inputs=base_model.input, outputs=predictions)\n    model.compile(\n        optimizer=Adam(learning_rate=LEARNING_RATE),\n        loss='binary_crossentropy',\n        metrics=['accuracy', tf.keras.metrics.AUC(name='auc')]\n    )\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.76998Z","iopub.execute_input":"2025-09-02T09:52:29.770242Z","iopub.status.idle":"2025-09-02T09:52:29.775965Z","shell.execute_reply.started":"2025-09-02T09:52:29.770219Z","shell.execute_reply":"2025-09-02T09:52:29.775222Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Models","metadata":{}},{"cell_type":"code","source":"efficientnet_model = create_model(EfficientNetB3)\ndensenet_model = create_model(DenseNet201)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:29.776656Z","iopub.execute_input":"2025-09-02T09:52:29.776906Z","iopub.status.idle":"2025-09-02T09:52:38.49911Z","shell.execute_reply.started":"2025-09-02T09:52:29.776877Z","shell.execute_reply":"2025-09-02T09:52:38.498333Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Callbacks","metadata":{}},{"cell_type":"code","source":"efficientnet_checkpoint = ModelCheckpoint(\n    'efficientnet_model.h5', monitor='val_auc', save_best_only=True, mode='max', verbose=1\n)\ndensenet_checkpoint = ModelCheckpoint(\n    'densenet_model.h5', monitor='val_auc', save_best_only=True, mode='max', verbose=1\n)\nearly_stopping = EarlyStopping(\n    monitor='val_auc', patience=5, mode='max', verbose=1, restore_best_weights=True\n)\nreduce_lr = ReduceLROnPlateau(\n    monitor='val_auc', factor=0.2, patience=2, min_lr=1e-6, mode='max', verbose=1\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:38.500167Z","iopub.execute_input":"2025-09-02T09:52:38.500389Z","iopub.status.idle":"2025-09-02T09:52:38.504876Z","shell.execute_reply.started":"2025-09-02T09:52:38.500373Z","shell.execute_reply":"2025-09-02T09:52:38.504224Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## EfficientNet-B3 Training","metadata":{}},{"cell_type":"code","source":"print(\"Training EfficientNet-B3 model...\")\nefficientnet_history = efficientnet_model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=EPOCHS,\n    callbacks=[efficientnet_checkpoint, early_stopping, reduce_lr]\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-09-02T09:52:38.505987Z","iopub.execute_input":"2025-09-02T09:52:38.506274Z","execution_failed":"2025-09-02T12:48:36.929Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## DenseNet201 Training","metadata":{}},{"cell_type":"code","source":"print(\"Training DenseNet-201 model...\")\ndensenet_history = densenet_model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=EPOCHS,\n    callbacks=[densenet_checkpoint, early_stopping, reduce_lr]\n)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-09-02T12:48:36.93Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Ensemble Model","metadata":{}},{"cell_type":"code","source":"def create_ensemble_model(models):\n    input_layer = layers.Input(shape=(*IMG_SIZE, 3))\n    outputs = [model(input_layer) for model in models]\n    avg_output = layers.Average()(outputs)\n    ensemble_model = Model(inputs=input_layer, outputs=avg_output)\n    ensemble_model.compile(\n        optimizer=Adam(learning_rate=LEARNING_RATE),\n        loss='binary_crossentropy',\n        metrics=['accuracy', tf.keras.metrics.AUC(name='auc')]\n    )\n    return ensemble_model\n\nensemble_model = create_ensemble_model([efficientnet_model, densenet_model])\n\nensemble_checkpoint = ModelCheckpoint(\n    'ensemble_model.h5', monitor='val_auc', save_best_only=True, mode='max', verbose=1\n)\n\nprint(\"Training Ensemble model...\")\nensemble_history = ensemble_model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=EPOCHS,\n    callbacks=[ensemble_checkpoint, early_stopping, reduce_lr]\n)\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-09-02T12:48:36.93Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Test Prediction","metadata":{}},{"cell_type":"code","source":"def predict_test_data(model, test_dir):\n    test_series_ids = os.listdir(test_dir)\n    predictions = []\n    for series_id in tqdm(test_series_ids):\n        series_path = os.path.join(test_dir, series_id)\n        dcm_files = glob.glob(os.path.join(series_path, \"*.dcm\"))\n        if len(dcm_files) > 0:\n            num_slices = min(10, len(dcm_files))\n            selected_files = np.random.choice(dcm_files, num_slices, replace=False)\n            imgs = [preprocess_dicom(f) for f in selected_files]\n            series_img = np.mean(imgs, axis=0)\n        else:\n            series_img = np.zeros((*IMG_SIZE, 3), dtype=np.float32)\n        series_img = np.expand_dims(series_img / 255.0, axis=0)\n        pred = model.predict(series_img)[0]\n        predictions.append(pred)\n    return predictions\n\nprint(\"Predicting on test data...\")\ntest_predictions = predict_test_data(ensemble_model, TEST_DIR)\n\nsubmission_df = pd.DataFrame(test_predictions, columns=LABEL_COLUMNS)\nsubmission_df.insert(0, 'SeriesInstanceUID', os.listdir(TEST_DIR))\nsubmission_df.to_csv('submission.csv', index=False)\nprint(\"Submission file created successfully!\")\n\nensemble_model.save('final_ensemble_model.h5')\nprint(\"Model saved successfully!\")\n","metadata":{"trusted":true,"execution":{"execution_failed":"2025-09-02T12:48:36.93Z"}},"outputs":[],"execution_count":null}]}