{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":99552,"databundleVersionId":13190393,"sourceType":"competition"}],"dockerImageVersionId":31090,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# Baseline Pipeline: EDA → Modeling → Submission\n\nThis notebook outlines a baseline workflow for the RSNA intracranial aneurysm detection challenge,\nincluding comprehensive exploratory data analysis (EDA), demographic-based baseline modeling\nwith hyperparameter tuning, and submission generation.","metadata":{"_uuid":"8d98f82b-0234-4c7e-b9ed-29cacf988454","_cell_guid":"49bec877-d2e7-4dfc-a44a-3f29fd52d1db","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# 1. Imports and Paths\nimport os\nimport shutil\nfrom collections import defaultdict\n\nimport pandas as pd\nimport polars as pl\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport seaborn as sns                          # Quick EDA visuals\nfrom sklearn.model_selection import train_test_split, GridSearchCV\nfrom sklearn.linear_model import LogisticRegression\nfrom sklearn.preprocessing import StandardScaler\nfrom sklearn.pipeline import Pipeline\nfrom sklearn.metrics import roc_auc_score\nimport pydicom\nimport kaggle_evaluation.rsna_inference_server as es  # Inference server\nSEED = 130\n\n# Input directory for Kaggle competition\nDATA_PATH = '/kaggle/input/rsna-intracranial-aneurysm-detection/'","metadata":{"_uuid":"05de1896-7ceb-458a-8021-7c6a364d708c","_cell_guid":"82013628-6087-4ac4-9cf4-9d6f18095e6a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:17.707453Z","iopub.execute_input":"2025-07-30T05:48:17.708121Z","iopub.status.idle":"2025-07-30T05:48:20.40453Z","shell.execute_reply.started":"2025-07-30T05:48:17.708095Z","shell.execute_reply":"2025-07-30T05:48:20.403918Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 2. Load Data","metadata":{"_uuid":"2f4e7088-0d21-4e34-accb-e97708f1ec67","_cell_guid":"ed8ddbf4-1510-4c9c-adae-b7321962fcc9","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Load training labels and localizer metadata\ntrain = pd.read_csv(os.path.join(DATA_PATH, 'train.csv'))\ntrain_localizers = pd.read_csv(os.path.join(DATA_PATH, 'train_localizers.csv'))\n\nprint(f\"Number of training series: {train.shape[0]}\")\nprint(f\"Number of localization rows: {train_localizers.shape[0]}\")","metadata":{"_uuid":"39afbe08-234e-4112-a65a-55d111a803ad","_cell_guid":"c168d258-7516-42f8-bc29-f837de1aa922","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:20.406122Z","iopub.execute_input":"2025-07-30T05:48:20.406517Z","iopub.status.idle":"2025-07-30T05:48:20.46001Z","shell.execute_reply.started":"2025-07-30T05:48:20.406497Z","shell.execute_reply":"2025-07-30T05:48:20.459267Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 3. Exploratory Data Analysis (EDA)","metadata":{"_uuid":"eccf9734-f25b-4528-9abc-f315287aa296","_cell_guid":"f97627c9-cb4c-4edb-8c62-41163cd21151","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# 3.1 Age distribution overall\n# Ensure we treat PatientAge as string before splitting\ndf_age = train['PatientAge'].astype(str)\nage_vals = df_age.str.split(' - ').str[0].astype(float)\n\nplt.figure(figsize=(8,4))\nplt.hist(age_vals, bins=20, edgecolor='k')\nplt.title('Patient Age Distribution')\nplt.xlabel('Age')\nplt.ylabel('Count')\nplt.show()","metadata":{"_uuid":"068f8bb4-3f64-4af0-a625-852f9b02e1a3","_cell_guid":"eb5046a9-d6f6-4c3b-8616-c5636ad92e51","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:20.460823Z","iopub.execute_input":"2025-07-30T05:48:20.461072Z","iopub.status.idle":"2025-07-30T05:48:20.862541Z","shell.execute_reply.started":"2025-07-30T05:48:20.461054Z","shell.execute_reply":"2025-07-30T05:48:20.861685Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.2 Sex distribution\nplt.figure(figsize=(6,4))\nsns.countplot(data=train, x='PatientSex', palette='pastel')\nplt.title('Patient Sex Distribution')\nplt.xlabel('Sex')\nplt.ylabel('Count')\nplt.show()","metadata":{"_uuid":"de240f78-d0ac-43af-879f-c4fbe5d94d6b","_cell_guid":"17dee98a-83c3-404c-a4bc-2e90f3e4beb9","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:20.863348Z","iopub.execute_input":"2025-07-30T05:48:20.863614Z","iopub.status.idle":"2025-07-30T05:48:20.994633Z","shell.execute_reply.started":"2025-07-30T05:48:20.86357Z","shell.execute_reply":"2025-07-30T05:48:20.993833Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.3 Age vs. aneurysm presence\nplt.figure(figsize=(8,4))\nsns.boxplot(x='Aneurysm Present', y=age_vals, data=train)\nplt.xticks([0,1], ['Absent','Present'])\nplt.title('Age vs. Aneurysm Presence')\nplt.ylabel('Age')\nplt.show()","metadata":{"_uuid":"0e475fc3-5e74-49c0-bf3b-97374bc4407b","_cell_guid":"c583cab2-1fdb-4c6e-9038-4a850e59da68","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:20.996678Z","iopub.execute_input":"2025-07-30T05:48:20.997062Z","iopub.status.idle":"2025-07-30T05:48:21.146247Z","shell.execute_reply.started":"2025-07-30T05:48:20.997042Z","shell.execute_reply":"2025-07-30T05:48:21.145419Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.4 Modality distribution\nplt.figure(figsize=(6,4))\nsns.countplot(data=train, x='Modality', palette='Set2')\nplt.title('Imaging Modality Counts')\nplt.xlabel('Modality')\nplt.ylabel('Count')\nplt.show()","metadata":{"_uuid":"eb41dd05-4f59-4b9e-b498-7cee470d66dd","_cell_guid":"ac1d2dbc-ea14-44d9-89cf-48543eee42bb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:21.147038Z","iopub.execute_input":"2025-07-30T05:48:21.147292Z","iopub.status.idle":"2025-07-30T05:48:21.286164Z","shell.execute_reply.started":"2025-07-30T05:48:21.14725Z","shell.execute_reply":"2025-07-30T05:48:21.285412Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.5 Aneurysm prevalence by modality\nmodality_rates = train.groupby('Modality')['Aneurysm Present'].mean().reset_index()\n\nplt.figure(figsize=(6,4))\nsns.barplot(data=modality_rates, x='Modality', y='Aneurysm Present', palette='Set1')\nplt.title('Aneurysm Prevalence by Modality')\nplt.ylabel('Prevalence')\nplt.show()","metadata":{"_uuid":"c16f3609-aaf2-4530-af80-43d63f466743","_cell_guid":"7b39f9c4-31f4-48a3-81c0-a9b4e9217a8b","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:21.286987Z","iopub.execute_input":"2025-07-30T05:48:21.287222Z","iopub.status.idle":"2025-07-30T05:48:21.430173Z","shell.execute_reply.started":"2025-07-30T05:48:21.287207Z","shell.execute_reply":"2025-07-30T05:48:21.429535Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.6 Label prevalence per location\nlabel_cols = [\n    'Left Infraclinoid Internal Carotid Artery','Right Infraclinoid Internal Carotid Artery',\n    'Left Supraclinoid Internal Carotid Artery','Right Supraclinoid Internal Carotid Artery',\n    'Left Middle Cerebral Artery','Right Middle Cerebral Artery','Anterior Communicating Artery',\n    'Left Anterior Cerebral Artery','Right Anterior Cerebral Artery',\n    'Left Posterior Communicating Artery','Right Posterior Communicating Artery',\n    'Basilar Tip','Other Posterior Circulation'\n]\nprevalences = train[label_cols].mean().sort_values(ascending=False)\n\nplt.figure(figsize=(10,6))\nsns.barplot(x=prevalences.values, y=prevalences.index, palette='coolwarm')\nplt.title('Aneurysm Prevalence by Vascular Location')\nplt.xlabel('Prevalence')\nplt.show()","metadata":{"_uuid":"1a91fa6d-9b81-40b8-a9f1-2157583cab6d","_cell_guid":"a836c41c-6399-47b9-9f05-8897b98f2898","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:21.430815Z","iopub.execute_input":"2025-07-30T05:48:21.431034Z","iopub.status.idle":"2025-07-30T05:48:21.699977Z","shell.execute_reply.started":"2025-07-30T05:48:21.431017Z","shell.execute_reply":"2025-07-30T05:48:21.699229Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3.7 Correlation heatmap for labels\nplt.figure(figsize=(12,10))\ncor_mat = train[label_cols + ['Aneurysm Present']].corr()\nsns.heatmap(cor_mat, annot=False, cmap='vlag')\nplt.title('Correlation Matrix of Label Columns')\nplt.show()","metadata":{"_uuid":"3344b4ae-3efe-4056-ac52-7b9e7ef4fc3b","_cell_guid":"53a61c35-ed1f-4883-bc4e-1f377a2b6d13","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:21.70092Z","iopub.execute_input":"2025-07-30T05:48:21.701406Z","iopub.status.idle":"2025-07-30T05:48:22.131626Z","shell.execute_reply.started":"2025-07-30T05:48:21.701382Z","shell.execute_reply":"2025-07-30T05:48:22.130899Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 4. Preprocessing & Feature Engineering\nWe will build a simple demographic-based baseline: numeric age, sex, and one-hot encoded modality.","metadata":{"_uuid":"311819c5-97e6-4364-8821-04199853fd02","_cell_guid":"2607746b-897e-4819-a889-75ffa4d04171","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# Extract numerical age and binary sex\nx_age = df_age.str.split(' - ').str[0].astype(float)\nX = pd.DataFrame({\n    'age': x_age,\n    'sex': (train['PatientSex']=='Male').astype(int)\n})\n\n# One-hot encode modality\nmod_dummies = pd.get_dummies(train['Modality'], prefix='mod')\nX = pd.concat([X, mod_dummies], axis=1)\n\n# Target variable\ny = train['Aneurysm Present']\n\nprint(f\"Feature matrix shape: {X.shape}\")","metadata":{"_uuid":"fec465ab-7e93-4f4b-afc4-fbf778a5e6fb","_cell_guid":"14f89044-71b5-4571-939c-ce2042ce97d0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:22.132576Z","iopub.execute_input":"2025-07-30T05:48:22.132817Z","iopub.status.idle":"2025-07-30T05:48:22.147357Z","shell.execute_reply.started":"2025-07-30T05:48:22.132798Z","shell.execute_reply":"2025-07-30T05:48:22.146533Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 5. Train/Test Split & Hyperparameter Tuning","metadata":{"_uuid":"b0c69c68-8f05-4ac1-9b45-8d98d27dfb41","_cell_guid":"65b25b40-8d6d-400c-9dc7-677528c587f0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# 5.1 Split into train and validation sets\n#X_train, X_val, y_train, y_val = train_test_split(\n#    X, y, test_size=0.2, stratify=y, random_state=42\n#)\n#\n# 5.2 Baseline Logistic Regression\n#base_model = LogisticRegression(max_iter=1000, solver='liblinear')\n#base_model.fit(X_train, y_train)\n#base_preds = base_model.predict_proba(X_val)[:,1]\n#base_auc = roc_auc_score(y_val, base_preds)\n#print(f\"Baseline Validation AUC: {base_auc:.4f}\")\n#\n## 5.3 Hyperparameter Tuning with GridSearchCV\n#pipe = Pipeline([\n#    ('scaler', StandardScaler()),\n#    ('logreg', LogisticRegression(solver='liblinear', max_iter=1000))\n#])\n#param_grid = {\n#    'logreg__C': [0.01, 0.1, 1, 10, 100],\n#    'logreg__penalty': ['l1','l2']\n#}\n#grid = GridSearchCV(pipe, param_grid, cv=5, scoring='roc_auc', n_jobs=-1)\n#grid.fit(X_train, y_train)\n#print(f\"Best parameters: {grid.best_params_}\")\n#print(f\"CV training AUC: {grid.best_score_:.4f}\")\n\n# 5.4 Evaluate tuned model on validation set\n#tuned_model = grid.best_estimator_\n#tuned_preds = tuned_model.predict_proba(X_val)[:,1]\n#tuned_auc = roc_auc_score(y_val, tuned_preds)\n#print(f\"Tuned Validation AUC: {tuned_auc:.4f}\")\n\n\nfrom sklearn.ensemble import GradientBoostingClassifier\n\n# 5.1 split into train and validation sets\nX_train, X_val, y_train, y_val = train_test_split(\n    X, y, test_size=0.2, stratify=y, random_state=SEED\n)\n\n# 5.2 initialize and fit Gradient Boosting model\ngbm = GradientBoostingClassifier(\n    n_estimators=100,     # number of trees\n    learning_rate=0.1,    # shrinkage factor\n    max_depth=8,          # maximum tree depth\n    random_state=SEED\n)\ngbm.fit(X_train, y_train)\n\n# 5.3 evaluate on validation set\nval_probs = gbm.predict_proba(X_val)[:, 1]\nval_auc = roc_auc_score(y_val, val_probs)\nprint(f\"GBM Validation AUC: {val_auc:.4f}\")","metadata":{"_uuid":"0db0d2f2-469e-42bc-a811-eb0288dd1337","_cell_guid":"2b1d8312-ba38-481f-8854-ac28f7702ebb","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:22.148177Z","iopub.execute_input":"2025-07-30T05:48:22.148386Z","iopub.status.idle":"2025-07-30T05:48:24.75416Z","shell.execute_reply.started":"2025-07-30T05:48:22.14837Z","shell.execute_reply":"2025-07-30T05:48:24.753303Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 6. Inference & Submission","metadata":{"_uuid":"2f6026e9-58d9-4595-bf13-209c0c1df529","_cell_guid":"24a8b97d-8396-4f2a-adad-d5d201de4997","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false}}},{"cell_type":"code","source":"# 6.1 Prepare globals for the predict() function\nID_COL = 'SeriesInstanceUID'\nLABEL_COLS = label_cols + ['Aneurysm Present']\n\n# Compute global means for fallback\nmeans = train[LABEL_COLS].mean().to_dict()\n# Create an index of training series\ntrain_idx = train.set_index('SeriesInstanceUID')\n\ndef predict(series_path: str) -> pl.DataFrame:\n    # Extract series ID from path\n    sid = os.path.basename(series_path)\n\n    # If the series ID is not in our training set, return the global mean probabilities\n    if sid not in train_idx.index:\n        out = [sid] + [means[c] for c in LABEL_COLS]\n        preds = pl.DataFrame([out], schema=[ID_COL] + LABEL_COLS)\n    else:\n        row = train_idx.loc[sid]\n        age_val = float(str(row['PatientAge']).split(' - ')[0])\n        sex_val = int(row['PatientSex'] == 'Male')\n        modality = row['Modality']\n\n        # Build feature vector\n        feats = {'age': age_val, 'sex': sex_val}\n        for m in mod_dummies.columns:\n            feats[m] = int(m == f\"mod_{modality}\")\n        feat_df = pl.DataFrame(feats, orient='row').to_pandas()\n\n        # Predict probability\n        prob = tuned_model.predict_proba(feat_df)[:,1][0]\n        out = [sid] + [prob] * len(LABEL_COLS)\n        preds = pl.DataFrame([out], schema=[ID_COL] + LABEL_COLS)\n\n    # **IMPORTANT**: clear shared folder after each call to avoid disk overflow\n    shutil.rmtree('/kaggle/shared', ignore_errors=True)\n\n    # Drop the ID column as required by the API\n    return preds.drop(ID_COL)","metadata":{"_uuid":"79a4559b-0e47-4355-b083-d5610d954e2b","_cell_guid":"fb49bdef-c709-4bae-a61f-657d068edce0","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:24.755004Z","iopub.execute_input":"2025-07-30T05:48:24.755214Z","iopub.status.idle":"2025-07-30T05:48:24.765698Z","shell.execute_reply.started":"2025-07-30T05:48:24.755196Z","shell.execute_reply":"2025-07-30T05:48:24.764852Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 6.2 Launch the inference server in competition mode only\ninference_server = es.RSNAInferenceServer(predict)\ninference_server.serve()","metadata":{"_uuid":"92a5a095-82cf-4d1e-87e2-af3d4528f115","_cell_guid":"86cd5653-8365-428a-b8eb-eaaf2bbad51a","trusted":true,"collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2025-07-30T05:48:24.766614Z","iopub.execute_input":"2025-07-30T05:48:24.767288Z","iopub.status.idle":"2025-07-30T05:48:24.836955Z","shell.execute_reply.started":"2025-07-30T05:48:24.76726Z","shell.execute_reply":"2025-07-30T05:48:24.836419Z"}},"outputs":[],"execution_count":null}]}