{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.8.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpuV5e8","dataSources":[{"sourceId":20270,"databundleVersionId":1222630,"sourceType":"competition"}],"dockerImageVersionId":30302,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!apt-get update -qq\n!apt-get install -y libgl1-mesa-glx libglib2.0-0 -qq\n!pip install seaborn plotly opencv-python scikit-learn tqdm --quiet","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:30:24.602800Z","iopub.execute_input":"2025-10-22T14:30:24.603096Z","iopub.status.idle":"2025-10-22T14:30:28.158387Z","shell.execute_reply.started":"2025-10-22T14:30:24.603047Z","shell.execute_reply":"2025-10-22T14:30:28.157328Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nfor dirname, _, filenames in os.walk('/kaggle/input'):\n    print(os.path.join(dirname))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:30:28.160036Z","iopub.execute_input":"2025-10-22T14:30:28.160240Z","iopub.status.idle":"2025-10-22T14:32:30.103113Z","shell.execute_reply.started":"2025-10-22T14:30:28.160222Z","shell.execute_reply":"2025-10-22T14:32:30.102492Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# --- CÀI THƯ VIỆN CẦN THIẾT ---\n!apt-get update -qq\n!apt-get install -y libgl1-mesa-glx libglib2.0-0 -qq\n!pip install seaborn plotly opencv-python scikit-learn tqdm --quiet\n\n# --- IMPORT THƯ VIỆN ---\nimport os, gc, re\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport plotly.graph_objects as go\nimport cv2\nimport tensorflow as tf\nfrom functools import partial\nfrom tqdm import tqdm_notebook as tqdm\n%matplotlib inline\n\n# --- KẾT NỐI TPU ---\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect()\n    strategy = tf.distribute.TPUStrategy(tpu)\n    print(\"✅ Connected to TPU:\", tpu.master())\nexcept:\n    print(\"❌ TPU not found, using default strategy.\")\n    strategy = tf.distribute.get_strategy()\n\n# --- ĐƯỜNG DẪN DATASET ---\nDATASET_PATH = \"/kaggle/input/siim-isic-melanoma-classification\"\nprint(\"📁 Dataset path:\", DATASET_PATH)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:32:30.103907Z","iopub.execute_input":"2025-10-22T14:32:30.104097Z","iopub.status.idle":"2025-10-22T14:32:33.667215Z","shell.execute_reply.started":"2025-10-22T14:32:30.104078Z","shell.execute_reply":"2025-10-22T14:32:33.666559Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nexcept:\n    strategy = tf.distribute.get_strategy()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:32:33.668085Z","iopub.execute_input":"2025-10-22T14:32:33.668266Z","iopub.status.idle":"2025-10-22T14:32:33.672160Z","shell.execute_reply.started":"2025-10-22T14:32:33.668246Z","shell.execute_reply":"2025-10-22T14:32:33.671617Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print('Number of replicas:', strategy.num_replicas_in_sync)\nprint(\"Version of Tensorflow used : \", tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:32:33.673693Z","iopub.execute_input":"2025-10-22T14:32:33.673881Z","iopub.status.idle":"2025-10-22T14:32:33.688662Z","shell.execute_reply.started":"2025-10-22T14:32:33.673865Z","shell.execute_reply":"2025-10-22T14:32:33.688220Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"AUTOTUNE = tf.data.experimental.AUTOTUNE\nGCS_PATH = \"/kaggle/input/siim-isic-melanoma-classification\"\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\nIMAGE_SIZE = [1024, 1024]\nSHAPE = [256, 256] ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:33:59.940670Z","iopub.execute_input":"2025-10-22T14:33:59.940966Z","iopub.status.idle":"2025-10-22T14:33:59.944903Z","shell.execute_reply.started":"2025-10-22T14:33:59.940946Z","shell.execute_reply":"2025-10-22T14:33:59.944429Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"Batch Size = \", BATCH_SIZE)\nprint(\"GCS Path = \", GCS_PATH)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:00.953877Z","iopub.execute_input":"2025-10-22T14:34:00.954134Z","iopub.status.idle":"2025-10-22T14:34:00.957408Z","shell.execute_reply.started":"2025-10-22T14:34:00.954108Z","shell.execute_reply":"2025-10-22T14:34:00.956917Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train = pd.DataFrame(pd.read_csv(\"../input/siim-isic-melanoma-classification/train.csv\"))\ntrain.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:02.276254Z","iopub.execute_input":"2025-10-22T14:34:02.276441Z","iopub.status.idle":"2025-10-22T14:34:02.371027Z","shell.execute_reply.started":"2025-10-22T14:34:02.276426Z","shell.execute_reply":"2025-10-22T14:34:02.370385Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test = pd.DataFrame(pd.read_csv(\"../input/siim-isic-melanoma-classification/test.csv\"))\ntest.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:02.805955Z","iopub.execute_input":"2025-10-22T14:34:02.806275Z","iopub.status.idle":"2025-10-22T14:34:02.836110Z","shell.execute_reply.started":"2025-10-22T14:34:02.806243Z","shell.execute_reply":"2025-10-22T14:34:02.835498Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:05.011922Z","iopub.execute_input":"2025-10-22T14:34:05.012281Z","iopub.status.idle":"2025-10-22T14:34:05.039024Z","shell.execute_reply.started":"2025-10-22T14:34:05.012252Z","shell.execute_reply":"2025-10-22T14:34:05.038518Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test.info()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:05.483990Z","iopub.execute_input":"2025-10-22T14:34:05.484239Z","iopub.status.idle":"2025-10-22T14:34:05.494728Z","shell.execute_reply.started":"2025-10-22T14:34:05.484213Z","shell.execute_reply":"2025-10-22T14:34:05.494256Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dir = \"/kaggle/input/siim-isic-melanoma-classification/jpeg/train/\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:07.075860Z","iopub.execute_input":"2025-10-22T14:34:07.076144Z","iopub.status.idle":"2025-10-22T14:34:07.079167Z","shell.execute_reply.started":"2025-10-22T14:34:07.076118Z","shell.execute_reply":"2025-10-22T14:34:07.078700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_names = train[\"image_name\"].values + \".jpg\"\nrandom_images = [np.random.choice(image_names) for i in range(4)] # Generates a random sample from a given 1-D array\nrandom_images ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:07.666644Z","iopub.execute_input":"2025-10-22T14:34:07.666856Z","iopub.status.idle":"2025-10-22T14:34:07.673223Z","shell.execute_reply.started":"2025-10-22T14:34:07.666838Z","shell.execute_reply":"2025-10-22T14:34:07.672752Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_images = []","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:08.133971Z","iopub.execute_input":"2025-10-22T14:34:08.134656Z","iopub.status.idle":"2025-10-22T14:34:08.137194Z","shell.execute_reply.started":"2025-10-22T14:34:08.134638Z","shell.execute_reply":"2025-10-22T14:34:08.136724Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize = (12, 8))\nfor i in range(4) : \n    plt.subplot(2, 2, i + 1) \n    image = cv2.imread(os.path.join(train_dir, random_images[i]))\n    # cv2 reads images in BGR format. Hence we convert it to RGB\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    sample_images.append(image)\n    plt.imshow(image, cmap = \"gray\")\n    plt.grid(True)\n# Automatically adjust subplot parameters to give specified padding.\nplt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:09.622702Z","iopub.execute_input":"2025-10-22T14:34:09.623021Z","iopub.status.idle":"2025-10-22T14:34:16.771981Z","shell.execute_reply.started":"2025-10-22T14:34:09.623001Z","shell.execute_reply":"2025-10-22T14:34:16.771394Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def non_local_means_denoising(image) : \n    denoised_image = cv2.fastNlMeansDenoisingColored(image, None, 10, 10, 7, 21)\n    return denoised_image","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:16.773092Z","iopub.execute_input":"2025-10-22T14:34:16.773273Z","iopub.status.idle":"2025-10-22T14:34:16.776778Z","shell.execute_reply.started":"2025-10-22T14:34:16.773254Z","shell.execute_reply":"2025-10-22T14:34:16.776316Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_image = cv2.imread(os.path.join(train_dir, random_images[0]))\n# cv2 reads images in BGR format. Hence we convert it to RGB\nsample_image = cv2.cvtColor(sample_image, cv2.COLOR_BGR2RGB)\ndenoised_image = non_local_means_denoising(sample_image)\n\n\nplt.figure(figsize = (12, 8))\nplt.subplot(1,2,1)\nplt.imshow(sample_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Normal Image\")\n\nplt.subplot(1,2,2)  \nplt.imshow(denoised_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Denoised image\")    \n# Automatically adjust subplot parameters to give specified padding.\nplt.tight_layout() ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:16.777380Z","iopub.execute_input":"2025-10-22T14:34:16.777536Z","iopub.status.idle":"2025-10-22T14:34:17.648915Z","shell.execute_reply.started":"2025-10-22T14:34:16.777521Z","shell.execute_reply":"2025-10-22T14:34:17.648321Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def histogram_equalization(image) : \n    image_ycrcb = cv2.cvtColor(image, cv2.COLOR_RGB2YCR_CB)\n    y_channel = image_ycrcb[:,:,0] # apply local histogram processing on this channel\n    cr_channel = image_ycrcb[:,:,1]\n    cb_channel = image_ycrcb[:,:,2]\n    \n    # Local histogram equalization\n    clahe = cv2.createCLAHE(clipLimit = 2.0, tileGridSize=(8,8))\n    equalized = clahe.apply(y_channel)\n    equalized_image = cv2.merge([equalized, cr_channel, cb_channel])\n    equalized_image = cv2.cvtColor(equalized_image, cv2.COLOR_YCR_CB2RGB)\n    return equalized_image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:17.650378Z","iopub.execute_input":"2025-10-22T14:34:17.650565Z","iopub.status.idle":"2025-10-22T14:34:17.654615Z","shell.execute_reply.started":"2025-10-22T14:34:17.650547Z","shell.execute_reply":"2025-10-22T14:34:17.654160Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"equalized_image = histogram_equalization(denoised_image)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:17.655334Z","iopub.execute_input":"2025-10-22T14:34:17.655515Z","iopub.status.idle":"2025-10-22T14:34:17.665450Z","shell.execute_reply.started":"2025-10-22T14:34:17.655498Z","shell.execute_reply":"2025-10-22T14:34:17.664927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize = (12, 8))\nplt.subplot(1,3,1)\nplt.imshow(sample_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Normal Image\", fontsize = 14)\n\nplt.subplot(1,3,2)  \nplt.imshow(denoised_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"denoised image after histogram processing\", fontsize = 14)\n\nplt.subplot(1,3,3)  \nplt.imshow(equalized_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Histogram equalized image\", fontsize = 14)\n# Automatically adjust subplot parameters to give specified padding.\nplt.tight_layout()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:17.666156Z","iopub.execute_input":"2025-10-22T14:34:17.666365Z","iopub.status.idle":"2025-10-22T14:34:18.214852Z","shell.execute_reply.started":"2025-10-22T14:34:17.666347Z","shell.execute_reply":"2025-10-22T14:34:18.214271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def segmentation(image, k, attempts) : \n    vectorized = np.float32(image.reshape((-1, 3)))\n    criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)\n    res , label , center = cv2.kmeans(vectorized, k, None, criteria, attempts, cv2.KMEANS_PP_CENTERS)\n    center = np.uint8(center)\n    res = center[label.flatten()]\n    segmented_image = res.reshape((image.shape))\n    return segmented_image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:18.215593Z","iopub.execute_input":"2025-10-22T14:34:18.215758Z","iopub.status.idle":"2025-10-22T14:34:18.219547Z","shell.execute_reply.started":"2025-10-22T14:34:18.215741Z","shell.execute_reply":"2025-10-22T14:34:18.219080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize = (12, 8))\nplt.subplot(1,1,1)\nplt.imshow(denoised_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"de Noised Image\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:18.220168Z","iopub.execute_input":"2025-10-22T14:34:18.220335Z","iopub.status.idle":"2025-10-22T14:34:18.522534Z","shell.execute_reply.started":"2025-10-22T14:34:18.220320Z","shell.execute_reply":"2025-10-22T14:34:18.521939Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize = (12, 8))\nsegmented_image = segmentation(denoised_image, 3, 10) # k = 3, attempt = 10\nplt.subplot(1,3,1)\nplt.imshow(segmented_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Segmented Image with k = 3\")\n\nsegmented_image = segmentation(denoised_image, 4, 10) # k = 4, attempt = 10\nplt.subplot(1,3,2)\nplt.imshow(segmented_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Segmented Image with k = 4\")\n\nsegmented_image = segmentation(denoised_image, 5, 10) # k = 5, attempt = 10\nplt.subplot(1,3,3)\nplt.imshow(segmented_image, cmap = \"gray\")\nplt.grid(False)\nplt.title(\"Segmented Image with k = 5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:18.523271Z","iopub.execute_input":"2025-10-22T14:34:18.523459Z","iopub.status.idle":"2025-10-22T14:34:19.571136Z","shell.execute_reply.started":"2025-10-22T14:34:18.523442Z","shell.execute_reply":"2025-10-22T14:34:19.570531Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split \ntraining_files, validation_files = train_test_split(tf.io.gfile.glob(GCS_PATH + \"/tfrecords/train*.tfrec\"),\n                                                   test_size = 0.1, random_state = 42)\n\ntesting_files = tf.io.gfile.glob(GCS_PATH + \"/tfrecords/test*.tfrec\")\n\nprint(\"Number of training files = \", len(training_files))\nprint(\"Number of validation files = \", len(validation_files))\nprint(\"Number of test files = \", len(testing_files))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.573069Z","iopub.execute_input":"2025-10-22T14:34:19.573250Z","iopub.status.idle":"2025-10-22T14:34:19.688716Z","shell.execute_reply.started":"2025-10-22T14:34:19.573233Z","shell.execute_reply":"2025-10-22T14:34:19.688085Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_image(image) : \n    image = tf.image.decode_jpeg(image, channels = 3)\n    image = tf.cast(image, tf.float32)\n    image = image / 255.0\n    image = tf.reshape(image, [IMAGE_SIZE[0], IMAGE_SIZE[1], 3])\n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.689474Z","iopub.execute_input":"2025-10-22T14:34:19.689875Z","iopub.status.idle":"2025-10-22T14:34:19.693524Z","shell.execute_reply.started":"2025-10-22T14:34:19.689855Z","shell.execute_reply":"2025-10-22T14:34:19.693025Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_images[0].shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.694169Z","iopub.execute_input":"2025-10-22T14:34:19.694349Z","iopub.status.idle":"2025-10-22T14:34:19.704477Z","shell.execute_reply.started":"2025-10-22T14:34:19.694333Z","shell.execute_reply":"2025-10-22T14:34:19.703957Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"training_files","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.705091Z","iopub.execute_input":"2025-10-22T14:34:19.705260Z","iopub.status.idle":"2025-10-22T14:34:19.712764Z","shell.execute_reply.started":"2025-10-22T14:34:19.705244Z","shell.execute_reply":"2025-10-22T14:34:19.712277Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_picked = training_files[0]\nsample_picked","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.713406Z","iopub.execute_input":"2025-10-22T14:34:19.713581Z","iopub.status.idle":"2025-10-22T14:34:19.721494Z","shell.execute_reply.started":"2025-10-22T14:34:19.713565Z","shell.execute_reply":"2025-10-22T14:34:19.720893Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"file = tf.data.TFRecordDataset(sample_picked)\nfile","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.722099Z","iopub.execute_input":"2025-10-22T14:34:19.722266Z","iopub.status.idle":"2025-10-22T14:34:19.769025Z","shell.execute_reply.started":"2025-10-22T14:34:19.722250Z","shell.execute_reply":"2025-10-22T14:34:19.768507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"feature_description = {\"image\" : tf.io.FixedLenFeature([], tf.string), \n                      \"target\" : tf.io.FixedLenFeature([], tf.int64)}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.769765Z","iopub.execute_input":"2025-10-22T14:34:19.769933Z","iopub.status.idle":"2025-10-22T14:34:19.772872Z","shell.execute_reply.started":"2025-10-22T14:34:19.769917Z","shell.execute_reply":"2025-10-22T14:34:19.772406Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def parse_function(example) : \n    # The example supplied is parsed based on the feature_description above.\n    return tf.io.parse_single_example(example, feature_description)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.773465Z","iopub.execute_input":"2025-10-22T14:34:19.773621Z","iopub.status.idle":"2025-10-22T14:34:19.780317Z","shell.execute_reply.started":"2025-10-22T14:34:19.773606Z","shell.execute_reply":"2025-10-22T14:34:19.779812Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"parsed_dataset = file.map(parse_function)\nparsed_dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.780942Z","iopub.execute_input":"2025-10-22T14:34:19.781122Z","iopub.status.idle":"2025-10-22T14:34:19.815204Z","shell.execute_reply.started":"2025-10-22T14:34:19.781107Z","shell.execute_reply":"2025-10-22T14:34:19.814730Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def read_tfrecord(example, labeled) : \n    if labeled == True : \n        tfrecord_format = {\"image\" : tf.io.FixedLenFeature([], tf.string),\n                           \"target\" : tf.io.FixedLenFeature([], tf.int64)}\n    else:\n        tfrecord_format = {\"image\" : tf.io.FixedLenFeature([], tf.string),\n                          \"image_name\" : tf.io.FixedLenFeature([], tf.string)}\n    \n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example[\"image\"])\n    if labeled == True : \n        label = tf.cast(example[\"target\"], tf.int32)\n        return image, label\n    else:\n        image_name = example[\"image_name\"]\n        return image, image_name     ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.815786Z","iopub.execute_input":"2025-10-22T14:34:19.815943Z","iopub.status.idle":"2025-10-22T14:34:19.820019Z","shell.execute_reply.started":"2025-10-22T14:34:19.815927Z","shell.execute_reply":"2025-10-22T14:34:19.819507Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def load_dataset(filenames, labeled, ordered):\n    ignore_order = tf.data.Options()\n    if ordered == False: # dataset is unordered, so we ignore the order to load data quickly.\n        ignore_order.experimental_deterministic = False # This disables the order and enhances the speed\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE) \n    dataset = dataset.with_options(ignore_order) \n    dataset = dataset.map(partial(read_tfrecord, labeled=labeled), num_parallel_calls=AUTOTUNE)\n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:19.820696Z","iopub.execute_input":"2025-10-22T14:34:19.820876Z","iopub.status.idle":"2025-10-22T14:34:19.827762Z","shell.execute_reply.started":"2025-10-22T14:34:19.820859Z","shell.execute_reply":"2025-10-22T14:34:19.827279Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def image_augmentation(image, label) :     \n    image = tf.image.resize(image, SHAPE)\n    image = tf.image.random_flip_left_right(image)\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:21.119563Z","iopub.execute_input":"2025-10-22T14:34:21.119848Z","iopub.status.idle":"2025-10-22T14:34:21.123620Z","shell.execute_reply.started":"2025-10-22T14:34:21.119830Z","shell.execute_reply":"2025-10-22T14:34:21.123007Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Load The Datasets : ","metadata":{}},{"cell_type":"code","source":"def get_training_dataset() : \n    dataset = load_dataset(training_files, labeled = True, ordered = False)\n    dataset = dataset.map(image_augmentation, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.repeat()\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE) \n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:21.369892Z","iopub.execute_input":"2025-10-22T14:34:21.370143Z","iopub.status.idle":"2025-10-22T14:34:21.373897Z","shell.execute_reply.started":"2025-10-22T14:34:21.370123Z","shell.execute_reply":"2025-10-22T14:34:21.373396Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_validation_dataset() : \n    dataset = load_dataset(validation_files, labeled = True, ordered = False)\n    dataset = dataset.map(image_augmentation, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTOTUNE) \n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:21.487520Z","iopub.execute_input":"2025-10-22T14:34:21.487693Z","iopub.status.idle":"2025-10-22T14:34:21.491006Z","shell.execute_reply.started":"2025-10-22T14:34:21.487678Z","shell.execute_reply":"2025-10-22T14:34:21.490558Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_test_dataset() : \n    dataset = load_dataset(testing_files, labeled = False, ordered = True)\n    dataset = dataset.map(image_augmentation, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.cache()\n    dataset = dataset.prefetch(AUTOTUNE) \n    return dataset","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:22.957207Z","iopub.execute_input":"2025-10-22T14:34:22.957488Z","iopub.status.idle":"2025-10-22T14:34:22.961265Z","shell.execute_reply.started":"2025-10-22T14:34:22.957471Z","shell.execute_reply":"2025-10-22T14:34:22.960761Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"training_dataset = get_training_dataset()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:23.080339Z","iopub.execute_input":"2025-10-22T14:34:23.080540Z","iopub.status.idle":"2025-10-22T14:34:23.238299Z","shell.execute_reply.started":"2025-10-22T14:34:23.080525Z","shell.execute_reply":"2025-10-22T14:34:23.237612Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"validation_dataset = get_validation_dataset()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:23.239328Z","iopub.execute_input":"2025-10-22T14:34:23.239530Z","iopub.status.idle":"2025-10-22T14:34:23.273780Z","shell.execute_reply.started":"2025-10-22T14:34:23.239513Z","shell.execute_reply":"2025-10-22T14:34:23.273297Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)\n\nnum_training_images = count_data_items(training_files)\nnum_validation_images = count_data_items(validation_files)\nnum_testing_images = count_data_items(testing_files)\n\nSTEPS_PER_EPOCH_TRAIN = num_training_images // BATCH_SIZE\nSTEPS_PER_EPOCH_VAL = num_validation_images // BATCH_SIZE\n\nprint(\"Number of Training Images = \", num_training_images)\nprint(\"Number of Validation Images = \", num_validation_images)\nprint(\"Number of Testing Images = \", num_testing_images)\nprint(\"\\n\")\nprint(\"Numer of steps per epoch in Train = \", STEPS_PER_EPOCH_TRAIN)\nprint(\"Numer of steps per epoch in Validation = \", STEPS_PER_EPOCH_VAL)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:23.309992Z","iopub.execute_input":"2025-10-22T14:34:23.310221Z","iopub.status.idle":"2025-10-22T14:34:23.315062Z","shell.execute_reply.started":"2025-10-22T14:34:23.310202Z","shell.execute_reply":"2025-10-22T14:34:23.314577Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_batch, label_batch = next(iter(training_dataset))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:25.365713Z","iopub.execute_input":"2025-10-22T14:34:25.365980Z","iopub.status.idle":"2025-10-22T14:34:25.495789Z","shell.execute_reply.started":"2025-10-22T14:34:25.365962Z","shell.execute_reply":"2025-10-22T14:34:25.495104Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_batch(image_batch, label_batch) :\n    plt.figure(figsize = (20, 20))\n    for n in range(8) : \n        ax = plt.subplot(2,4,n+1)\n        plt.imshow(image_batch[n])\n        if label_batch[n] == 0 : \n            plt.title(\"BENIGN\")\n        else:\n            plt.title(\"MALIGNANT\")\n    plt.grid(False)\n    plt.tight_layout()       ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:25.501896Z","iopub.execute_input":"2025-10-22T14:34:25.502137Z","iopub.status.idle":"2025-10-22T14:34:25.506208Z","shell.execute_reply.started":"2025-10-22T14:34:25.502117Z","shell.execute_reply":"2025-10-22T14:34:25.505601Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_batch(image_batch.numpy(), label_batch.numpy())","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:25.645840Z","iopub.execute_input":"2025-10-22T14:34:25.646079Z","iopub.status.idle":"2025-10-22T14:34:27.441704Z","shell.execute_reply.started":"2025-10-22T14:34:25.646037Z","shell.execute_reply":"2025-10-22T14:34:27.440987Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Let's free up some memory","metadata":{}},{"cell_type":"code","source":"del image_batch\ndel label_batch\ngc.collect()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:27.442872Z","iopub.execute_input":"2025-10-22T14:34:27.443082Z","iopub.status.idle":"2025-10-22T14:34:27.682513Z","shell.execute_reply.started":"2025-10-22T14:34:27.443047Z","shell.execute_reply":"2025-10-22T14:34:27.681868Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# Model Construction : ","metadata":{}},{"cell_type":"code","source":"malignant = len(train[train[\"target\"] == 1])\nbenign = len(train[train[\"target\"] == 0 ])\ntotal = len(train) \n\nprint(\"Malignant Cases in Train Data = \", malignant)\nprint(\"Benign Cases In Train Dataset = \",benign)\nprint(\"Total Cases In Train Dataset = \",total)\nprint(\"Ratio of Malignant to Benign = \",malignant/benign)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:27.683575Z","iopub.execute_input":"2025-10-22T14:34:27.683757Z","iopub.status.idle":"2025-10-22T14:34:27.693363Z","shell.execute_reply.started":"2025-10-22T14:34:27.683740Z","shell.execute_reply":"2025-10-22T14:34:27.692817Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"weight_malignant = (total/malignant)/2.0\nweight_benign = (total/benign)/2.0\n\nclass_weight = {0 : weight_benign , 1 : weight_malignant}\n\nprint(\"Weight for benign cases = \", class_weight[0])\nprint(\"Weight for malignant cases = \", class_weight[1])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:27.699819Z","iopub.execute_input":"2025-10-22T14:34:27.700002Z","iopub.status.idle":"2025-10-22T14:34:27.703678Z","shell.execute_reply.started":"2025-10-22T14:34:27.699985Z","shell.execute_reply":"2025-10-22T14:34:27.703167Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callback_early_stopping = tf.keras.callbacks.EarlyStopping(patience = 15, verbose = 0, restore_best_weights = True)\n\ncallbacks_lr_reduce = tf.keras.callbacks.ReduceLROnPlateau(monitor = \"val_auc\", factor = 0.1, patience = 10, \n                                                          verbose = 0, min_lr = 1e-6)\n\ncallback_checkpoint = tf.keras.callbacks.ModelCheckpoint(\"melanoma_weights.h5\",\n                                                         save_weights_only=True, monitor='val_auc',\n                                                         mode='max', save_best_only = True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:34:27.833630Z","iopub.execute_input":"2025-10-22T14:34:27.833834Z","iopub.status.idle":"2025-10-22T14:34:27.837746Z","shell.execute_reply.started":"2025-10-22T14:34:27.833816Z","shell.execute_reply":"2025-10-22T14:34:27.837209Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Model Design : MobileNetV2\n\nA supercool resource : **https://machinethink.net/blog/mobilenet-v2/**","metadata":{},"attachments":{}},{"cell_type":"markdown","source":"## Bias Initialization : \n\nSince the dataset is heavily imbalanced, we may want to assign different weights to different classes. Setting an initial bias is important in such cases.","metadata":{}},{"cell_type":"code","source":"with strategy.scope():\n    bias = np.log(malignant/benign)\n    bias = tf.keras.initializers.Constant(bias)\n    base_model = tf.keras.applications.MobileNetV2(\n        input_shape=(SHAPE[0], SHAPE[1], 3),\n        include_top=False,\n        weights=\"imagenet\"\n    )\n    base_model.trainable = False\n\n    model = tf.keras.Sequential([\n        base_model,\n        tf.keras.layers.GlobalAveragePooling2D(),\n        tf.keras.layers.Dense(20, activation=\"relu\"),\n        tf.keras.layers.Dropout(0.4),\n        tf.keras.layers.Dense(10, activation=\"relu\"),\n        tf.keras.layers.Dropout(0.3),\n        tf.keras.layers.Dense(1, activation=\"sigmoid\", bias_initializer=bias)\n    ])\n\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(lr=1e-2),\n        loss=\"binary_crossentropy\",\n        metrics=[tf.keras.metrics.AUC(name='auc')]\n    )\n\n    model.summary()\n\n    # --- ⏱️ BẮT ĐẦU ĐO THỜI GIAN ---\n    import time\n    start_time = time.time()\n\n    EPOCHS = 10\n    history = model.fit(\n        training_dataset,\n        epochs=EPOCHS,\n        steps_per_epoch=STEPS_PER_EPOCH_TRAIN,\n        validation_data=validation_dataset,\n        validation_steps=STEPS_PER_EPOCH_VAL,\n        callbacks=[callback_early_stopping, callbacks_lr_reduce, callback_checkpoint],\n        class_weight=class_weight\n    )\n\n    end_time = time.time()\n    training_time = end_time - start_time\n    print(f\"\\n⏱️ Thời gian huấn luyện tổng cộng: {training_time/60:.2f} phút ({training_time:.2f} giây)\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope() : \n    bias = np.log(malignant/benign)\n    bias = tf.keras.initializers.Constant(bias)\n    base_model = tf.keras.applications.MobileNetV2(input_shape = (SHAPE[0], SHAPE[1], 3), include_top = False,\n                                               weights = \"imagenet\")\n    base_model.trainable = False\n    model = tf.keras.Sequential([base_model,\n                                 tf.keras.layers.GlobalAveragePooling2D(),\n                                 tf.keras.layers.Dense(20, activation = \"relu\"),\n                                 tf.keras.layers.Dropout(0.4),\n                                 tf.keras.layers.Dense(10, activation = \"relu\"),\n                                 tf.keras.layers.Dropout(0.3),\n                                 tf.keras.layers.Dense(1, activation = \"sigmoid\", bias_initializer = bias)                                     \n                                ])\n    model.compile(optimizer = tf.keras.optimizers.Adam(lr = 1e-2), loss = \"binary_crossentropy\", metrics = [tf.keras.metrics.AUC(name = 'auc')])\n    model.summary()\n    \n    EPOCHS = 500\n    history = model.fit(training_dataset, epochs = EPOCHS, steps_per_epoch = STEPS_PER_EPOCH_TRAIN,\n                       validation_data = validation_dataset, validation_steps = STEPS_PER_EPOCH_VAL,\n                       callbacks = [callback_early_stopping, callbacks_lr_reduce, callback_checkpoint],\n                       class_weight = class_weight)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.316863Z","iopub.status.idle":"2025-10-22T14:44:47.317089Z","shell.execute_reply.started":"2025-10-22T14:44:47.316965Z","shell.execute_reply":"2025-10-22T14:44:47.316976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"n_epochs_it_ran_for = len(history.history['loss'])\nn_epochs_it_ran_for","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.317614Z","iopub.status.idle":"2025-10-22T14:44:47.317812Z","shell.execute_reply.started":"2025-10-22T14:44:47.317711Z","shell.execute_reply":"2025-10-22T14:44:47.317721Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"X = np.arange(0,n_epochs_it_ran_for,1)\nplt.figure(1, figsize = (20, 12))\nplt.subplot(1,2,1)\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Loss\")\nplt.plot(X, history.history[\"loss\"], label = \"Training Loss\")\nplt.plot(X, history.history[\"val_loss\"], label = \"Validation Loss\")\nplt.grid(True)\nplt.legend()\n\nplt.subplot(1,2,2)\nplt.xlabel(\"Epochs\")\nplt.ylabel(\"Accuracy\")\nplt.plot(X, history.history[\"auc\"], label = \"Training Accuracy\")\nplt.plot(X, history.history[\"val_auc\"], label = \"Validation Accuracy\")\nplt.grid(True)\nplt.legend()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.318257Z","iopub.status.idle":"2025-10-22T14:44:47.318457Z","shell.execute_reply.started":"2025-10-22T14:44:47.318358Z","shell.execute_reply":"2025-10-22T14:44:47.318368Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"Due to callbacks, best weights are automatically restored!","metadata":{}},{"cell_type":"code","source":"testing_dataset = get_test_dataset()\ntesting_dataset_images = testing_dataset.map(lambda image, image_name : image)\ntesting_image_names = testing_dataset.map(lambda image, image_name : image_name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.318956Z","iopub.status.idle":"2025-10-22T14:44:47.319171Z","shell.execute_reply.started":"2025-10-22T14:44:47.319051Z","shell.execute_reply":"2025-10-22T14:44:47.319080Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"resulting_probabilities = model.predict(testing_dataset_images, verbose = 1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.319563Z","iopub.status.idle":"2025-10-22T14:44:47.319754Z","shell.execute_reply.started":"2025-10-22T14:44:47.319655Z","shell.execute_reply":"2025-10-22T14:44:47.319665Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(resulting_probabilities)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.320208Z","iopub.status.idle":"2025-10-22T14:44:47.320402Z","shell.execute_reply.started":"2025-10-22T14:44:47.320305Z","shell.execute_reply":"2025-10-22T14:44:47.320315Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_submission_file = pd.read_csv(\"../input/siim-isic-melanoma-classification/sample_submission.csv\")\nsample_submission_file.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.320816Z","iopub.status.idle":"2025-10-22T14:44:47.321001Z","shell.execute_reply.started":"2025-10-22T14:44:47.320905Z","shell.execute_reply":"2025-10-22T14:44:47.320915Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"del sample_submission_file[\"target\"]\nsample_submission_file.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.321489Z","iopub.status.idle":"2025-10-22T14:44:47.321677Z","shell.execute_reply.started":"2025-10-22T14:44:47.321581Z","shell.execute_reply":"2025-10-22T14:44:47.321590Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"testing_image_names","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.322204Z","iopub.status.idle":"2025-10-22T14:44:47.322403Z","shell.execute_reply.started":"2025-10-22T14:44:47.322304Z","shell.execute_reply":"2025-10-22T14:44:47.322314Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"testing_image_names = np.concatenate([x for x in testing_image_names], axis=0)\ntesting_image_names = np.array(testing_image_names)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.322813Z","iopub.status.idle":"2025-10-22T14:44:47.322998Z","shell.execute_reply.started":"2025-10-22T14:44:47.322902Z","shell.execute_reply":"2025-10-22T14:44:47.322912Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"decoded_test_names = []\nfor names in testing_image_names : \n    names = names.decode('utf-8')\n    decoded_test_names.append(names)\ndecoded_test_names = np.array(decoded_test_names)\ndel testing_image_names","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.323393Z","iopub.status.idle":"2025-10-22T14:44:47.323577Z","shell.execute_reply.started":"2025-10-22T14:44:47.323482Z","shell.execute_reply":"2025-10-22T14:44:47.323491Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"len(decoded_test_names), type(decoded_test_names), decoded_test_names.shape","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.323979Z","iopub.status.idle":"2025-10-22T14:44:47.324188Z","shell.execute_reply.started":"2025-10-22T14:44:47.324083Z","shell.execute_reply":"2025-10-22T14:44:47.324093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"decoded_test_names","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.324595Z","iopub.status.idle":"2025-10-22T14:44:47.324786Z","shell.execute_reply.started":"2025-10-22T14:44:47.324685Z","shell.execute_reply":"2025-10-22T14:44:47.324694Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"testing_image_names = pd.DataFrame(decoded_test_names, columns=[\"image_name\"])\ntesting_image_names.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.325204Z","iopub.status.idle":"2025-10-22T14:44:47.325397Z","shell.execute_reply.started":"2025-10-22T14:44:47.325299Z","shell.execute_reply":"2025-10-22T14:44:47.325309Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred_dataframe = pd.DataFrame({\"image_name\" : decoded_test_names, \n                               \"target\" : np.concatenate(resulting_probabilities)})\npred_dataframe","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.325890Z","iopub.status.idle":"2025-10-22T14:44:47.326093Z","shell.execute_reply.started":"2025-10-22T14:44:47.325980Z","shell.execute_reply":"2025-10-22T14:44:47.325989Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sample_submission_file = sample_submission_file.merge(pred_dataframe, on = \"image_name\")\nsample_submission_file.to_csv(\"submission.csv\", index = False)\nsample_submission_file.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.326511Z","iopub.status.idle":"2025-10-22T14:44:47.326696Z","shell.execute_reply.started":"2025-10-22T14:44:47.326599Z","shell.execute_reply":"2025-10-22T14:44:47.326608Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"melanoma_model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-10-22T14:44:47.327136Z","iopub.status.idle":"2025-10-22T14:44:47.327333Z","shell.execute_reply.started":"2025-10-22T14:44:47.327228Z","shell.execute_reply":"2025-10-22T14:44:47.327238Z"}},"outputs":[],"execution_count":null}]}