{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.x"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceType":"competition","sourceId":3362,"databundleVersionId":31148}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"id":"3acb05af-e649-48a7-b4d6-3f3d76f87958","cell_type":"markdown","source":"# Dogs vs Cats - Training theo slide\n\nNotebook này dùng đúng kiểu code trong slide:\n\n- `ImageDataGenerator`\n- `flow_from_directory()`\n- ConvNet với 3 lớp `Conv2D`\n- Train bằng `model.fit`\n- Vẽ biểu đồ Accuracy/Loss\n- Ma trận nhầm lẫn\n- Accuracy, F1-score, Precision, Recall\n\nLưu ý: `test1.zip` của Kaggle không có nhãn thật, nên phần đánh giá dùng validation set được tách ra từ `train.zip`.\n","metadata":{}},{"id":"c4fd12cd-4a0c-4bef-8501-7860648b949b","cell_type":"code","source":"import os\nimport zipfile\nimport shutil\nimport random\n\nimport numpy as np\nimport matplotlib.pyplot as plt\n\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.optimizers import RMSprop\n\nfrom sklearn.metrics import confusion_matrix, classification_report\nfrom sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:02.561675Z","iopub.execute_input":"2026-05-19T15:57:02.562367Z","iopub.status.idle":"2026-05-19T15:57:02.567076Z","shell.execute_reply.started":"2026-05-19T15:57:02.562339Z","shell.execute_reply":"2026-05-19T15:57:02.566307Z"}},"outputs":[],"execution_count":null},{"id":"ba315615-9d78-49b0-939b-5d1f6cf3f7a5","cell_type":"code","source":"with zipfile.ZipFile('/kaggle/input/competitions/dogs-vs-cats/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\nprint(\"Giải nén xong!\")\nprint(f\"Số lượng ảnh train: {len(os.listdir('/kaggle/working/train'))}\")\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:02.568408Z","iopub.execute_input":"2026-05-19T15:57:02.568865Z","iopub.status.idle":"2026-05-19T15:57:08.185135Z","shell.execute_reply.started":"2026-05-19T15:57:02.568828Z","shell.execute_reply":"2026-05-19T15:57:08.184467Z"}},"outputs":[],"execution_count":null},{"id":"bb3b282b-0867-472e-b909-ba80999dea1d","cell_type":"code","source":"base_dir = '/kaggle/working/cats_and_dogs'\n\ntrain_dir = os.path.join(base_dir, 'train')\nvalidation_dir = os.path.join(base_dir, 'validation')\n\ntrain_cats_dir = os.path.join(train_dir, 'cats')\ntrain_dogs_dir = os.path.join(train_dir, 'dogs')\n\nvalidation_cats_dir = os.path.join(validation_dir, 'cats')\nvalidation_dogs_dir = os.path.join(validation_dir, 'dogs')\n\nif os.path.exists(base_dir):\n    shutil.rmtree(base_dir)\n\nos.makedirs(train_cats_dir, exist_ok=True)\nos.makedirs(train_dogs_dir, exist_ok=True)\nos.makedirs(validation_cats_dir, exist_ok=True)\nos.makedirs(validation_dogs_dir, exist_ok=True)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:08.186076Z","iopub.execute_input":"2026-05-19T15:57:08.186458Z","iopub.status.idle":"2026-05-19T15:57:08.192459Z","shell.execute_reply.started":"2026-05-19T15:57:08.186416Z","shell.execute_reply":"2026-05-19T15:57:08.191847Z"}},"outputs":[],"execution_count":null},{"id":"a531443f-9df7-40a3-bc7f-3096ba38f678","cell_type":"code","source":"source_dir = '/kaggle/working/train'\n\ncat_files = [f for f in os.listdir(source_dir) if f.startswith('cat')]\ndog_files = [f for f in os.listdir(source_dir) if f.startswith('dog')]\n\nrandom.seed(42)\nrandom.shuffle(cat_files)\nrandom.shuffle(dog_files)\n\nsplit_ratio = 0.8\n\ncat_split = int(len(cat_files) * split_ratio)\ndog_split = int(len(dog_files) * split_ratio)\n\ntrain_cat_files = cat_files[:cat_split]\nvalidation_cat_files = cat_files[cat_split:]\n\ntrain_dog_files = dog_files[:dog_split]\nvalidation_dog_files = dog_files[dog_split:]\n\nfor filename in train_cat_files:\n    shutil.copy(os.path.join(source_dir, filename), os.path.join(train_cats_dir, filename))\n\nfor filename in validation_cat_files:\n    shutil.copy(os.path.join(source_dir, filename), os.path.join(validation_cats_dir, filename))\n\nfor filename in train_dog_files:\n    shutil.copy(os.path.join(source_dir, filename), os.path.join(train_dogs_dir, filename))\n\nfor filename in validation_dog_files:\n    shutil.copy(os.path.join(source_dir, filename), os.path.join(validation_dogs_dir, filename))\n\nprint(\"Train cats:\", len(os.listdir(train_cats_dir)))\nprint(\"Train dogs:\", len(os.listdir(train_dogs_dir)))\nprint(\"Validation cats:\", len(os.listdir(validation_cats_dir)))\nprint(\"Validation dogs:\", len(os.listdir(validation_dogs_dir)))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:08.193275Z","iopub.execute_input":"2026-05-19T15:57:08.193583Z","iopub.status.idle":"2026-05-19T15:57:10.936133Z","shell.execute_reply.started":"2026-05-19T15:57:08.193544Z","shell.execute_reply":"2026-05-19T15:57:10.935289Z"}},"outputs":[],"execution_count":null},{"id":"a194c306-39c7-41de-a4f6-664437be3584","cell_type":"code","source":"train_datagen = ImageDataGenerator(rescale=1./255)\nvalidation_datagen = ImageDataGenerator(rescale=1./255)\n\ntrain_generator = train_datagen.flow_from_directory(\n    train_dir,\n    target_size=(150, 150),\n    batch_size=20,\n    class_mode='binary'\n)\n\nvalidation_generator = validation_datagen.flow_from_directory(\n    validation_dir,\n    target_size=(150, 150),\n    batch_size=20,\n    class_mode='binary',\n    shuffle=False\n)\n\nprint(\"Class indices:\", train_generator.class_indices)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:10.937910Z","iopub.execute_input":"2026-05-19T15:57:10.938408Z","iopub.status.idle":"2026-05-19T15:57:11.181936Z","shell.execute_reply.started":"2026-05-19T15:57:10.938381Z","shell.execute_reply":"2026-05-19T15:57:11.181083Z"}},"outputs":[],"execution_count":null},{"id":"31ce5387-c92d-4de0-951d-5232210901a8","cell_type":"code","source":"model = tf.keras.models.Sequential([\n    tf.keras.layers.Conv2D(\n        16,\n        (3, 3),\n        activation='relu',\n        input_shape=(150, 150, 3)\n    ),\n    tf.keras.layers.MaxPooling2D(2, 2),\n\n    tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n\n    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n\n    tf.keras.layers.Flatten(),\n\n    tf.keras.layers.Dense(512, activation='relu'),\n    tf.keras.layers.Dense(1, activation='sigmoid')\n])\n\nmodel.summary()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:11.182996Z","iopub.execute_input":"2026-05-19T15:57:11.183304Z","iopub.status.idle":"2026-05-19T15:57:11.244583Z","shell.execute_reply.started":"2026-05-19T15:57:11.183281Z","shell.execute_reply":"2026-05-19T15:57:11.243858Z"}},"outputs":[],"execution_count":null},{"id":"b82c6882-530b-474b-b772-d11abfa52c0a","cell_type":"code","source":"model.compile(\n    loss='binary_crossentropy',\n    optimizer=RMSprop(learning_rate=0.001),\n    metrics=['accuracy']\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:11.245834Z","iopub.execute_input":"2026-05-19T15:57:11.246155Z","iopub.status.idle":"2026-05-19T15:57:11.254208Z","shell.execute_reply.started":"2026-05-19T15:57:11.246132Z","shell.execute_reply":"2026-05-19T15:57:11.253411Z"}},"outputs":[],"execution_count":null},{"id":"addb58b6-287b-4821-8b6b-4a78a5c038ef","cell_type":"code","source":"history = model.fit(\n    train_generator,\n    steps_per_epoch=100,\n    epochs=15,\n    validation_data=validation_generator,\n    validation_steps=50,\n    verbose=2\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:57:11.255995Z","iopub.execute_input":"2026-05-19T15:57:11.256343Z","iopub.status.idle":"2026-05-19T15:58:13.081573Z","shell.execute_reply.started":"2026-05-19T15:57:11.256309Z","shell.execute_reply":"2026-05-19T15:58:13.080901Z"}},"outputs":[],"execution_count":null},{"id":"e3686216-21d5-4d58-b13a-be089b558150","cell_type":"code","source":"acc = history.history['accuracy']\nval_acc = history.history['val_accuracy']\n\nloss = history.history['loss']\nval_loss = history.history['val_loss']\n\nepochs = range(len(acc))\n\nplt.figure(figsize=(8, 5))\nplt.plot(epochs, acc, label='Training Accuracy')\nplt.plot(epochs, val_acc, label='Validation Accuracy')\nplt.title('Training and Validation Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.legend()\nplt.grid(True)\nplt.show()\n\nplt.figure(figsize=(8, 5))\nplt.plot(epochs, loss, label='Training Loss')\nplt.plot(epochs, val_loss, label='Validation Loss')\nplt.title('Training and Validation Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.legend()\nplt.grid(True)\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:58:13.082707Z","iopub.execute_input":"2026-05-19T15:58:13.083010Z","iopub.status.idle":"2026-05-19T15:58:13.385783Z","shell.execute_reply.started":"2026-05-19T15:58:13.082976Z","shell.execute_reply":"2026-05-19T15:58:13.385010Z"}},"outputs":[],"execution_count":null},{"id":"8b0ea840-fd26-4159-acd0-e93ecfce018b","cell_type":"code","source":"validation_generator.reset()\n\ny_pred_prob = model.predict(validation_generator)\ny_pred = (y_pred_prob > 0.5).astype(int).reshape(-1)\n\ny_true = validation_generator.classes\n\ncm = confusion_matrix(y_true, y_pred)\n\nprint(\"Confusion Matrix:\")\nprint(cm)\n\nplt.figure(figsize=(6, 5))\nplt.imshow(cm)\nplt.title(\"Confusion Matrix - Validation Set\")\nplt.xlabel(\"Predicted Label\")\nplt.ylabel(\"True Label\")\n\nplt.xticks([0, 1], [\"Cat\", \"Dog\"])\nplt.yticks([0, 1], [\"Cat\", \"Dog\"])\n\nfor i in range(cm.shape[0]):\n    for j in range(cm.shape[1]):\n        plt.text(j, i, cm[i, j], ha=\"center\", va=\"center\")\n\nplt.colorbar()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:58:13.386673Z","iopub.execute_input":"2026-05-19T15:58:13.387032Z","iopub.status.idle":"2026-05-19T15:58:20.775828Z","shell.execute_reply.started":"2026-05-19T15:58:13.387007Z","shell.execute_reply":"2026-05-19T15:58:20.774994Z"}},"outputs":[],"execution_count":null},{"id":"1be8a101-6ac7-4619-a2fb-79c684e271b0","cell_type":"code","source":"accuracy = accuracy_score(y_true, y_pred)\nf1 = f1_score(y_true, y_pred)\nprecision = precision_score(y_true, y_pred)\nrecall = recall_score(y_true, y_pred)\n\nprint(\"Kết quả đánh giá trên validation set:\")\nprint(f\"Accuracy : {accuracy:.4f}\")\nprint(f\"F1-score : {f1:.4f}\")\nprint(f\"Precision: {precision:.4f}\")\nprint(f\"Recall   : {recall:.4f}\")\n\nprint(\"\\nClassification Report:\")\nprint(classification_report(\n    y_true,\n    y_pred,\n    target_names=[\"Cat\", \"Dog\"]\n))\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-05-19T15:58:20.776886Z","iopub.execute_input":"2026-05-19T15:58:20.777210Z","iopub.status.idle":"2026-05-19T15:58:20.802608Z","shell.execute_reply.started":"2026-05-19T15:58:20.777175Z","shell.execute_reply":"2026-05-19T15:58:20.801918Z"}},"outputs":[],"execution_count":null}]}