{"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":"nvidiaTeslaT4","dataSources":[{"sourceId":14774,"databundleVersionId":875431,"sourceType":"competition"}],"dockerImageVersionId":31259,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# # This Python 3 environment comes with many helpful analytics libraries installed\n# # It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# # For example, here's several helpful packages to load\n\n# import numpy as np # linear algebra\n# import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# # Input data files are available in the read-only \"../input/\" directory\n# # For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# # You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# # You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:48:37.784666Z","iopub.execute_input":"2026-02-03T16:48:37.785447Z","iopub.status.idle":"2026-02-03T16:48:37.789566Z","shell.execute_reply.started":"2026-02-03T16:48:37.785413Z","shell.execute_reply":"2026-02-03T16:48:37.788833Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport tensorflow as tf\n\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.applications.resnet import preprocess_input\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Dropout\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.optimizers import Adam\n\nfrom sklearn.utils.class_weight import compute_class_weight\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df = pd.read_csv(\n    '/kaggle/input/aptos2019-blindness-detection/train.csv'\n)\n\nprint(train_df.head())\nprint(train_df['diagnosis'].value_counts())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:01.939748Z","iopub.execute_input":"2026-02-03T16:49:01.940189Z","iopub.status.idle":"2026-02-03T16:49:01.987035Z","shell.execute_reply.started":"2026-02-03T16:49:01.940166Z","shell.execute_reply":"2026-02-03T16:49:01.986250Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['filename'] = train_df['id_code'] + '.png'\n\nprint(train_df.head())\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:01.987936Z","iopub.execute_input":"2026-02-03T16:49:01.988244Z","iopub.status.idle":"2026-02-03T16:49:01.994695Z","shell.execute_reply.started":"2026-02-03T16:49:01.988216Z","shell.execute_reply":"2026-02-03T16:49:01.993970Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df['diagnosis'] = train_df['diagnosis'].astype(str)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:01.996542Z","iopub.execute_input":"2026-02-03T16:49:01.996876Z","iopub.status.idle":"2026-02-03T16:49:02.011285Z","shell.execute_reply.started":"2026-02-03T16:49:01.996850Z","shell.execute_reply":"2026-02-03T16:49:02.010755Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(train_df.dtypes)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:02.012129Z","iopub.execute_input":"2026-02-03T16:49:02.012392Z","iopub.status.idle":"2026-02-03T16:49:02.026416Z","shell.execute_reply.started":"2026-02-03T16:49:02.012366Z","shell.execute_reply":"2026-02-03T16:49:02.025753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = (224, 224)\nBATCH_SIZE = 32\n\ntrain_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input,\n    validation_split=0.2,\n    rotation_range=15,\n    horizontal_flip=True,\n    zoom_range=0.1\n)\n\nval_datagen = ImageDataGenerator(\n    preprocessing_function=preprocess_input,\n    validation_split=0.2\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:02.027317Z","iopub.execute_input":"2026-02-03T16:49:02.027908Z","iopub.status.idle":"2026-02-03T16:49:02.040216Z","shell.execute_reply.started":"2026-02-03T16:49:02.027874Z","shell.execute_reply":"2026-02-03T16:49:02.039670Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_gen = train_datagen.flow_from_dataframe(\n    dataframe=train_df,\n    directory='/kaggle/input/aptos2019-blindness-detection/train_images',\n    x_col='filename',\n    y_col='diagnosis',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='sparse',\n    subset='training',\n    shuffle=True\n)\n\nval_gen = val_datagen.flow_from_dataframe(\n    dataframe=train_df,\n    directory='/kaggle/input/aptos2019-blindness-detection/train_images',\n    x_col='filename',\n    y_col='diagnosis',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='sparse',\n    subset='validation',\n    shuffle=False\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:02.041326Z","iopub.execute_input":"2026-02-03T16:49:02.041578Z","iopub.status.idle":"2026-02-03T16:49:12.294364Z","shell.execute_reply.started":"2026-02-03T16:49:02.041551Z","shell.execute_reply":"2026-02-03T16:49:12.293716Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_weights = compute_class_weight(\n    class_weight='balanced',\n    classes=np.unique(train_df['diagnosis']),\n    y=train_df['diagnosis']\n)\n\nclass_weights = dict(enumerate(class_weights))\nprint(class_weights)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:12.295235Z","iopub.execute_input":"2026-02-03T16:49:12.295540Z","iopub.status.idle":"2026-02-03T16:49:12.355112Z","shell.execute_reply.started":"2026-02-03T16:49:12.295517Z","shell.execute_reply":"2026-02-03T16:49:12.354476Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_model = ResNet50(\n    weights='imagenet',\n    include_top=False,\n    input_shape=(224, 224, 3)\n)\n\n# freeze toàn bộ backbone\nfor layer in base_model.layers:\n    layer.trainable = False\n\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dense(256, activation='relu')(x)\nx = Dropout(0.5)(x)\noutput = Dense(5, activation='softmax')(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\n\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:12.356043Z","iopub.execute_input":"2026-02-03T16:49:12.356510Z","iopub.status.idle":"2026-02-03T16:49:18.201102Z","shell.execute_reply.started":"2026-02-03T16:49:12.356485Z","shell.execute_reply":"2026-02-03T16:49:18.200572Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer=Adam(learning_rate=1e-4),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:18.202092Z","iopub.execute_input":"2026-02-03T16:49:18.202437Z","iopub.status.idle":"2026-02-03T16:49:18.215554Z","shell.execute_reply.started":"2026-02-03T16:49:18.202415Z","shell.execute_reply":"2026-02-03T16:49:18.214861Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history1 = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=10,\n    class_weight=class_weights\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T16:49:18.216372Z","iopub.execute_input":"2026-02-03T16:49:18.216624Z","iopub.status.idle":"2026-02-03T17:53:20.925467Z","shell.execute_reply.started":"2026-02-03T16:49:18.216603Z","shell.execute_reply":"2026-02-03T17:53:20.924659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for layer in base_model.layers[-40:]:\n    layer.trainable = True\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T17:53:20.926574Z","iopub.execute_input":"2026-02-03T17:53:20.926838Z","iopub.status.idle":"2026-02-03T17:53:20.931038Z","shell.execute_reply.started":"2026-02-03T17:53:20.926815Z","shell.execute_reply":"2026-02-03T17:53:20.930472Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.compile(\n    optimizer=Adam(learning_rate=1e-5),\n    loss='sparse_categorical_crossentropy',\n    metrics=['accuracy']\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T17:53:20.933470Z","iopub.execute_input":"2026-02-03T17:53:20.933691Z","iopub.status.idle":"2026-02-03T17:53:20.973459Z","shell.execute_reply.started":"2026-02-03T17:53:20.933672Z","shell.execute_reply":"2026-02-03T17:53:20.972780Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history2 = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=20,\n    class_weight=class_weights\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T17:53:20.974393Z","iopub.execute_input":"2026-02-03T17:53:20.974678Z","iopub.status.idle":"2026-02-03T19:56:13.449256Z","shell.execute_reply.started":"2026-02-03T17:53:20.974651Z","shell.execute_reply":"2026-02-03T19:56:13.448665Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nacc = history1.history['accuracy'] + history2.history['accuracy']\nval_acc = history1.history['val_accuracy'] + history2.history['val_accuracy']\nloss = history1.history['loss'] + history2.history['loss']\nval_loss = history1.history['val_loss'] + history2.history['val_loss']\n\nplt.figure(figsize=(12,4))\n\nplt.subplot(1,2,1)\nplt.plot(acc)\nplt.plot(val_acc)\nplt.title('Accuracy')\nplt.legend(['Train', 'Val'])\n\nplt.subplot(1,2,2)\nplt.plot(loss)\nplt.plot(val_loss)\nplt.title('Loss')\nplt.legend(['Train', 'Val'])\n\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T19:56:13.450374Z","iopub.execute_input":"2026-02-03T19:56:13.450616Z","iopub.status.idle":"2026-02-03T19:56:13.744964Z","shell.execute_reply.started":"2026-02-03T19:56:13.450595Z","shell.execute_reply":"2026-02-03T19:56:13.744424Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"/kaggle/working/resnet_aptos_model.h5\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-03T19:56:13.745771Z","iopub.execute_input":"2026-02-03T19:56:13.746042Z","iopub.status.idle":"2026-02-03T19:56:14.449151Z","shell.execute_reply.started":"2026-02-03T19:56:13.746020Z","shell.execute_reply":"2026-02-03T19:56:14.448481Z"}},"outputs":[],"execution_count":null}]}