{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":3362,"databundleVersionId":31148,"sourceType":"competition"}],"dockerImageVersionId":31089,"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\nimport numpy as np # linear algebra\nimport 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\nimport os\nfor 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":"2025-08-12T14:13:57.360349Z","iopub.execute_input":"2025-08-12T14:13:57.360656Z","iopub.status.idle":"2025-08-12T14:13:57.367265Z","shell.execute_reply.started":"2025-08-12T14:13:57.360636Z","shell.execute_reply":"2025-08-12T14:13:57.366550Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"1. Dataset Preparation","metadata":{}},{"cell_type":"code","source":"# ===== 1.1 Import =====\nimport tensorflow as tf\nimport zipfile, os, shutil\nfrom sklearn.model_selection import train_test_split\n\n# ===== 1.2 Giải nén dữ liệu =====\nzip_train_path = \"/kaggle/input/dogs-vs-cats/train.zip\"\nzip_test_path  = \"/kaggle/input/dogs-vs-cats/test1.zip\"\nextract_root   = \"/kaggle/working/data\"\n\nwith zipfile.ZipFile(zip_train_path, 'r') as zip_ref:\n    zip_ref.extractall(extract_root)\n\nwith zipfile.ZipFile(zip_test_path, 'r') as zip_ref:\n    zip_ref.extractall(extract_root + \"/test\")\n\nprint(\"✅ Đã giải nén train & test\")\n\n# ===== 1.3 Tạo thư mục train/val cho cats và dogs =====\nbase_dir = \"/kaggle/working/split_data\"\ntrain_dir = os.path.join(base_dir, 'train')\nval_dir   = os.path.join(base_dir, 'val')\n\nfor category in ['cats', 'dogs']:\n    os.makedirs(os.path.join(train_dir, category), exist_ok=True)\n    os.makedirs(os.path.join(val_dir, category), exist_ok=True)\n\n# ===== 1.4 Lấy danh sách file ảnh & chia train/val =====\ncat_files = [f for f in os.listdir(extract_root + \"/train\") if f.startswith(\"cat\")]\ndog_files = [f for f in os.listdir(extract_root + \"/train\") if f.startswith(\"dog\")]\n\ncat_train, cat_val = train_test_split(cat_files, test_size=0.2, random_state=123)\ndog_train, dog_val = train_test_split(dog_files, test_size=0.2, random_state=123)\n\n# ===== 1.5 Di chuyển file ảnh vào thư mục mới =====\nfor f in cat_train:\n    shutil.move(os.path.join(extract_root + \"/train\", f),\n                os.path.join(train_dir, 'cats', f))\nfor f in cat_val:\n    shutil.move(os.path.join(extract_root + \"/train\", f),\n                os.path.join(val_dir, 'cats', f))\n\nfor f in dog_train:\n    shutil.move(os.path.join(extract_root + \"/train\", f),\n                os.path.join(train_dir, 'dogs', f))\nfor f in dog_val:\n    shutil.move(os.path.join(extract_root + \"/train\", f),\n                os.path.join(val_dir, 'dogs', f))\n\nprint(\"✅ Đã chia dữ liệu train/val đều cho cats & dogs\")\n\n# ===== 1.6 Load dataset =====\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    image_size=(150, 150),\n    batch_size=32\n)\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    val_dir,\n    image_size=(150, 150),\n    batch_size=32\n)\n\n# ===== 1.7 Chuẩn hóa và one-hot encode =====\nnormalization_layer = tf.keras.layers.Rescaling(1./255)\ntrain_ds = train_ds.map(lambda x, y: (normalization_layer(x), tf.one_hot(y, depth=2)))\nval_ds   = val_ds.map(lambda x, y: (normalization_layer(x), tf.one_hot(y, depth=2)))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T14:26:15.092331Z","iopub.execute_input":"2025-08-12T14:26:15.092622Z","iopub.status.idle":"2025-08-12T14:26:24.062396Z","shell.execute_reply.started":"2025-08-12T14:26:15.092602Z","shell.execute_reply":"2025-08-12T14:26:24.061840Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"2. Building a Basic CNN","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import layers, models\n\n# ===== 2.1 Xây dựng kiến trúc CNN =====\nmodel = models.Sequential([\n    layers.Conv2D(32, (3,3), activation='relu', input_shape=(150, 150, 3)), # Conv layer 1\n    layers.MaxPooling2D((2,2)),                                            # Pooling 1\n    \n    layers.Conv2D(64, (3,3), activation='relu'),  # Conv layer 2\n    layers.MaxPooling2D((2,2)),                   # Pooling 2\n    \n    layers.Conv2D(128, (3,3), activation='relu'), # Conv layer 3\n    layers.MaxPooling2D((2,2)),                   # Pooling 3\n    \n    layers.Flatten(),                             # Flatten to vector\n    layers.Dense(128, activation='relu'),         # Fully connected layer\n    layers.Dense(2, activation='sigmoid')         \n])\n\n# ===== 2.2 In model summary =====\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T15:09:44.756534Z","iopub.execute_input":"2025-08-12T15:09:44.757370Z","iopub.status.idle":"2025-08-12T15:09:44.820531Z","shell.execute_reply.started":"2025-08-12T15:09:44.757335Z","shell.execute_reply":"2025-08-12T15:09:44.819933Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"3. Training and Evaluation","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.optimizers import Adam\nfrom sklearn.metrics import classification_report\nimport numpy as np\nimport matplotlib.pyplot as plt\n\n\n# ===== 3.1 Compile model =====\nmodel.compile(\n    optimizer=Adam(),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)\n\n# ===== 3.2 Train model =====\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=5\n)\n\n# ===== 3.3 Vẽ biểu đồ loss & accuracy =====\nplt.figure(figsize=(12,4))\nplt.subplot(1,2,1)\nplt.plot(history.history['accuracy'], label='Train')\nplt.plot(history.history['val_accuracy'], label='Val')\nplt.title('Accuracy')\nplt.legend()\n\nplt.subplot(1,2,2)\nplt.plot(history.history['loss'], label='Train')\nplt.plot(history.history['val_loss'], label='Val')\nplt.title('Loss')\nplt.legend()\nplt.show()\n\n# ===== 3.4 Đánh giá trên validation set =====\nval_loss, val_acc = model.evaluate(val_ds)\nprint(f\"Validation Accuracy: {val_acc:.4f}\")\n\n# ===== 3.5 Precision, Recall, F1-score =====\ny_true = []\ny_pred = []\n\nfor images, labels in val_ds:\n    preds = model.predict(images)\n    y_true.extend(np.argmax(labels.numpy(), axis=1))\n    y_pred.extend(np.argmax(preds, axis=1))\n\nprint(classification_report(y_true, y_pred, target_names=['Cat','Dog']))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T14:26:47.146883Z","iopub.execute_input":"2025-08-12T14:26:47.147610Z","iopub.status.idle":"2025-08-12T14:28:30.340617Z","shell.execute_reply.started":"2025-08-12T14:26:47.147582Z","shell.execute_reply":"2025-08-12T14:28:30.339694Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"4. Advanced CNN Techniques","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras import regularizers\n\n# ===== 4.1 Thêm Dropout & L2 Regularization =====\nmodel_reg = models.Sequential([\n    layers.Conv2D(32, (3,3), activation='relu', kernel_regularizer=regularizers.l2(0.001), input_shape=(150, 150, 3)),\n    layers.MaxPooling2D((2,2)),\n    layers.Dropout(0.25),  # Dropout layer\n    \n    layers.Conv2D(64, (3,3), activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n    layers.MaxPooling2D((2,2)),\n    layers.Dropout(0.25),\n    \n    layers.Flatten(),\n    layers.Dense(128, activation='relu'),\n    layers.Dropout(0.5),\n    layers.Dense(2, activation='softmax')\n])\n\n# ===== 4.2 Data augmentation =====\ndata_augmentation = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.1),\n    layers.RandomZoom(0.1),\n])\n\naug_train_ds = train_ds.map(lambda x, y: (data_augmentation(x, training=True), y))\n\n# ===== 4.3 Compile & train =====\nmodel_reg.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])\nhistory_reg = model_reg.fit(aug_train_ds, validation_data=val_ds, epochs=5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T14:28:47.378537Z","iopub.execute_input":"2025-08-12T14:28:47.379221Z","iopub.status.idle":"2025-08-12T14:37:10.940128Z","shell.execute_reply.started":"2025-08-12T14:28:47.379194Z","shell.execute_reply":"2025-08-12T14:37:10.939486Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"5. Transfer Learning","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import VGG16\n\n# ===== 5.1 Load VGG16 pre-trained =====\nbase_model = VGG16(input_shape=(150,150,3), include_top=False, weights='imagenet')\n\n# Freeze các layer ban đầu\nfor layer in base_model.layers:\n    layer.trainable = False\n\n# ===== 5.2 Thêm các layer cho classification =====\nx = layers.Flatten()(base_model.output)\nx = layers.Dense(128, activation='relu')(x)\nx = layers.Dense(2, activation='softmax')(x)\n\ntransfer_model = models.Model(inputs=base_model.input, outputs=x)\n\n# ===== 5.3 Compile & train =====\ntransfer_model.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])\nhistory_transfer = transfer_model.fit(train_ds, validation_data=val_ds, epochs=5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T14:37:22.402821Z","iopub.execute_input":"2025-08-12T14:37:22.403363Z","iopub.status.idle":"2025-08-12T14:44:45.248203Z","shell.execute_reply.started":"2025-08-12T14:37:22.403334Z","shell.execute_reply":"2025-08-12T14:44:45.247624Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"7. Improving Model Performance","metadata":{}},{"cell_type":"code","source":"#đổi optimizer\nfrom tensorflow.keras.optimizers import RMSprop, SGD\n\nmodel_sgd = models.Sequential([\n    layers.Conv2D(32, (3,3), activation='relu', input_shape=(150,150,3)),\n    layers.MaxPooling2D(2,2),\n    layers.Conv2D(64, (3,3), activation='relu'),\n    layers.MaxPooling2D(2,2),\n    layers.Flatten(),\n    layers.Dense(128, activation='relu'),\n    layers.Dense(2, activation='softmax')\n])\n\nmodel_sgd.compile(optimizer=SGD(learning_rate=0.01, momentum=0.9),\n                  loss='categorical_crossentropy',\n                  metrics=['accuracy'])\n\nmodel_sgd.fit(train_ds, validation_data=val_ds, epochs=5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T15:32:59.434580Z","iopub.execute_input":"2025-08-12T15:32:59.435404Z","iopub.status.idle":"2025-08-12T15:34:12.199406Z","shell.execute_reply.started":"2025-08-12T15:32:59.435378Z","shell.execute_reply":"2025-08-12T15:34:12.198682Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"8. Optional Challenges","metadata":{}},{"cell_type":"code","source":"# Custom CNN giống AlexNet nhỏ gọn\nmodel_custom = models.Sequential([\n    layers.Conv2D(64, (3,3), activation='relu', input_shape=(150,150,3)),\n    layers.MaxPooling2D(2,2),\n    \n    layers.Conv2D(128, (3,3), activation='relu'),\n    layers.MaxPooling2D(2,2),\n    \n    layers.Conv2D(256, (3,3), activation='relu'),\n    layers.MaxPooling2D(2,2),\n    \n    layers.Flatten(),\n    layers.Dense(256, activation='relu'),\n    layers.Dropout(0.5),\n    layers.Dense(2, activation='softmax')\n])\n\nmodel_custom.compile(optimizer=Adam(), loss='categorical_crossentropy', metrics=['accuracy'])\nmodel_custom.fit(train_ds, validation_data=val_ds, epochs=5)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-08-12T15:34:18.150454Z","iopub.execute_input":"2025-08-12T15:34:18.150775Z","iopub.status.idle":"2025-08-12T15:37:13.944273Z","shell.execute_reply.started":"2025-08-12T15:34:18.150753Z","shell.execute_reply":"2025-08-12T15:37:13.943676Z"}},"outputs":[],"execution_count":null}]}