{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":29653,"databundleVersionId":2420395,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport torch\nimport torch.nn as nn\nimport pandas as pd\nimport numpy as np\nimport matplotlib.pyplot as plt\nimport matplotlib\nimport pydicom as dicom\nimport cv2\nimport ast\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:42.524421Z","iopub.execute_input":"2026-07-10T22:24:42.524698Z","iopub.status.idle":"2026-07-10T22:24:50.798945Z","shell.execute_reply.started":"2026-07-10T22:24:42.524679Z","shell.execute_reply":"2026-07-10T22:24:50.797984Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/'\nos.listdir(path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:50.800307Z","iopub.execute_input":"2026-07-10T22:24:50.800858Z","iopub.status.idle":"2026-07-10T22:24:50.808473Z","shell.execute_reply.started":"2026-07-10T22:24:50.800834Z","shell.execute_reply":"2026-07-10T22:24:50.807787Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/'\nos.listdir(path)\ntrain_data = pd.read_csv(path+'train_labels.csv')\nsamp_subm = pd.read_csv(path+'sample_submission.csv')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:50.809385Z","iopub.execute_input":"2026-07-10T22:24:50.809790Z","iopub.status.idle":"2026-07-10T22:24:50.844578Z","shell.execute_reply.started":"2026-07-10T22:24:50.809739Z","shell.execute_reply":"2026-07-10T22:24:50.843793Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Samples train:', len(train_data))\nprint('Samples test:', len(samp_subm))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:50.846224Z","iopub.execute_input":"2026-07-10T22:24:50.846559Z","iopub.status.idle":"2026-07-10T22:24:50.851495Z","shell.execute_reply.started":"2026-07-10T22:24:50.846539Z","shell.execute_reply":"2026-07-10T22:24:50.850802Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:50.852502Z","iopub.execute_input":"2026-07-10T22:24:50.853022Z","iopub.status.idle":"2026-07-10T22:24:50.885603Z","shell.execute_reply.started":"2026-07-10T22:24:50.852991Z","shell.execute_reply":"2026-07-10T22:24:50.884912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data[\"MGMT_value\"].value_counts().head(2).plot(kind = 'pie', autopct='%1.1f%%', figsize=(8, 8)).legend()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:50.886550Z","iopub.execute_input":"2026-07-10T22:24:50.887163Z","iopub.status.idle":"2026-07-10T22:24:51.215792Z","shell.execute_reply.started":"2026-07-10T22:24:50.887141Z","shell.execute_reply":"2026-07-10T22:24:51.214810Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_data[\"MGMT_value\"].value_counts()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.216820Z","iopub.execute_input":"2026-07-10T22:24:51.217153Z","iopub.status.idle":"2026-07-10T22:24:51.224907Z","shell.execute_reply.started":"2026-07-10T22:24:51.217126Z","shell.execute_reply":"2026-07-10T22:24:51.223939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"samp_subm.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.225833Z","iopub.execute_input":"2026-07-10T22:24:51.226155Z","iopub.status.idle":"2026-07-10T22:24:51.244661Z","shell.execute_reply.started":"2026-07-10T22:24:51.226133Z","shell.execute_reply":"2026-07-10T22:24:51.243907Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"folder = str(train_data.loc[0, 'BraTS21ID']).zfill(5)\nfolder","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.245563Z","iopub.execute_input":"2026-07-10T22:24:51.245947Z","iopub.status.idle":"2026-07-10T22:24:51.261581Z","shell.execute_reply.started":"2026-07-10T22:24:51.245911Z","shell.execute_reply":"2026-07-10T22:24:51.260603Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"os.listdir(path+'train/'+folder)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.265215Z","iopub.execute_input":"2026-07-10T22:24:51.265543Z","iopub.status.idle":"2026-07-10T22:24:51.281724Z","shell.execute_reply.started":"2026-07-10T22:24:51.265523Z","shell.execute_reply":"2026-07-10T22:24:51.280827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Number of FLAIR images:', len(os.listdir(path+'train/'+folder+'/'+'FLAIR')))\nprint('Number of T1w images:', len(os.listdir(path+'train/'+folder+'/'+'T1w')))\nprint('Number of T1wCE images:', len(os.listdir(path+'train/'+folder+'/'+'T1wCE')))\nprint('Number of T2w images:', len(os.listdir(path+'train/'+folder+'/'+'T2w')))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.282785Z","iopub.execute_input":"2026-07-10T22:24:51.283199Z","iopub.status.idle":"2026-07-10T22:24:51.334149Z","shell.execute_reply.started":"2026-07-10T22:24:51.283178Z","shell.execute_reply":"2026-07-10T22:24:51.333271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"path_file = ''.join([path, 'train/', folder, '/', 'FLAIR/'])\nimage = os.listdir(path_file)[0]\ndata_file = dicom.dcmread(path_file+image)\nimg = data_file.pixel_array\nprint('Image shape:', img.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.335002Z","iopub.execute_input":"2026-07-10T22:24:51.335212Z","iopub.status.idle":"2026-07-10T22:24:51.350221Z","shell.execute_reply.started":"2026-07-10T22:24:51.335196Z","shell.execute_reply":"2026-07-10T22:24:51.349290Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#Flair Image\ndef plot_examples(row = 0, cat = 'FLAIR'): \n    folder = str(train_data.loc[row, 'BraTS21ID']).zfill(5)\n    path_file = ''.join([path, 'train/', folder, '/', cat, '/'])\n    images = os.listdir(path_file)\n    \n    fig, axs = plt.subplots(1, 5, figsize=(30, 30))\n    fig.subplots_adjust(hspace = .2, wspace=.2)\n    axs = axs.ravel()\n    \n    for num in range(5):\n        data_file = dicom.dcmread(path_file+images[num])\n        img = data_file.pixel_array\n        axs[num].imshow(img, cmap='gray')\n        axs[num].set_title(cat+' '+images[num])\n        axs[num].set_xticklabels([])\n        axs[num].set_yticklabels([])\n        \nrow = 0\nplot_examples(row = row, cat = 'FLAIR')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:51.351160Z","iopub.execute_input":"2026-07-10T22:24:51.352048Z","iopub.status.idle":"2026-07-10T22:24:52.202335Z","shell.execute_reply.started":"2026-07-10T22:24:51.352024Z","shell.execute_reply":"2026-07-10T22:24:52.201499Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#T1w Images\nplot_examples(row = row, cat = 'T1w')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:52.203275Z","iopub.execute_input":"2026-07-10T22:24:52.203916Z","iopub.status.idle":"2026-07-10T22:24:52.989828Z","shell.execute_reply.started":"2026-07-10T22:24:52.203895Z","shell.execute_reply":"2026-07-10T22:24:52.989022Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#T1wCE Images\nplot_examples(row = row, cat = 'T1wCE')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:52.990718Z","iopub.execute_input":"2026-07-10T22:24:52.991365Z","iopub.status.idle":"2026-07-10T22:24:53.807926Z","shell.execute_reply.started":"2026-07-10T22:24:52.991345Z","shell.execute_reply":"2026-07-10T22:24:53.806920Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#T2w Images\nplot_examples(row = row, cat = 'T2w')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:53.808927Z","iopub.execute_input":"2026-07-10T22:24:53.809220Z","iopub.status.idle":"2026-07-10T22:24:54.618900Z","shell.execute_reply.started":"2026-07-10T22:24:53.809202Z","shell.execute_reply":"2026-07-10T22:24:54.617713Z"}},"outputs":[],"execution_count":null},{"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, models\nfrom sklearn.model_selection import train_test_split\nimport cv2\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import classification_report, confusion_matrix, roc_curve, auc\n\n# Path dataset\npath = '/kaggle/input/rsna-miccai-brain-tumor-radiogenomic-classification/'\ntrain_labels = pd.read_csv(path + 'train_labels.csv')\n\n# Konfigurasi\nIMG_SIZE = 128\nBATCH_SIZE = 32\nEPOCHS = 15\nMODALITY = 'FLAIR' \n\n# Fungsi untuk membaca dan memproses gambar DICOM\ndef load_dicom_image(filepath, img_size=IMG_SIZE):\n    dicom = pydicom.dcmread(filepath)\n    img = dicom.pixel_array.astype(float)\n    \n    # Normalisasi\n    img = (img - img.min()) / (img.max() - img.min())\n    \n    # Konversi ke uint8\n    img = (img * 255).astype(np.uint8)\n    \n    # Resize\n    img = cv2.resize(img, (img_size, img_size))\n    \n    # Stack ke 3 channel\n    img = np.stack([img]*3, axis=-1)\n    return img\n\n# Fungsi untuk memuat data pasien\ndef load_patient_data(patient_id, num_slices=16):\n    patient_path = os.path.join(path, 'train', str(patient_id).zfill(5), MODALITY)\n    slices = []\n    \n    if not os.path.exists(patient_path):\n        print(f\"Data tidak ditemukan untuk pasien {patient_id}\")\n        return None\n    \n    # Dapatkan semua file DICOM\n    dicom_files = sorted([f for f in os.listdir(patient_path) if f.endswith('.dcm')])\n    \n    if not dicom_files:\n        print(f\"Tidak ada file DICOM untuk pasien {patient_id}\")\n        return None\n    \n    # Pilih slice secara merata\n    step = max(1, len(dicom_files) // num_slices)\n    selected_files = dicom_files[::step][:num_slices]\n    \n    # Muat slice yang dipilih\n    for filename in selected_files:\n        img_path = os.path.join(patient_path, filename)\n        img = load_dicom_image(img_path)\n        slices.append(img)\n    \n    # Jika tidak cukup slice, duplikat yang terakhir\n    while len(slices) < num_slices:\n        slices.append(slices[-1].copy())  # Gunakan copy untuk menghindari reference yang sama\n    \n    return np.array(slices)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:24:54.619942Z","iopub.execute_input":"2026-07-10T22:24:54.620299Z","iopub.status.idle":"2026-07-10T22:25:12.634684Z","shell.execute_reply.started":"2026-07-10T22:24:54.620277Z","shell.execute_reply":"2026-07-10T22:25:12.633962Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Membuat dataset\nX = []\ny = []\n\nprint(\"Memuat data training...\")\nfor idx, row in train_labels.iterrows():\n    patient_id = row['BraTS21ID']\n    label = row['MGMT_value']\n    \n    patient_data = load_patient_data(patient_id)\n    if patient_data is not None:\n        X.append(patient_data)\n        y.append(label)\n\n# Konversi ke numpy array\nX = np.array(X, dtype=np.float32)\ny = np.array(y, dtype=np.float32)\n\nprint(f\"Total data yang dimuat: {len(X)} sampel\")\nprint(f\"Distribusi kelas: {np.sum(y == 1)} positif, {np.sum(y == 0)} negatif\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:25:12.635705Z","iopub.execute_input":"2026-07-10T22:25:12.636324Z","iopub.status.idle":"2026-07-10T22:26:32.821646Z","shell.execute_reply.started":"2026-07-10T22:25:12.636300Z","shell.execute_reply":"2026-07-10T22:26:32.820803Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Split data: training (60%), validation (20%), test (20%)\nX_train, X_temp, y_train, y_temp = train_test_split(\n    X, y, test_size=0.4, random_state=42, stratify=y\n)\nX_val, X_test, y_val, y_test = train_test_split(\n    X_temp, y_temp, test_size=0.5, random_state=42, stratify=y_temp\n)\n\nprint(\"\\nDistribusi dataset:\")\nprint(f\"Training:   {len(X_train)} sampel\")\nprint(f\"Validation: {len(X_val)} sampel\")\nprint(f\"Test:       {len(X_test)} sampel\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:32.822846Z","iopub.execute_input":"2026-07-10T22:26:32.823183Z","iopub.status.idle":"2026-07-10T22:26:33.588150Z","shell.execute_reply.started":"2026-07-10T22:26:32.823154Z","shell.execute_reply":"2026-07-10T22:26:33.587112Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Arsitektur model CNN 3D\ndef build_3d_cnn(input_shape, num_classes):\n    model = models.Sequential([\n        # Blok konvolusi 1\n        layers.Conv3D(16, (3, 3, 3), activation='relu', padding='same', input_shape=input_shape),\n        layers.BatchNormalization(),\n        layers.MaxPooling3D((2, 2, 2)),\n        layers.Dropout(0.2),\n        \n        # Blok konvolusi 2\n        layers.Conv3D(32, (3, 3, 3), activation='relu', padding='same'),\n        layers.BatchNormalization(),\n        layers.MaxPooling3D((2, 2, 2)),\n        layers.Dropout(0.3),\n        \n        # Blok konvolusi 3\n        layers.Conv3D(64, (3, 3, 3), activation='relu', padding='same'),\n        layers.BatchNormalization(),\n        layers.MaxPooling3D((2, 2, 2)),\n        layers.Dropout(0.4),\n        \n        layers.GlobalAveragePooling3D(),\n        layers.Dense(128, activation='relu'),\n        layers.Dropout(0.5),\n        layers.Dense(num_classes, activation='sigmoid')\n    ])\n    \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:33.589140Z","iopub.execute_input":"2026-07-10T22:26:33.589519Z","iopub.status.idle":"2026-07-10T22:26:33.595800Z","shell.execute_reply.started":"2026-07-10T22:26:33.589498Z","shell.execute_reply":"2026-07-10T22:26:33.595131Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Bangun model\ninput_shape = (X_train.shape[1], X_train.shape[2], X_train.shape[3], X_train.shape[4])\nprint(f\"\\nInput shape: {input_shape}\")\nmodel = build_3d_cnn(input_shape, num_classes=1)\n\n# Ringkasan model\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:33.596680Z","iopub.execute_input":"2026-07-10T22:26:33.597297Z","iopub.status.idle":"2026-07-10T22:26:36.052494Z","shell.execute_reply.started":"2026-07-10T22:26:33.597231Z","shell.execute_reply":"2026-07-10T22:26:36.051815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Kompilasi model\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n    loss='binary_crossentropy',\n    metrics=['accuracy', tf.keras.metrics.AUC(name='auc')]\n)\n\n# Callback\ncallbacks = [\n    tf.keras.callbacks.EarlyStopping(\n        patience=5, \n        monitor='val_auc', \n        mode='max', \n        restore_best_weights=True,\n        verbose=1\n    ),\n    tf.keras.callbacks.ReduceLROnPlateau(\n        monitor='val_loss', \n        factor=0.2, \n        patience=3, \n        min_lr=1e-6,\n        verbose=1\n    ),\n    tf.keras.callbacks.ModelCheckpoint(\n        filepath='best_model.h5',\n        save_best_only=True,\n        monitor='val_auc',\n        mode='max',\n        verbose=1\n    )\n]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:36.053454Z","iopub.execute_input":"2026-07-10T22:26:36.053812Z","iopub.status.idle":"2026-07-10T22:26:36.082500Z","shell.execute_reply.started":"2026-07-10T22:26:36.053737Z","shell.execute_reply":"2026-07-10T22:26:36.081838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models, regularizers\n\n\ndef conv_block(x, filters, l2_weight=1e-4, dropout_rate=0.0):\n    \"\"\"\n    Bloc Convolution 3D -> BatchNorm -> ReLU -> MaxPooling\n    \"\"\"\n\n    x = layers.Conv3D(\n        filters=filters,\n        kernel_size=(3, 3, 3),\n        padding='same',\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(l2_weight)\n    )(x)\n\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation('relu')(x)\n\n    if dropout_rate > 0:\n        x = layers.SpatialDropout3D(dropout_rate)(x)\n\n    x = layers.MaxPooling3D(pool_size=(2, 2, 2))(x)\n\n    return x\n\n\ndef build_3d_cnn(\n        input_shape,\n        num_classes=1,\n        l2_weight=1e-4\n):\n    inputs = layers.Input(shape=input_shape)\n\n    # Feature Extraction\n    x = conv_block(inputs, 32, l2_weight, 0.10)\n    x = conv_block(x, 64, l2_weight, 0.15)\n    x = conv_block(x, 128, l2_weight, 0.20)\n    x = conv_block(x, 256, l2_weight, 0.25)\n\n    # Global Representation\n    x = layers.GlobalAveragePooling3D()(x)\n\n    # Fully Connected Layers\n    x = layers.Dense(\n        512,\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(l2_weight)\n    )(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation('relu')(x)\n    x = layers.Dropout(0.5)(x)\n\n    x = layers.Dense(\n        256,\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(l2_weight)\n    )(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation('relu')(x)\n    x = layers.Dropout(0.4)(x)\n\n    # Output Layer\n    if num_classes == 1:\n        outputs = layers.Dense(1, activation='sigmoid')(x)\n    else:\n        outputs = layers.Dense(num_classes, activation='softmax')(x)\n\n    model = models.Model(inputs, outputs, name=\"Improved_3D_CNN\")\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:36.083281Z","iopub.execute_input":"2026-07-10T22:26:36.083507Z","iopub.status.idle":"2026-07-10T22:26:36.093140Z","shell.execute_reply.started":"2026-07-10T22:26:36.083490Z","shell.execute_reply":"2026-07-10T22:26:36.092247Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = build_3d_cnn(input_shape=(64, 64, 64, 1))\n\nmodel.compile(\n    optimizer=tf.keras.optimizers.AdamW(\n        learning_rate=1e-4,\n        weight_decay=1e-5\n    ),\n    loss='binary_crossentropy',\n    metrics=[\n        'accuracy',\n        tf.keras.metrics.AUC(name='auc'),\n        tf.keras.metrics.Precision(name='precision'),\n        tf.keras.metrics.Recall(name='recall')\n    ]\n)\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:36.093998Z","iopub.execute_input":"2026-07-10T22:26:36.094287Z","iopub.status.idle":"2026-07-10T22:26:36.300730Z","shell.execute_reply.started":"2026-07-10T22:26:36.094261Z","shell.execute_reply":"2026-07-10T22:26:36.299819Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow.keras import layers, models, regularizers\nfrom tensorflow.keras import backend as K\n\n# Important si un ancien modèle existe déjà en mémoire\nK.clear_session()\n\n\n# --------------------------------------------------\n# Bloc convolutionnel\n# --------------------------------------------------\ndef conv_block(x, filters, l2_weight=1e-4, dropout_rate=0.0):\n\n    x = layers.Conv3D(\n        filters=filters,\n        kernel_size=(3, 3, 3),\n        padding='same',\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(l2_weight)\n    )(x)\n\n    x = layers.BatchNormalization()(x)\n    x = layers.Activation('relu')(x)\n\n    if dropout_rate > 0:\n        x = layers.SpatialDropout3D(dropout_rate)(x)\n\n    x = layers.MaxPooling3D(pool_size=(2, 2, 2))(x)\n\n    return x\n\n\n# --------------------------------------------------\n# Modèle 3D CNN\n# --------------------------------------------------\ndef build_3d_cnn(input_shape):\n\n    inputs = layers.Input(shape=input_shape)\n\n    x = conv_block(inputs, 32, dropout_rate=0.10)\n    x = conv_block(x, 64, dropout_rate=0.15)\n    x = conv_block(x, 128, dropout_rate=0.20)\n    x = conv_block(x, 256, dropout_rate=0.25)\n\n    x = layers.GlobalAveragePooling3D()(x)\n\n    x = layers.Dense(\n        512,\n        activation='relu',\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(1e-4)\n    )(x)\n\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.5)(x)\n\n    x = layers.Dense(\n        256,\n        activation='relu',\n        kernel_initializer='he_normal',\n        kernel_regularizer=regularizers.l2(1e-4)\n    )(x)\n\n    x = layers.BatchNormalization()(x)\n    x = layers.Dropout(0.4)(x)\n\n    outputs = layers.Dense(1, activation='sigmoid')(x)\n\n    model = models.Model(\n        inputs=inputs,\n        outputs=outputs,\n        name=\"Improved_3D_CNN\"\n    )\n\n    return model\n\n\n# --------------------------------------------------\n# Création automatique avec la bonne taille\n# --------------------------------------------------\ninput_shape = X_train.shape[1:]\n\nprint(\"Input shape détecté :\", input_shape)\n\nmodel = build_3d_cnn(input_shape)\n\nprint(\"Input attendu par le modèle :\", model.input_shape)\n\n\n# --------------------------------------------------\n# Compilation\n# --------------------------------------------------\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(\n        learning_rate=1e-4\n    ),\n    loss='binary_crossentropy',\n    metrics=[\n        'accuracy',\n        tf.keras.metrics.AUC(name='auc')\n    ]\n)\n\n\n# --------------------------------------------------\n# Résumé du modèle\n# --------------------------------------------------\nmodel.summary()\n\n\n# --------------------------------------------------\n# Entraînement\n# --------------------------------------------------\nhistory = model.fit(\n    X_train,\n    y_train,\n    validation_data=(X_val, y_val),\n    epochs=20,\n    batch_size=8,\n    verbose=1\n)\n\n\n# --------------------------------------------------\n# Validation\n# --------------------------------------------------\nprint(\"\\n===== Validation Set =====\")\n\nval_results = model.evaluate(\n    X_val,\n    y_val,\n    verbose=1\n)\n\nfor name, value in zip(model.metrics_names, val_results):\n    print(f\"{name}: {value:.4f}\")\n\n\n# --------------------------------------------------\n# Test\n# --------------------------------------------------\nprint(\"\\n===== Test Set =====\")\n\ntest_results = model.evaluate(\n    X_test,\n    y_test,\n    verbose=1\n)\n\nfor name, value in zip(model.metrics_names, test_results):\n    print(f\"{name}: {value:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:26:36.302210Z","iopub.execute_input":"2026-07-10T22:26:36.302500Z","iopub.status.idle":"2026-07-10T22:29:18.430683Z","shell.execute_reply.started":"2026-07-10T22:26:36.302482Z","shell.execute_reply":"2026-07-10T22:29:18.429916Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Prediksi pada test set\ny_pred_prob = model.predict(X_test).flatten()\ny_pred = (y_pred_prob > 0.5).astype(int)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:29:18.431797Z","iopub.execute_input":"2026-07-10T22:29:18.432514Z","iopub.status.idle":"2026-07-10T22:29:21.714259Z","shell.execute_reply.started":"2026-07-10T22:29:18.432484Z","shell.execute_reply":"2026-07-10T22:29:21.713489Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Classification report\nprint(\"\\nClassification Report:\")\nprint(classification_report(y_test, y_pred))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:29:21.715252Z","iopub.execute_input":"2026-07-10T22:29:21.715892Z","iopub.status.idle":"2026-07-10T22:29:21.730789Z","shell.execute_reply.started":"2026-07-10T22:29:21.715870Z","shell.execute_reply":"2026-07-10T22:29:21.729730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Confusion matrix\nconf_matrix = confusion_matrix(y_test, y_pred)\nprint(\"\\nConfusion Matrix:\")\nprint(conf_matrix)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:29:21.734425Z","iopub.execute_input":"2026-07-10T22:29:21.735007Z","iopub.status.idle":"2026-07-10T22:29:21.743489Z","shell.execute_reply.started":"2026-07-10T22:29:21.734986Z","shell.execute_reply":"2026-07-10T22:29:21.742468Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Plot ROC curve\nfpr, tpr, thresholds = roc_curve(y_test, y_pred_prob)\nroc_auc = auc(fpr, tpr)\n\nplt.figure()\nplt.plot(fpr, tpr, color='darkorange', lw=2, label=f'ROC curve (area = {roc_auc:.2f})')\nplt.plot([0, 1], [0, 1], color='navy', lw=2, linestyle='--')\nplt.xlim([0.0, 1.0])\nplt.ylim([0.0, 1.05])\nplt.xlabel('False Positive Rate')\nplt.ylabel('True Positive Rate')\nplt.title('Receiver Operating Characteristic')\nplt.legend(loc=\"lower right\")\nplt.savefig('roc_curve.png')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:29:21.744601Z","iopub.execute_input":"2026-07-10T22:29:21.745161Z","iopub.status.idle":"2026-07-10T22:29:22.040639Z","shell.execute_reply.started":"2026-07-10T22:29:21.745140Z","shell.execute_reply":"2026-07-10T22:29:22.039609Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Simpan model akhir\nmodel.save('final_model.h5')\nprint(\"\\nModel akhir disimpan sebagai 'final_model.h5'\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-10T22:29:22.042012Z","iopub.execute_input":"2026-07-10T22:29:22.042370Z","iopub.status.idle":"2026-07-10T22:29:22.582962Z","shell.execute_reply.started":"2026-07-10T22:29:22.042342Z","shell.execute_reply":"2026-07-10T22:29:22.582144Z"}},"outputs":[],"execution_count":null}]}