{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","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":4104,"databundleVersionId":46661},{"sourceType":"datasetVersion","sourceId":265751,"datasetId":110097,"databundleVersionId":277918},{"sourceType":"datasetVersion","sourceId":2269470,"datasetId":1366461,"databundleVersionId":2310528},{"sourceType":"kernelVersion","sourceId":301654202}],"dockerImageVersionId":31287,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import torch\n\ntorch.cuda.is_available()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T17:34:10.267411Z","iopub.execute_input":"2026-03-05T17:34:10.268038Z","iopub.status.idle":"2026-03-05T17:34:10.273246Z","shell.execute_reply.started":"2026-03-05T17:34:10.268006Z","shell.execute_reply":"2026-03-05T17:34:10.27267Z"}},"outputs":[{"execution_count":5,"output_type":"execute_result","data":{"text/plain":"True"},"metadata":{}}],"execution_count":5},{"cell_type":"code","source":"import os\nimport glob\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\nimport tensorflow.keras.backend as K\nimport itertools\nimport seaborn as sns\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import SparseCategoricalCrossentropy\nfrom tensorflow.keras.layers import Dense, Flatten\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.data import Dataset\nfrom skimage.io import imread\nfrom sklearn.metrics import *\nfrom sklearn.model_selection import *\nfrom skimage.io import *\nfrom glob import glob\nimport warnings\n\n\nwarnings.filterwarnings('ignore')\nprint(\"Necessary modules have been imported\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T17:34:12.244305Z","iopub.execute_input":"2026-03-05T17:34:12.244569Z","iopub.status.idle":"2026-03-05T17:34:12.251001Z","shell.execute_reply.started":"2026-03-05T17:34:12.244549Z","shell.execute_reply":"2026-03-05T17:34:12.250271Z"}},"outputs":[{"name":"stdout","text":"Necessary modules have been imported\n","output_type":"stream"}],"execution_count":6},{"cell_type":"code","source":"\ndef parse_image(filename, label):\n    image = tf.io.read_file(filename)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [256, 256])\n    image = image / 255.0\n    return image, label\n\n\ndef load_dataset(file_paths, labels, batch_size=32):\n    dataset = Dataset.from_tensor_slices((file_paths, labels))\n    dataset = dataset.map(parse_image, num_parallel_calls=tf.data.AUTOTUNE)\n    dataset = dataset.shuffle(buffer_size=len(file_paths)).batch(batch_size)\n    dataset = dataset.prefetch(buffer_size=tf.data.AUTOTUNE)\n    return dataset\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T17:34:15.75285Z","iopub.execute_input":"2026-03-05T17:34:15.753555Z","iopub.status.idle":"2026-03-05T17:34:15.75852Z","shell.execute_reply.started":"2026-03-05T17:34:15.75353Z","shell.execute_reply":"2026-03-05T17:34:15.757821Z"}},"outputs":[],"execution_count":7},{"cell_type":"markdown","source":"## Importing labels","metadata":{}},{"cell_type":"code","source":"! ls\n!unzip -o ./diabetic-retinopathy-detection/trainLabels.csv.zip\ntrainLabels = pd.read_csv(\"./trainLabels.csv\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-05T17:37:20.734485Z","iopub.execute_input":"2026-03-05T17:37:20.73481Z","iopub.status.idle":"2026-03-05T17:37:21.012113Z","shell.execute_reply.started":"2026-03-05T17:37:20.734781Z","shell.execute_reply":"2026-03-05T17:37:21.01109Z"}},"outputs":[{"name":"stdout","text":"unzip:  cannot find or open ./diabetic-retinopathy-detection/trainLabels.csv.zip, ./diabetic-retinopathy-detection/trainLabels.csv.zip.zip or ./diabetic-retinopathy-detection/trainLabels.csv.zip.ZIP.\n","output_type":"stream"},{"traceback":["\u001b[0;31m---------------------------------------------------------------------------\u001b[0m","\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)","\u001b[0;32m/tmp/ipykernel_55/473467115.py\u001b[0m in \u001b[0;36m<cell line: 0>\u001b[0;34m()\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m' ls'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0mget_ipython\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msystem\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'unzip -o ./diabetic-retinopathy-detection/trainLabels.csv.zip'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 3\u001b[0;31m \u001b[0mtrainLabels\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mpd\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread_csv\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"./trainLabels.csv\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36mread_csv\u001b[0;34m(filepath_or_buffer, sep, delimiter, header, names, index_col, usecols, dtype, engine, converters, true_values, false_values, skipinitialspace, skiprows, skipfooter, nrows, na_values, keep_default_na, na_filter, verbose, skip_blank_lines, parse_dates, infer_datetime_format, keep_date_col, date_parser, date_format, dayfirst, cache_dates, iterator, chunksize, compression, thousands, decimal, lineterminator, quotechar, quoting, doublequote, escapechar, comment, encoding, encoding_errors, dialect, on_bad_lines, delim_whitespace, low_memory, memory_map, float_precision, storage_options, dtype_backend)\u001b[0m\n\u001b[1;32m   1024\u001b[0m     \u001b[0mkwds\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mupdate\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mkwds_defaults\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1025\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1026\u001b[0;31m     \u001b[0;32mreturn\u001b[0m \u001b[0m_read\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1027\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1028\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36m_read\u001b[0;34m(filepath_or_buffer, kwds)\u001b[0m\n\u001b[1;32m    618\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    619\u001b[0m     \u001b[0;31m# Create the parser.\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 620\u001b[0;31m     \u001b[0mparser\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mTextFileReader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfilepath_or_buffer\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m**\u001b[0m\u001b[0mkwds\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    621\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    622\u001b[0m     \u001b[0;32mif\u001b[0m \u001b[0mchunksize\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0miterator\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, f, engine, **kwds)\u001b[0m\n\u001b[1;32m   1618\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1619\u001b[0m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mhandles\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mIOHandles\u001b[0m \u001b[0;34m|\u001b[0m \u001b[0;32mNone\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1620\u001b[0;31m         \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_engine\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_make_engine\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mself\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mengine\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   1621\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1622\u001b[0m     \u001b[0;32mdef\u001b[0m \u001b[0mclose\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m->\u001b[0m \u001b[0;32mNone\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/parsers/readers.py\u001b[0m in \u001b[0;36m_make_engine\u001b[0;34m(self, f, engine)\u001b[0m\n\u001b[1;32m   1878\u001b[0m                 \u001b[0;32mif\u001b[0m \u001b[0;34m\"b\"\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1879\u001b[0m                     \u001b[0mmode\u001b[0m \u001b[0;34m+=\u001b[0m \u001b[0;34m\"b\"\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 1880\u001b[0;31m             self.handles = get_handle(\n\u001b[0m\u001b[1;32m   1881\u001b[0m                 \u001b[0mf\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   1882\u001b[0m                 \u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;32m/usr/local/lib/python3.12/dist-packages/pandas/io/common.py\u001b[0m in \u001b[0;36mget_handle\u001b[0;34m(path_or_buf, mode, encoding, compression, memory_map, is_text, errors, storage_options)\u001b[0m\n\u001b[1;32m    871\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mioargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencoding\u001b[0m \u001b[0;32mand\u001b[0m \u001b[0;34m\"b\"\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0;32min\u001b[0m \u001b[0mioargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    872\u001b[0m             \u001b[0;31m# Encoding\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 873\u001b[0;31m             handle = open(\n\u001b[0m\u001b[1;32m    874\u001b[0m                 \u001b[0mhandle\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    875\u001b[0m                 \u001b[0mioargs\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmode\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n","\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: './trainLabels.csv'"],"ename":"FileNotFoundError","evalue":"[Errno 2] No such file or directory: './trainLabels.csv'","output_type":"error"}],"execution_count":11},{"cell_type":"code","source":"!apt install p7zip-full -y\n!7z x ../input/diabetic-retinopathy-detection/train.zip.001 \"-i!train/11*.jpeg\" -y \n# restrict extracted file to about 100 for the disk restriction\n!mkdir data\n!mv train data/train_11\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 獲取所有圖像文件路徑\nfile_paths = glob(\"./data/train_11/*.jpeg\")\n\n# 假設file_paths包含圖像的完整路徑，從中提取圖像的基礎名稱\nfile_basenames = [os.path.basename(f).replace(\".jpeg\", \"\") for f in file_paths]\n\n# 根據文件名來篩選出對應的標籤\nfiltered_labels = trainLabels[trainLabels['image'].isin(file_basenames)]['level'].values\n\n# 確認file_paths與filtered_labels的數量相同\nprint(f\"Number of image files: {len(file_paths)}\")\nprint(f\"Number of filtered labels: {len(filtered_labels)}\")\n\n# 加載數據集\ndataset = load_dataset(file_paths, filtered_labels)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"\ndef show_batch(image_batch, label_batch):\n    plt.figure(figsize=(10, 10))\n    for n in range(5):  # 顯示5張圖片\n        ax = plt.subplot(1, 5, n + 1)\n        plt.imshow(image_batch[n])\n        plt.title(int(label_batch[n]))\n        plt.axis(\"off\")\n    plt.show()\n\n# 過濾出每個類別的一張圖片\ndef get_images_by_label(dataset, num_classes=5):\n    images = [None] * num_classes  # 用None初始化一個大小為num_classes的列表\n    labels = [None] * num_classes\n    label_counts = {i: 0 for i in range(num_classes)}  # 用來追蹤每個類別已經收集到的圖片數量\n\n    for image_batch, label_batch in dataset:\n        for img, lbl in zip(image_batch, label_batch):\n            label = int(lbl)\n            if label_counts[label] == 0:  # 只收集一次該類別的圖片\n                images[label] = img\n                labels[label] = lbl\n                label_counts[label] += 1\n            if sum(label_counts.values()) == num_classes:  # 如果已經收集到每個類別一張圖片，退出\n                return np.array(images), np.array(labels)\n    return np.array(images), np.array(labels)\n\n# 從數據集中獲取每個類別的圖片\nimage_batch, label_batch = get_images_by_label(dataset)\n\n# 按照類別順序排列圖片和標籤\nsorted_indices = np.argsort(label_batch)\nimage_batch = image_batch[sorted_indices]\nlabel_batch = label_batch[sorted_indices]\n\n# 顯示批量圖像\nshow_batch(image_batch, label_batch)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Filtering csv file","metadata":{}},{"cell_type":"code","source":"# Filtering and Balancing Data\nbase_image_dir = os.path.join('.', 'data/train_11')\ntrainLabels['path'] = trainLabels['image'].map(lambda x: os.path.join(base_image_dir,'{}.jpeg'.format(x)))\ntrainLabels['exists'] = trainLabels['path'].map(os.path.exists)  # Filter out missing images\ndf = trainLabels[trainLabels['exists']]\ndf = df.drop(columns=['image', 'exists'])\ndf = df.sample(frac=1).reset_index(drop=True)  # Shuffle dataframe\ndf['level'] = df['level'].astype(str)\ndf.head(10)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport pandas as pd\n\n# Đường dẫn tới thư mục chứa ảnh\nbase_image_dir = os.path.join('.', 'data/train_11')\n\n# Ánh xạ đường dẫn hình ảnh\ntrainLabels['path'] = trainLabels['image'].map(lambda x: os.path.join(base_image_dir, '{}.jpeg'.format(x)))\n\n# Kiểm tra sự tồn tại của các hình ảnh\ntrainLabels['exists'] = trainLabels['path'].map(os.path.exists)\n\n# Lọc ra những hình ảnh tồn tại\ndf = trainLabels[trainLabels['exists']]\n\n# Xóa cột không cần thiết\ndf = df.drop(columns=['image', 'exists'])\n\n# Keep all severity levels for multi-class classification\n# Xáo trộn dataframe\ndf = df.sample(frac=1).reset_index(drop=True)\n\n# Đảm bảo cột 'level' là kiểu chuỗi\ndf['level'] = df['level'].astype(str)\n\n# Hiển thị 10 dòng đầu tiên\nprint(df.head(10))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Visualize distribution\ndf['level'].hist(figsize=(10, 5))\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"**The dataset is highly imbalanced, with many samples for level 0, and very little for the rest of the levels.**","metadata":{}},{"cell_type":"markdown","source":"## Resnet 50","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, Input, Conv2D, multiply, Lambda, BatchNormalization\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.regularizers import l2\nimport numpy as np\nfrom tensorflow.keras.utils import plot_model\n\ndef create_deeper_attention_model(input_shape, num_classes):\n    in_lay = Input(input_shape)\n    \n    # Thay thế InceptionV3 bằng ResNet50\n    base_pretrained_model = ResNet50(input_shape=input_shape, include_top=False, weights='imagenet')\n    base_pretrained_model.trainable = False  # Đóng băng các lớp của ResNet50\n    \n    pt_depth = base_pretrained_model.output_shape[-1]\n    pt_features = base_pretrained_model(in_lay)\n    \n    bn_features = BatchNormalization()(pt_features)\n    \n    # Attention mechanism\n    attn_layer = Conv2D(128, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(Dropout(0.5)(bn_features))\n    attn_layer = Conv2D(64, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(attn_layer)\n    attn_layer = Conv2D(32, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(attn_layer)\n    attn_layer = Conv2D(1, kernel_size=(1,1), padding='valid', activation='sigmoid')(attn_layer)\n    \n    up_c2_w = np.ones((1, 1, 1, pt_depth))\n    up_c2 = Conv2D(pt_depth, kernel_size=(1,1), padding='same', activation='linear', use_bias=False, weights=[up_c2_w])\n    up_c2.trainable = False\n    attn_layer = up_c2(attn_layer)\n    \n    mask_features = multiply([attn_layer, bn_features])\n    gap_features = GlobalAveragePooling2D()(mask_features)\n    gap_mask = GlobalAveragePooling2D()(attn_layer)\n    \n    gap = Lambda(lambda x: x[0]/x[1], name='RescaleGAP')([gap_features, gap_mask])\n    gap_dr = Dropout(0.5)(gap)\n    dr_steps = Dropout(0.5)(Dense(256, activation='relu', kernel_regularizer=l2(0.001))(gap_dr))\n    out_layer = Dense(num_classes, activation='softmax')(dr_steps)\n    \n    model = Model(inputs=[in_lay], outputs=[out_layer])\n    \n    # Thay đổi learning rate\n    learning_rate = 0.0001\n    model.compile(optimizer=Adam(learning_rate=learning_rate), \n                  loss='categorical_crossentropy', \n                  metrics=['categorical_accuracy', 'AUC'])\n    \n    return model\n\n# Tạo mô hình với ResNet50\nattention_model = create_deeper_attention_model((256, 256, 3), 5)  # Có 5 lớp\nattention_model.summary()\n\n# Vẽ kiến trúc mô hình\nplot_model(attention_model, show_shapes=True, show_layer_names=True, dpi=60, to_file='model_architecture.png')\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":" **Model Training with Stratified K-Fold Cross-Validation and Epoch Incrementing**","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nimport matplotlib.pyplot as plt\nimport datetime\nimport numpy as np\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import confusion_matrix\n\n# Initialize lists to store fold results\nall_train_accu = []\nall_val_accu = []\nall_train_loss = []\nall_val_loss = []\nall_train_auc = []\nall_val_auc = []\n\nall_history = {\n    'categorical_accuracy': [],\n    'val_categorical_accuracy': [],\n    'loss': [],\n    'val_loss': [],\n    'auc': [],\n    'val_auc': []\n}\n\ndef balance_data(class_size, df):\n    train_df = df.groupby('level').apply(lambda x: x.sample(class_size, replace=True)).reset_index(drop=True)\n    train_df = train_df.sample(frac=1).reset_index(drop=True)\n    print('New Data Size:', train_df.shape[0], 'Old Size:', df.shape[0])\n    train_df['level'].hist(figsize=(10,5))\n    return train_df\n\nn_splits = 10\nskf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42)\n\ncumulative_cm = None\n\nfor fold_index, (train_index, val_index) in enumerate(skf.split(df, df['level'])):\n\n    print(f\"Training fold {fold_index + 1}/{n_splits}\")\n\n    train_df = df.iloc[train_index].copy()\n    val_df = df.iloc[val_index].copy()\n\n    max_class_size = train_df['level'].value_counts().max()\n    train_df = balance_data(max_class_size, train_df)\n\n    train_datagen = ImageDataGenerator(\n        rescale=1.0/255,\n        shear_range=0.2,\n        horizontal_flip=True,\n        zoom_range=0.2\n    )\n\n    test_datagen = ImageDataGenerator(rescale=1.0/255)\n\n    x_train = train_datagen.flow_from_dataframe(\n        dataframe=train_df,\n        directory=\".\",\n        x_col=\"path\",\n        y_col=\"level\",\n        target_size=(256,256),\n        batch_size=32,\n        class_mode='categorical',\n        shuffle=True\n    )\n\n    x_test = test_datagen.flow_from_dataframe(\n        dataframe=val_df,\n        directory=\".\",\n        x_col=\"path\",\n        y_col=\"level\",\n        target_size=(256,256),\n        batch_size=32,\n        class_mode='categorical',\n        shuffle=False\n    )\n\n    if cumulative_cm is None:\n        cumulative_cm = np.zeros(\n            (len(x_train.class_indices), len(x_train.class_indices)),\n            dtype=int\n        )\n\n    attention_model = create_deeper_attention_model((256,256,3), len(x_train.class_indices))\n\n    current_time = datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n\n    filepath = f\"dr-detector-fold{fold_index+1}-{current_time}-{{epoch:02d}}.hdf5\"\n\n    checkpoint = ModelCheckpoint(\n        filepath=filepath,\n        monitor=\"val_loss\",\n        verbose=1,\n        save_best_only=True,\n        mode=\"min\"\n    )\n\n    earlystop = EarlyStopping(\n        monitor='val_loss',\n        verbose=1,\n        min_delta=0,\n        patience=15,\n        restore_best_weights=True\n    )\n\n    reduce_lr = ReduceLROnPlateau(\n        monitor='val_loss',\n        verbose=1,\n        factor=0.1,\n        patience=7,\n        min_delta=0.0001,\n        cooldown=0,\n        min_lr=1e-7\n    )\n\n    callbacks = [checkpoint, earlystop, reduce_lr]\n\n    history = attention_model.fit(\n        x_train,\n        steps_per_epoch=max(1, x_train.samples // x_train.batch_size),\n        epochs=5,\n        validation_data=x_test,\n        validation_steps=max(1, x_test.samples // x_test.batch_size),\n        callbacks=callbacks\n    )\n\n    result_train = attention_model.evaluate(x_train, verbose=0)\n    result_test = attention_model.evaluate(x_test, verbose=0)\n\n    train_loss, train_accu, train_auc = result_train\n    test_loss, test_accu, test_auc = result_test\n\n    all_train_accu.append(train_accu)\n    all_val_accu.append(test_accu)\n\n    all_train_loss.append(train_loss)\n    all_val_loss.append(test_loss)\n\n    all_train_auc.append(train_auc)\n    all_val_auc.append(test_auc)\n\n    all_history['categorical_accuracy'].append(history.history.get('categorical_accuracy', []))\n    all_history['val_categorical_accuracy'].append(history.history.get('val_categorical_accuracy', []))\n    all_history['loss'].append(history.history.get('loss', []))\n    all_history['val_loss'].append(history.history.get('val_loss', []))\n        auc_key = next((k for k in history.history if k.startswith('auc')), 'auc')\n    val_auc_key = next((k for k in history.history if k.startswith('val_auc')), 'val_auc')\n    all_history['auc'].append(history.history.get(auc_key, []))\n    all_history['val_auc'].append(history.history.get(val_auc_key, []))\n\n    y_pred_train = attention_model.predict(x_train, verbose=0)\n    y_pred_train = np.argmax(y_pred_train, axis=1)\n\n    y_true_train = x_train.classes\n\n    cm_train = confusion_matrix(y_true_train, y_pred_train)\n    cumulative_cm += cm_train\n\n    print(f\"Final training accuracy for fold {fold_index+1} = {train_accu*100:.2f}%\")\n    print(f\"Final validation accuracy for fold {fold_index+1} = {test_accu*100:.2f}%\")\n\n    print(f\"Final training loss = {train_loss:.2f}\")\n    print(f\"Final validation loss = {test_loss:.2f}\")\n\n    print(f\"Final training AUC = {train_auc:.2f}\")\n    print(f\"Final validation AUC = {test_auc:.2f}\")\n\n\nmean_train_accu = np.mean(all_train_accu)\nmean_val_accu = np.mean(all_val_accu)\n\nmean_train_loss = np.mean(all_train_loss)\nmean_val_loss = np.mean(all_val_loss)\n\nmean_train_auc = np.mean(all_train_auc)\nmean_val_auc = np.mean(all_val_auc)\n\nstd_train_accu = np.std(all_train_accu)\nstd_val_accu = np.std(all_val_accu)\n\nstd_train_loss = np.std(all_train_loss)\nstd_val_loss = np.std(all_val_loss)\n\nstd_train_auc = np.std(all_train_auc)\nstd_val_auc = np.std(all_val_auc)\n\nprint(\"\\nOverall Results:\")\n\nprint(f\"Mean training accuracy = {mean_train_accu*100:.2f}% ± {std_train_accu*100:.2f}%\")\nprint(f\"Mean validation accuracy = {mean_val_accu*100:.2f}% ± {std_val_accu*100:.2f}%\")\n\nprint(f\"Mean training loss = {mean_train_loss:.2f} ± {std_train_loss:.2f}\")\nprint(f\"Mean validation loss = {mean_val_loss:.2f} ± {std_val_loss:.2f}\")\n\nprint(f\"Mean training AUC = {mean_train_auc:.2f} ± {std_train_auc:.2f}\")\nprint(f\"Mean validation AUC = {mean_val_auc:.2f} ± {std_val_auc:.2f}\")\n\nmean_history = {key: np.mean(np.array([ep for ep in all_history[key] if len(ep) > 0]), axis=0) for key in all_history.keys()}\n\nplt.figure(figsize=(18,6))\n\nplt.subplot(1,3,1)\nplt.plot(mean_history['categorical_accuracy'], label='Train Accuracy')\nplt.plot(mean_history['val_categorical_accuracy'], label='Test Accuracy')\nplt.title('Average Model Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend()\n\nplt.subplot(1,3,2)\nplt.plot(mean_history['loss'], label='Train Loss')\nplt.plot(mean_history['val_loss'], label='Test Loss')\nplt.title('Average Model Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend()\n\nplt.subplot(1,3,3)\nplt.plot(mean_history['auc'], label='Train AUC')\nplt.plot(mean_history['val_auc'], label='Test AUC')\nplt.title('Average Model AUC')\nplt.xlabel('Epochs')\nplt.ylabel('AUC')\nplt.legend()\n\nplt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Sau khi huấn luyện mô hình, lưu mô hình dưới dạng tệp .h5\nattention_model.save(\"resnet_attention_model.h5\")\nprint(\"Model saved as resnet_attention_model.h5\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"%%writefile app.py\n\nimport streamlit as st\nfrom PIL import Image\nimport numpy as np\nimport tensorflow as tf\n\n# Load your pre-trained model\nmodel = tf.keras.models.load_model('resnet_attention_model.h5', compile=False)\n\n# Function to preprocess the uploaded image\ndef preprocess_image(image):\n    image = image.resize((256, 256))  # Resize the image to the input size of your model\n    image = np.array(image)           # Convert to numpy array\n    image = image / 255.0             # Normalize pixel values\n    image = np.expand_dims(image, axis=0)  # Add batch dimension\n    return image\n\n# Streamlit app layout\nst.title(\"Image Classification App\")\nst.write(\"Upload an image to classify it using the pre-trained model\")\n\n# File uploader widget\nuploaded_file = st.file_uploader(\"Choose an image...\", type=\"jpg\")\n\nif uploaded_file is not None:\n    # Load the image from the uploaded file\n    image = Image.open(uploaded_file)\n\n    # Display the uploaded image\n    st.image(image, caption='Uploaded Image', use_column_width=True)\n\n    # Preprocess the image\n    processed_image = preprocess_image(image)\n\n    # Make predictions using the model\n    prediction = model.predict(processed_image)\n\n    # Display the prediction result\n    st.write(\"Predicted class:\", np.argmax(prediction))\n\n    # Display the confidence scores for each class\n    st.write(\"Confidence scores:\", prediction)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"With LR_","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, Input, Conv2D, multiply, Lambda, BatchNormalization\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.regularizers import l2\nimport numpy as np\nfrom tensorflow.keras.utils import plot_model\n\ndef create_deeper_attention_model(input_shape, num_classes, learning_rate=0.0001):\n    in_lay = Input(input_shape)\n    \n    # Thay thế InceptionV3 bằng ResNet50\n    base_pretrained_model = ResNet50(input_shape=input_shape, include_top=False, weights='imagenet')\n    base_pretrained_model.trainable = False  # Đóng băng các lớp của ResNet50\n    \n    pt_depth = base_pretrained_model.output_shape[-1]\n    pt_features = base_pretrained_model(in_lay)\n    \n    bn_features = BatchNormalization()(pt_features)\n    \n    # Attention mechanism\n    attn_layer = Conv2D(128, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(Dropout(0.5)(bn_features))\n    attn_layer = Conv2D(64, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(attn_layer)\n    attn_layer = Conv2D(32, kernel_size=(3,3), padding='same', activation='relu', kernel_regularizer=l2(0.001))(attn_layer)\n    attn_layer = Conv2D(1, kernel_size=(1,1), padding='valid', activation='sigmoid')(attn_layer)\n    \n    up_c2_w = np.ones((1, 1, 1, pt_depth))\n    up_c2 = Conv2D(pt_depth, kernel_size=(1,1), padding='same', activation='linear', use_bias=False, weights=[up_c2_w])\n    up_c2.trainable = False\n    attn_layer = up_c2(attn_layer)\n    \n    mask_features = multiply([attn_layer, bn_features])\n    gap_features = GlobalAveragePooling2D()(mask_features)\n    gap_mask = GlobalAveragePooling2D()(attn_layer)\n    \n    gap = Lambda(lambda x: x[0]/x[1], name='RescaleGAP')([gap_features, gap_mask])\n    gap_dr = Dropout(0.5)(gap)\n    dr_steps = Dropout(0.5)(Dense(256, activation='relu', kernel_regularizer=l2(0.001))(gap_dr))\n    out_layer = Dense(num_classes, activation='softmax')(dr_steps)\n    \n    model = Model(inputs=[in_lay], outputs=[out_layer])\n    \n    # Thay đổi learning rate\n    model.compile(optimizer=Adam(learning_rate=learning_rate), \n                  loss='categorical_crossentropy', \n                  metrics=['categorical_accuracy', 'AUC'])\n    \n    return model\n\n# Tạo mô hình với ResNet50\nattention_model = create_deeper_attention_model((256, 256, 3), 5)  # Có 5 lớp\nattention_model.summary()\n\n# Vẽ kiến trúc mô hình\nplot_model(attention_model, show_shapes=True, show_layer_names=True, dpi=60, to_file='model_architecture.png')\n\nfrom tensorflow.keras.callbacks import ModelCheckpoint, EarlyStopping, ReduceLROnPlateau\nimport matplotlib.pyplot as plt\nimport datetime\nimport numpy as np\nfrom sklearn.model_selection import StratifiedKFold\nfrom sklearn.metrics import classification_report, confusion_matrix\nimport itertools\n\n# Initialize lists to store fold results\nall_train_accu = []\nall_val_accu = []\nall_train_loss = []\nall_val_loss = []\nall_train_auc = []\nall_val_auc = []\nall_history = {'categorical_accuracy': [], 'val_categorical_accuracy': [], \n               'loss': [], 'val_loss': [], 'auc': [], 'val_auc': []}\n\n# Balance data function\ndef balance_data(class_size, df):\n    train_df = df.groupby(['level']).apply(lambda x: x.sample(class_size, replace=True)).reset_index(drop=True)\n    train_df = train_df.sample(frac=1).reset_index(drop=True)\n    print('New Data Size:', train_df.shape[0], 'Old Size:', df.shape[0])\n    train_df['level'].hist(figsize=(10, 5))\n    return train_df\n\n# Initialize StratifiedKFold\nn_splits = 10\nskf = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=42)\n\n# Initialize cumulative confusion matrix\ncumulative_cm = None\n\n# Iterate through each fold\nfor fold_index, (train_index, val_index) in enumerate(skf.split(df, df['level'])):\n    print(f\"Training fold {fold_index + 1}/{n_splits}\")\n\n    # Split data into training and validation sets\n    train_df, val_df = df.iloc[train_index], df.iloc[val_index]\n\n    # Ensure balanced class distribution\n    max_class_size = train_df.pivot_table(index='level', aggfunc=len).max().max()\n    train_df = balance_data(max_class_size, train_df)\n\n    # ImageDataGenerator for training and validation\n    train_datagen = ImageDataGenerator(rescale=1.0/255, shear_range=0.2, horizontal_flip=True, zoom_range=0.2)\n    test_datagen = ImageDataGenerator(rescale=1.0/255)\n\n    # Batch size = 64\n    x_train = train_datagen.flow_from_dataframe(train_df,directory=\".\",x_col=\"path\",y_col=\"level\",target_size=(256, 256),batch_size=64,class_mode='categorical',shuffle=False)\n    x_test = test_datagen.flow_from_dataframe(    val_df,    directory=\".\",    x_col=\"path\",    y_col=\"level\",    target_size=(256, 256),    batch_size=64,   class_mode='categorical',shuffle=False)\n\n    # Initialize cumulative confusion matrix\n    if cumulative_cm is None:\n        cumulative_cm = np.zeros((len(x_train.class_indices), len(x_train.class_indices)), dtype=int)\n\n    # Create and train the model\n    attention_model = create_deeper_attention_model((256, 256, 3), len(x_train.class_indices), learning_rate=0.0001)\n    current_time = datetime.datetime.now().strftime(\"%Y%m%d-%H%M%S\")\n    filepath = f\"dr-detector-fold{fold_index + 1}-{current_time}-{{epoch:02d}}-{{val_categorical_accuracy:.2f}}.hdf5\"\n\n    # Callbacks\n    checkpoint = ModelCheckpoint('resnet50_best', monitor=\"val_categorical_accuracy\", verbose=1, save_best_only=True, mode=\"max\")\n    earlystop = EarlyStopping(monitor='val_categorical_accuracy', verbose=1, min_delta=0, patience=15, restore_best_weights=True)\n    reduce_lr = ReduceLROnPlateau(monitor='val_loss', verbose=1, factor=0.1, patience=7, min_delta=0.0001, cooldown=0, min_lr=1e-7)\n    callbacks = [checkpoint, earlystop, reduce_lr]\n\n    # Train the model\n    history = attention_model.fit(x_train,  steps_per_epoch=max(1, x_train.samples // 64), epochs=5, validation_data=x_test, validation_steps=max(1, x_test.samples // 64), callbacks=callbacks)\n\n    # Evaluate the model\n    result_train = attention_model.evaluate(x_train)\n    result_test = attention_model.evaluate(x_test)\n    train_loss, train_accu, train_auc = result_train\n    test_loss, test_accu, test_auc = result_test\n\n    # Store results\n    all_train_accu.append(train_accu)\n    all_val_accu.append(test_accu)\n    all_train_loss.append(train_loss)\n    all_val_loss.append(test_loss)\n    all_train_auc.append(train_auc)\n    all_val_auc.append(test_auc)\n\n    all_history['categorical_accuracy'].append(history.history['categorical_accuracy'])\n    all_history['val_categorical_accuracy'].append(history.history['val_categorical_accuracy'])\n    all_history['loss'].append(history.history['loss'])\n    all_history['val_loss'].append(history.history['val_loss'])\n    all_history['auc'].append(history.history['auc'])\n    all_history['val_auc'].append(history.history['val_auc'])\n\n    # Generate predictions and compute confusion matrix\n    y_pred_train = attention_model.predict(x_train)\n    y_pred_train = np.argmax(y_pred_train, axis=1)\n    y_true_train = x_train.classes\n    cm_train = confusion_matrix(y_true_train, y_pred_train)\n    cumulative_cm += cm_train\n\n    print(f\"Final training accuracy for fold {fold_index + 1} = {train_accu * 100:.2f}%, validation accuracy = {test_accu * 100:.2f}%\")\n    print(f\"Final training loss = {train_loss:.2f}, validation loss = {test_loss:.2f}\")\n    print(f\"Final training AUC = {train_auc:.2f}, validation AUC = {test_auc:.2f}\")\n\n# Calculate mean and standard deviation for all metrics\nmean_train_accu = np.mean(all_train_accu)\nmean_val_accu = np.mean(all_val_accu)\nmean_train_loss = np.mean(all_train_loss)\nmean_val_loss = np.mean(all_val_loss)\nmean_train_auc = np.mean(all_train_auc)\nmean_val_auc = np.mean(all_val_auc)\n\nstd_train_accu = np.std(all_train_accu)\nstd_val_accu = np.std(all_val_accu)\nstd_train_loss = np.std(all_train_loss)\nstd_val_loss = np.std(all_val_loss)\nstd_train_auc = np.std(all_train_auc)\nstd_val_auc = np.std(all_val_auc)\n\n# Print overall results\nprint(\"\\nOverall Results:\")\nprint(f\"Mean training accuracy = {mean_train_accu * 100:.2f}% ± {std_train_accu * 100:.2f}%\")\nprint(f\"Mean validation accuracy = {mean_val_accu * 100:.2f}% ± {std_val_accu * 100:.2f}%\")\nprint(f\"Mean training loss = {mean_train_loss:.2f} ± {std_train_loss:.2f}\")\nprint(f\"Mean validation loss = {mean_val_loss:.2f} ± {std_val_loss:.2f}\")\nprint(f\"Mean training AUC = {mean_train_auc:.2f} ± {std_train_auc:.2f}\")\nprint(f\"Mean validation AUC = {mean_val_auc:.2f} ± {std_val_auc:.2f}\")\n\n# Visualize mean results over all folds\nmean_history = {key: np.mean(np.array(all_history[key]), axis=0) for key in all_history.keys()}\nplt.figure(figsize=(18, 6))\n\nplt.subplot(1, 3, 1)\nplt.plot(mean_history['categorical_accuracy'], label='Train Accuracy')\nplt.plot(mean_history['val_categorical_accuracy'], label='Test Accuracy')\nplt.title('Average Model Accuracy')\nplt.xlabel('Epochs')\nplt.ylabel('Accuracy')\nplt.legend(loc='upper left')\n\nplt.subplot(1, 3, 2)\nplt.plot(mean_history['loss'], label='Train Loss')\nplt.plot(mean_history['val_loss'], label='Test Loss')\nplt.title('Average Model Loss')\nplt.xlabel('Epochs')\nplt.ylabel('Loss')\nplt.legend(loc='upper left')\n\nplt.subplot(1, 3, 3)\nplt.plot(mean_history['auc'], label='Train AUC')\nplt.plot(mean_history['val_auc'], label='Test AUC')\nplt.title('Average Model AUC')\nplt.xlabel('Epochs')\nplt.ylabel('AUC')\nplt.legend(loc='upper left')\n\nplt.show()\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"from matplotlib.colors import LinearSegmentedColormap  # Add this import\n# Compute and visualize the average confusion matrix\naverage_cm = cumulative_cm / n_splits\ntarget_names = list(x_train.class_indices.keys())\n\ncolors = [(0.8, 1, 0.8), (0.7, 0.9, 1)]\ncmap = LinearSegmentedColormap.from_list(\"custom_cmap\", colors)\n\nplt.figure(figsize=(8, 8))\nplt.imshow(average_cm, interpolation='nearest', cmap=cmap)\nplt.title('Average Confusion Matrix')\nplt.colorbar()\ntick_marks = np.arange(len(target_names))\nplt.xticks(tick_marks, target_names, rotation=90)\nplt.yticks(tick_marks, target_names)\n\nfor i, j in itertools.product(range(average_cm.shape[0]), range(average_cm.shape[1])):\n    plt.text(j, i, format(average_cm[i, j], '.2f'), horizontalalignment=\"center\", color=\"black\")\n\nplt.tight_layout()\nplt.ylabel('True label')\nplt.xlabel('Predicted label')\nplt.show()\n\n# Completion message\nprint(\"All folds completed.\")\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"! streamlit run app.py &>./logs.txt & npx localtunnel --port 8501\n","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}