{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.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":3362,"databundleVersionId":31148}],"dockerImageVersionId":30627,"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","execution":{"iopub.status.busy":"2026-02-19T14:21:51.745056Z","iopub.execute_input":"2026-02-19T14:21:51.745523Z","iopub.status.idle":"2026-02-19T14:21:51.755462Z","shell.execute_reply.started":"2026-02-19T14:21:51.745487Z","shell.execute_reply":"2026-02-19T14:21:51.754283Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nprint(os.listdir(\"../input\"))\n\nimport zipfile\n\nwith zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/test1.zip\",\"r\") as z:\n    z.extractall(\".\")\n    \nwith zipfile.ZipFile(\"/kaggle/input/dogs-vs-cats/train.zip\",\"r\") as z:\n    z.extractall(\".\")","metadata":{"execution":{"iopub.status.busy":"2026-02-19T14:21:51.770449Z","iopub.execute_input":"2026-02-19T14:21:51.771558Z","iopub.status.idle":"2026-02-19T14:22:02.926824Z","shell.execute_reply.started":"2026-02-19T14:21:51.771517Z","shell.execute_reply":"2026-02-19T14:22:02.925605Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"!ls train","metadata":{"execution":{"iopub.status.busy":"2024-01-08T06:57:18.293035Z","iopub.execute_input":"2024-01-08T06:57:18.293457Z","iopub.status.idle":"2024-01-08T06:57:19.311078Z","shell.execute_reply.started":"2024-01-08T06:57:18.293422Z","shell.execute_reply":"2024-01-08T06:57:19.309969Z"}}},{"cell_type":"code","source":"# plot dog photos from the dogs vs cats dataset\nfrom matplotlib import pyplot\nfrom matplotlib.image import imread\n# define location of dataset\nfolder = 'train/'\n# plot first few images\nfor i in range(9):\n\t# define subplot\n\tpyplot.subplot(330 + 1 + i)\n\t# define filename\n\tfilename = folder + 'dog.' + str(i) + '.jpg'\n\t# load image pixels\n\timage = imread(filename)\n\t# plot raw pixel data\n\tpyplot.imshow(image)\n# show the figure\npyplot.show()","metadata":{"execution":{"iopub.status.busy":"2026-02-19T14:22:02.929277Z","iopub.execute_input":"2026-02-19T14:22:02.930183Z","iopub.status.idle":"2026-02-19T14:22:04.161065Z","shell.execute_reply.started":"2026-02-19T14:22:02.930138Z","shell.execute_reply":"2026-02-19T14:22:04.159766Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# plot cat photos from the dogs vs cats dataset\nfrom matplotlib import pyplot\nfrom matplotlib.image import imread\n# define location of dataset\nfolder = 'train/'\n# plot first few images\nfor i in range(9):\n\t# define subplot\n\tpyplot.subplot(330 + 1 + i)\n\t# define filename\n\tfilename = folder + 'cat.' + str(i) + '.jpg'\n\t# load image pixels\n\timage = imread(filename)\n\t# plot raw pixel data\n\tpyplot.imshow(image)\n# show the figure\npyplot.show()","metadata":{"execution":{"iopub.status.busy":"2026-02-19T14:22:04.163217Z","iopub.execute_input":"2026-02-19T14:22:04.163679Z","iopub.status.idle":"2026-02-19T14:22:05.742291Z","shell.execute_reply.started":"2026-02-19T14:22:04.163643Z","shell.execute_reply":"2026-02-19T14:22:05.741167Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Select Standardized Photo Size","metadata":{}},{"cell_type":"markdown","source":"## Pre-Process Photo Sizes (Optional)¶","metadata":{}},{"cell_type":"code","source":"# load dogs vs cats dataset, reshape and save to a new file\nfrom os import listdir\nfrom numpy import asarray\nfrom numpy import save\nfrom keras.preprocessing.image import load_img\nfrom keras.preprocessing.image import img_to_array\n\n# define location of dataset\nfolder = 'train/'\nphotos, labels = list(), list()\n\nmax_images = 5000   # 👈 เพิ่มบรรทัดนี้ (จำกัดจำนวนรูป)\ncount = 0           # 👈 ตัวนับ\n\n# enumerate files in the directory\nfor file in listdir(folder):\n\n    if count >= max_images:   # 👈 หยุดเมื่อครบจำนวน\n        break\n\n    # determine class\n    output = 0.0\n    if file.startswith('dog'):\n        output = 1.0\n\n    # load image\n    photo = load_img(folder + file, target_size=(128, 128))\n\n    # convert to numpy array\n    photo = img_to_array(photo)\n\n    # store\n    photos.append(photo)\n    labels.append(output)\n\n    count += 1   # 👈 เพิ่มตัวนับ\n\n# convert to numpy arrays\nphotos = asarray(photos)\nlabels = asarray(labels)\n\nprint(photos.shape, labels.shape)\n\n# save the reshaped photos\nsave('dogs_vs_cats_photos.npy', photos)\nsave('dogs_vs_cats_labels.npy', labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:22:05.745389Z","iopub.execute_input":"2026-02-19T14:22:05.746258Z","iopub.status.idle":"2026-02-19T14:22:13.424535Z","shell.execute_reply.started":"2026-02-19T14:22:05.746223Z","shell.execute_reply":"2026-02-19T14:22:13.423253Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# load and confirm the shape\nfrom numpy import load\nphotos = load('dogs_vs_cats_photos.npy')\nlabels = load('dogs_vs_cats_labels.npy')\nprint(photos.shape, labels.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:22:59.350129Z","iopub.execute_input":"2026-02-19T14:22:59.351033Z","iopub.status.idle":"2026-02-19T14:22:59.752363Z","shell.execute_reply.started":"2026-02-19T14:22:59.350982Z","shell.execute_reply":"2026-02-19T14:22:59.751234Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Pre-Process Photos into Standard Directories","metadata":{}},{"cell_type":"code","source":"from os import makedirs\nfrom os import listdir\nfrom shutil import copyfile\nfrom random import seed\nfrom random import random","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:01.555230Z","iopub.execute_input":"2026-02-19T14:23:01.556174Z","iopub.status.idle":"2026-02-19T14:23:01.561530Z","shell.execute_reply.started":"2026-02-19T14:23:01.556136Z","shell.execute_reply":"2026-02-19T14:23:01.560244Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# create directories\ndataset_home = 'dataset_dogs_vs_cats/'\nsubdirs = ['train/', 'test/']\nfor subdir in subdirs:\n\t# create label subdirectories\n\tlabeldirs = ['dogs/', 'cats/']\n\tfor labldir in labeldirs:\n\t\tnewdir = dataset_home + subdir + labldir\n\t\tmakedirs(newdir, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:02.535126Z","iopub.execute_input":"2026-02-19T14:23:02.536138Z","iopub.status.idle":"2026-02-19T14:23:02.542021Z","shell.execute_reply.started":"2026-02-19T14:23:02.536101Z","shell.execute_reply":"2026-02-19T14:23:02.540827Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# seed random number generator\nseed(1)\n# define ratio of pictures to use for validation\nval_ratio = 0.25\n# copy training dataset images into subdirectories\nsrc_directory = 'train/'\nfor file in listdir(src_directory):\n\tsrc = src_directory + '/' + file\n\tdst_dir = 'train/'\n\tif random() < val_ratio:\n\t\tdst_dir = 'test/'\n\tif file.startswith('cat'):\n\t\tdst = dataset_home + dst_dir + 'cats/'  + file\n\t\tcopyfile(src, dst)\n\telif file.startswith('dog'):\n\t\tdst = dataset_home + dst_dir + 'dogs/'  + file\n\t\tcopyfile(src, dst)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:05.080525Z","iopub.execute_input":"2026-02-19T14:23:05.080994Z","iopub.status.idle":"2026-02-19T14:23:08.679441Z","shell.execute_reply.started":"2026-02-19T14:23:05.080957Z","shell.execute_reply":"2026-02-19T14:23:08.678516Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# organize dataset into a useful structure\nfrom os import makedirs\nfrom os import listdir\nfrom shutil import copyfile\nfrom random import seed\nfrom random import random\n# create directories\ndataset_home = 'dataset_dogs_vs_cats/'\nsubdirs = ['train/', 'test/']\nfor subdir in subdirs:\n\t# create label subdirectories\n\tlabeldirs = ['dogs/', 'cats/']\n\tfor labldir in labeldirs:\n\t\tnewdir = dataset_home + subdir + labldir\n\t\tmakedirs(newdir, exist_ok=True)\n# seed random number generator\nseed(1)\n# define ratio of pictures to use for validation\nval_ratio = 0.25\n# copy training dataset images into subdirectories\nsrc_directory = 'train/'\nfor file in listdir(src_directory):\n\tsrc = src_directory + '/' + file\n\tdst_dir = 'train/'\n\tif random() < val_ratio:\n\t\tdst_dir = 'test/'\n\tif file.startswith('cat'):\n\t\tdst = dataset_home + dst_dir + 'cats/'  + file\n\t\tcopyfile(src, dst)\n\telif file.startswith('dog'):\n\t\tdst = dataset_home + dst_dir + 'dogs/'  + file\n\t\tcopyfile(src, dst)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:09.850642Z","iopub.execute_input":"2026-02-19T14:23:09.851092Z","iopub.status.idle":"2026-02-19T14:23:13.440140Z","shell.execute_reply.started":"2026-02-19T14:23:09.851057Z","shell.execute_reply":"2026-02-19T14:23:13.438992Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Develop a Baseline CNN Model","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dense\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nmodel = Sequential()\n# block 1\nmodel.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(200, 200, 3)))\nmodel.add(MaxPooling2D((2, 2)))\n# block 2\nmodel.add(Conv2D(64, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))\nmodel.add(MaxPooling2D((2, 2)))\n# block 3\nmodel.add(Conv2D(128, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same'))\nmodel.add(MaxPooling2D((2, 2)))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:14.884468Z","iopub.execute_input":"2026-02-19T14:23:14.884884Z","iopub.status.idle":"2026-02-19T14:23:14.949298Z","shell.execute_reply.started":"2026-02-19T14:23:14.884838Z","shell.execute_reply":"2026-02-19T14:23:14.948111Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# define cnn model\n\nmodel = Sequential()\nmodel.add(Conv2D(32, (3, 3), activation='relu', kernel_initializer='he_uniform', padding='same', input_shape=(200, 200, 3)))\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dense(1, activation='sigmoid'))\n\t# compile model\nopt = SGD(learning_rate=0.001, momentum=0.9)\nmodel.compile(\n                optimizer=opt, \n                loss='binary_crossentropy', \n                metrics=['accuracy']\n            )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:16.325915Z","iopub.execute_input":"2026-02-19T14:23:16.326998Z","iopub.status.idle":"2026-02-19T14:23:16.393343Z","shell.execute_reply.started":"2026-02-19T14:23:16.326936Z","shell.execute_reply":"2026-02-19T14:23:16.392157Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def define_model():\n    from tensorflow.keras.models import Sequential\n    from tensorflow.keras.layers import Dense, Flatten\n\n    model = Sequential()\n    model.add(Flatten(input_shape=(128,128,3)))\n    model.add(Dense(128, activation='relu'))\n    model.add(Dense(1, activation='sigmoid'))\n\n    model.compile(\n        optimizer='adam',\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n    return model\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:53.010805Z","iopub.execute_input":"2026-02-19T14:23:53.011739Z","iopub.status.idle":"2026-02-19T14:23:53.018274Z","shell.execute_reply.started":"2026-02-19T14:23:53.011699Z","shell.execute_reply":"2026-02-19T14:23:53.016927Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# define model\nmodel = define_model()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:54.835437Z","iopub.execute_input":"2026-02-19T14:23:54.836193Z","iopub.status.idle":"2026-02-19T14:23:54.887050Z","shell.execute_reply.started":"2026-02-19T14:23:54.836155Z","shell.execute_reply":"2026-02-19T14:23:54.886085Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# create data generator\ndatagen = ImageDataGenerator(rescale=1.0/255.0)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:23:55.941009Z","iopub.execute_input":"2026-02-19T14:23:55.941444Z","iopub.status.idle":"2026-02-19T14:23:55.947332Z","shell.execute_reply.started":"2026-02-19T14:23:55.941400Z","shell.execute_reply":"2026-02-19T14:23:55.945964Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)\n)\n\ntest_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:26:52.005214Z","iopub.execute_input":"2026-02-19T14:26:52.005661Z","iopub.status.idle":"2026-02-19T14:26:52.850138Z","shell.execute_reply.started":"2026-02-19T14:26:52.005626Z","shell.execute_reply":"2026-02-19T14:26:52.848940Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    train_it,\n    steps_per_epoch=len(train_it),\n    validation_data=test_it,\n    validation_steps=len(test_it),\n    epochs=10,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:28:58.541067Z","iopub.execute_input":"2026-02-19T14:28:58.541479Z","iopub.status.idle":"2026-02-19T14:35:36.622447Z","shell.execute_reply.started":"2026-02-19T14:28:58.541450Z","shell.execute_reply":"2026-02-19T14:35:36.621446Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"_, acc = model.evaluate(test_it, steps=len(test_it), verbose=0)\nprint('> %.3f' % (acc * 100.0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:35:43.043321Z","iopub.execute_input":"2026-02-19T14:35:43.043773Z","iopub.status.idle":"2026-02-19T14:35:52.737257Z","shell.execute_reply.started":"2026-02-19T14:35:43.043736Z","shell.execute_reply":"2026-02-19T14:35:52.736210Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\t# save plot to file\npyplot.tight_layout()\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:35:54.987170Z","iopub.execute_input":"2026-02-19T14:35:54.987529Z","iopub.status.idle":"2026-02-19T14:35:55.574743Z","shell.execute_reply.started":"2026-02-19T14:35:54.987504Z","shell.execute_reply":"2026-02-19T14:35:55.573650Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# baseline model for the dogs vs cats dataset\nimport sys\nfrom matplotlib import pyplot\nfrom keras.utils import to_categorical\nfrom keras.models import Sequential\nfrom keras.layers import Conv2D\nfrom keras.layers import MaxPooling2D\nfrom keras.layers import Dense\nfrom keras.layers import Flatten\nfrom keras.optimizers import SGD\nfrom keras.preprocessing.image import ImageDataGenerator","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:36:00.529762Z","iopub.execute_input":"2026-02-19T14:36:00.530615Z","iopub.status.idle":"2026-02-19T14:36:00.536482Z","shell.execute_reply.started":"2026-02-19T14:36:00.530579Z","shell.execute_reply":"2026-02-19T14:36:00.535333Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = Sequential()\nmodel.add(Conv2D(32, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128, 128, 3)))   # ← แก้ตรงนี้\n\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dense(1, activation='sigmoid'))\n\n# compile model\nopt = SGD(learning_rate=0.001, momentum=0.9)   # ← แก้ lr → learning_rate\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:36:50.279064Z","iopub.execute_input":"2026-02-19T14:36:50.279518Z","iopub.status.idle":"2026-02-19T14:36:50.347651Z","shell.execute_reply.started":"2026-02-19T14:36:50.279483Z","shell.execute_reply":"2026-02-19T14:36:50.346725Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\n# save plot\npyplot.savefig('training_plot.png')   # ← แก้ตรงนี้\npyplot.close()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:37:45.662807Z","iopub.execute_input":"2026-02-19T14:37:45.663209Z","iopub.status.idle":"2026-02-19T14:37:45.963658Z","shell.execute_reply.started":"2026-02-19T14:37:45.663179Z","shell.execute_reply":"2026-02-19T14:37:45.962687Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# create data generator\ndatagen = ImageDataGenerator(rescale=1.0/255.0)\n\n# prepare iterators\ntrain_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)   # ← แก้ 200 → 128\n)\n\ntest_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)   # ← แก้ 200 → 128\n)\n\n# fit model\nhistory = model.fit(                     # ← แก้ fit_generator → fit\n    train_it,\n    steps_per_epoch=len(train_it),\n    validation_data=test_it,\n    validation_steps=len(test_it),\n    epochs=20,\n    verbose=0\n)\n\n# evaluate model\n_, acc = model.evaluate(                 # ← แก้ evaluate_generator → evaluate\n    test_it,\n    steps=len(test_it),\n    verbose=0\n)\n\nprint('> %.3f' % (acc * 100.0))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:39:02.383287Z","iopub.execute_input":"2026-02-19T14:39:02.383758Z","iopub.status.idle":"2026-02-19T14:41:01.126714Z","shell.execute_reply.started":"2026-02-19T14:39:02.383723Z","shell.execute_reply":"2026-02-19T14:41:01.123628Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## One Block VGG Model","metadata":{}},{"cell_type":"code","source":"model = Sequential()\nmodel.add(Conv2D(32, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128, 128, 3)))   # ← แก้ตรงนี้\n\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dense(1, activation='sigmoid'))\n\n# compile model\nopt = SGD(learning_rate=0.001, momentum=0.9)   # ← แก้ lr → learning_rate\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:41:07.838609Z","iopub.execute_input":"2026-02-19T14:41:07.839070Z","iopub.status.idle":"2026-02-19T14:41:07.905782Z","shell.execute_reply.started":"2026-02-19T14:41:07.839032Z","shell.execute_reply":"2026-02-19T14:41:07.904896Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    train_it,\n    steps_per_epoch=len(train_it),\n    validation_data=test_it,\n    validation_steps=len(test_it),\n    epochs=12,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:41:11.142688Z","iopub.execute_input":"2026-02-19T14:41:11.143430Z","iopub.status.idle":"2026-02-19T14:49:08.108830Z","shell.execute_reply.started":"2026-02-19T14:41:11.143394Z","shell.execute_reply":"2026-02-19T14:49:08.107781Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\t# save plot to file\npyplot.tight_layout()\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:50:31.428547Z","iopub.execute_input":"2026-02-19T14:50:31.429202Z","iopub.status.idle":"2026-02-19T14:50:32.017601Z","shell.execute_reply.started":"2026-02-19T14:50:31.429165Z","shell.execute_reply":"2026-02-19T14:50:32.016450Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Two Block VGG Model","metadata":{}},{"cell_type":"code","source":"# define cnn model\nmodel = Sequential()\nmodel.add(Conv2D(32, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128, 128, 3)))   # ← แก้ 200 → 128\n\nmodel.add(MaxPooling2D((2, 2)))\n\nmodel.add(Conv2D(64, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\n\nmodel.add(MaxPooling2D((2, 2)))\n\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dense(1, activation='sigmoid'))\n\n# compile model\nopt = SGD(learning_rate=0.001, momentum=0.9)   # ← แก้ lr → learning_rate\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:50:59.462418Z","iopub.execute_input":"2026-02-19T14:50:59.462851Z","iopub.status.idle":"2026-02-19T14:50:59.547144Z","shell.execute_reply.started":"2026-02-19T14:50:59.462815Z","shell.execute_reply":"2026-02-19T14:50:59.546013Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    train_it,\n    steps_per_epoch=len(train_it),\n    validation_data=test_it,\n    validation_steps=len(test_it),\n    epochs=12,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T14:51:20.038889Z","iopub.execute_input":"2026-02-19T14:51:20.039362Z","iopub.status.idle":"2026-02-19T14:59:25.983918Z","shell.execute_reply.started":"2026-02-19T14:51:20.039306Z","shell.execute_reply":"2026-02-19T14:59:25.982705Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\t# save plot to file\npyplot.tight_layout()\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:01:41.342293Z","iopub.execute_input":"2026-02-19T15:01:41.342811Z","iopub.status.idle":"2026-02-19T15:01:41.827137Z","shell.execute_reply.started":"2026-02-19T15:01:41.342765Z","shell.execute_reply":"2026-02-19T15:01:41.825943Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Three Block VGG Model","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout\n\nmodel = Sequential()\n\nmodel.add(Conv2D(32, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128, 128, 3)))  # ← แก้ 200 → 128\n\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(64, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(128, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2, 2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(1, activation='sigmoid'))\n\nopt = SGD(learning_rate=0.001, momentum=0.9)  # ← แก้ lr → learning_rate\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:10:32.948844Z","iopub.execute_input":"2026-02-19T15:10:32.949775Z","iopub.status.idle":"2026-02-19T15:10:33.077843Z","shell.execute_reply.started":"2026-02-19T15:10:32.949734Z","shell.execute_reply":"2026-02-19T15:10:33.076571Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"history = model.fit(\n    train_it,\n    steps_per_epoch=len(train_it),\n    validation_data=test_it,\n    validation_steps=len(test_it),\n    epochs=12,\n    verbose=1\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:11:17.399296Z","iopub.execute_input":"2026-02-19T15:11:17.399783Z","iopub.status.idle":"2026-02-19T15:19:37.486552Z","shell.execute_reply.started":"2026-02-19T15:11:17.399747Z","shell.execute_reply":"2026-02-19T15:19:37.485522Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\t# save plot to file\npyplot.tight_layout()\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:19:40.628284Z","iopub.execute_input":"2026-02-19T15:19:40.629453Z","iopub.status.idle":"2026-02-19T15:19:41.097944Z","shell.execute_reply.started":"2026-02-19T15:19:40.629390Z","shell.execute_reply":"2026-02-19T15:19:41.096600Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Dropout Regularization","metadata":{}},{"cell_type":"code","source":"# baseline model with dropout for dogs vs cats\nimport sys\nfrom matplotlib import pyplot\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# --- define model ---\nmodel = Sequential()\n\nmodel.add(Conv2D(32, (3,3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128,128,3)))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(64, (3,3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Conv2D(128, (3,3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2,2)))\nmodel.add(Dropout(0.2))\n\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dropout(0.5))\nmodel.add(Dense(1, activation='sigmoid'))\n\nopt = SGD(learning_rate=0.001, momentum=0.9)\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])\n\n# --- data generator ---\ndatagen = ImageDataGenerator(rescale=1.0/255.0)\n\ntrain_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128,128)\n)\n\ntest_it = datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128,128)\n)\n\n# --- train ---\nhistory = model.fit(\n    train_it,\n    validation_data=test_it,\n    epochs=20,\n    verbose=1\n)\n\n# --- evaluate ---\nloss, acc = model.evaluate(test_it, verbose=1)\nprint('> %.3f' % (acc * 100.0))\n\n# --- plot learning curves ---\npyplot.figure(figsize=(8,8))\n\n# loss\npyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], label='train')\npyplot.plot(history.history['val_loss'], label='test')\npyplot.legend()\n\n# accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], label='train')\npyplot.plot(history.history['val_accuracy'], label='test')\npyplot.legend()\n\npyplot.tight_layout()\npyplot.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:19:47.503784Z","iopub.execute_input":"2026-02-19T15:19:47.504239Z","iopub.status.idle":"2026-02-19T15:33:57.661596Z","shell.execute_reply.started":"2026-02-19T15:19:47.504206Z","shell.execute_reply":"2026-02-19T15:33:57.660433Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\npyplot.legend()\n\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\npyplot.legend()\n\t# save plot to file\npyplot.tight_layout()\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:34:22.062840Z","iopub.execute_input":"2026-02-19T15:34:22.063289Z","iopub.status.idle":"2026-02-19T15:34:22.705221Z","shell.execute_reply.started":"2026-02-19T15:34:22.063257Z","shell.execute_reply":"2026-02-19T15:34:22.704100Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Image Data Augmentation","metadata":{}},{"cell_type":"code","source":"# create data generators\ntrain_datagen = ImageDataGenerator(\n    rescale=1.0/255.0,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    horizontal_flip=True\n)\n\ntest_datagen = ImageDataGenerator(rescale=1.0/255.0)\n\n# prepare iterators\ntrain_it = train_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)   # ← แก้ 224 → 128\n)\n\ntest_it = test_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)   # ← แก้ 224 → 128\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:34:34.114216Z","iopub.execute_input":"2026-02-19T15:34:34.115160Z","iopub.status.idle":"2026-02-19T15:34:34.981958Z","shell.execute_reply.started":"2026-02-19T15:34:34.115122Z","shell.execute_reply":"2026-02-19T15:34:34.980744Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# baseline model with data augmentation for dogs vs cats\nimport sys\nfrom matplotlib import pyplot\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Dense, Flatten\nfrom tensorflow.keras.optimizers import SGD\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# --- define cnn model ---\nmodel = Sequential()\nmodel.add(Conv2D(32, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same',\n                 input_shape=(128, 128, 3)))   # ← แก้ 200 → 128\nmodel.add(MaxPooling2D((2, 2)))\n\nmodel.add(Conv2D(64, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2, 2)))\n\nmodel.add(Conv2D(128, (3, 3), activation='relu',\n                 kernel_initializer='he_uniform',\n                 padding='same'))\nmodel.add(MaxPooling2D((2, 2)))\n\nmodel.add(Flatten())\nmodel.add(Dense(128, activation='relu', kernel_initializer='he_uniform'))\nmodel.add(Dense(1, activation='sigmoid'))\n\nopt = SGD(learning_rate=0.001, momentum=0.9)   # ← แก้ lr\nmodel.compile(optimizer=opt, loss='binary_crossentropy', metrics=['accuracy'])\n\n# --- data generators ---\ntrain_datagen = ImageDataGenerator(\n    rescale=1.0/255.0,\n    width_shift_range=0.1,\n    height_shift_range=0.1,\n    horizontal_flip=True\n)\n\ntest_datagen = ImageDataGenerator(rescale=1.0/255.0)\n\ntrain_it = train_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)   # ← แก้ 200 → 128\n)\n\ntest_it = test_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=64,\n    target_size=(128, 128)\n)\n\n# --- train ---\nhistory = model.fit(                      # ← แก้ fit_generator\n    train_it,\n    validation_data=test_it,\n    epochs=20,\n    verbose=1\n)\n\n# --- evaluate ---\nloss, acc = model.evaluate(test_it, verbose=1)   # ← แก้ evaluate_generator\nprint('> %.3f' % (acc * 100.0))\n\n# --- plot learning curves ---\npyplot.figure(figsize=(8,8))\n\n# loss\npyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], label='train')\npyplot.plot(history.history['val_loss'], label='test')\npyplot.legend()\n\n# accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], label='train')\npyplot.plot(history.history['val_accuracy'], label='test')\npyplot.legend()\n\npyplot.tight_layout()\npyplot.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T15:34:37.843230Z","iopub.execute_input":"2026-02-19T15:34:37.843703Z","iopub.status.idle":"2026-02-19T16:14:22.993954Z","shell.execute_reply.started":"2026-02-19T15:34:37.843657Z","shell.execute_reply":"2026-02-19T16:14:22.992664Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\n\t# save plot to file\nfilename = sys.argv[0].split('/')[-1]\npyplot.savefig(filename + '_plot.png')\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T16:14:32.966788Z","iopub.execute_input":"2026-02-19T16:14:32.967242Z","iopub.status.idle":"2026-02-19T16:14:33.517327Z","shell.execute_reply.started":"2026-02-19T16:14:32.967206Z","shell.execute_reply":"2026-02-19T16:14:33.515815Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Explore Transfer Learning","metadata":{}},{"cell_type":"code","source":"from matplotlib import pyplot\nfrom tensorflow.keras.applications.vgg16 import VGG16, preprocess_input\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\n# --- load base VGG16 ---\nbase_model = VGG16(include_top=False, input_shape=(224, 224, 3))\n\nfor layer in base_model.layers:\n    layer.trainable = False\n\n# --- new classifier ---\nx = GlobalAveragePooling2D()(base_model.output)   # ← เร็วกว่า Flatten มาก\nx = Dense(128, activation='relu')(x)\noutput = Dense(1, activation='sigmoid')(x)\n\nmodel = Model(inputs=base_model.input, outputs=output)\n\nmodel.compile(\n    optimizer=Adam(learning_rate=0.0001),   # ← เร็วกว่า SGD\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\n# --- data generators ---\ntrain_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\ntest_datagen = ImageDataGenerator(preprocessing_function=preprocess_input)\n\ntrain_it = train_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/train/',\n    class_mode='binary',\n    batch_size=16,      # ← ปลอดภัย + เสถียร\n    target_size=(224, 224)\n)\n\ntest_it = test_datagen.flow_from_directory(\n    'dataset_dogs_vs_cats/test/',\n    class_mode='binary',\n    batch_size=16,\n    target_size=(224, 224)\n)\n\nhistory = model.fit(\n    train_it,\n    validation_data=test_it,\n    epochs=10,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T16:14:46.586408Z","iopub.execute_input":"2026-02-19T16:14:46.586882Z","iopub.status.idle":"2026-02-19T16:27:39.888324Z","shell.execute_reply.started":"2026-02-19T16:14:46.586830Z","shell.execute_reply":"2026-02-19T16:27:39.887233Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import tensorflow as tf\nfrom tensorflow import keras\nfrom tensorflow.keras.applications import VGG16\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, Flatten\nfrom tensorflow.keras.optimizers import Adam, SGD\n\nbase_model = VGG16(\n    weights='imagenet', \n    include_top=False, \n    input_shape=(224, 224, 3)\n)\n\n\nfor layer in base_model.layers:\n    layer.trainable = False\n\n\nx = Flatten()(base_model.output)\nx = Dense(128, activation='relu', kernel_initializer='he_uniform')(x)\noutput = Dense(1, activation='sigmoid')(x) \n\nmodel = Model(inputs=base_model.input, outputs=output)\n\nmodel.compile(\n    optimizer=Adam(learning_rate=0.001), \n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\nhistory = model.fit(\n    train_it,             \n    epochs=10,\n    validation_data=test_it,\n    verbose=1\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T16:27:45.088661Z","iopub.execute_input":"2026-02-19T16:27:45.089086Z","iopub.status.idle":"2026-02-19T16:40:12.726828Z","shell.execute_reply.started":"2026-02-19T16:27:45.089055Z","shell.execute_reply":"2026-02-19T16:40:12.725680Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pyplot.subplot(211)\npyplot.title('Cross Entropy Loss')\npyplot.plot(history.history['loss'], color='blue', label='train')\npyplot.plot(history.history['val_loss'], color='orange', label='test')\n\t# plot accuracy\npyplot.subplot(212)\npyplot.title('Classification Accuracy')\npyplot.plot(history.history['accuracy'], color='blue', label='train')\npyplot.plot(history.history['val_accuracy'], color='orange', label='test')\n\t# save plot to file\nfilename = sys.argv[0].split('/')[-1]\npyplot.savefig(filename + '_plot.png')\npyplot.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T16:40:28.762837Z","iopub.execute_input":"2026-02-19T16:40:28.763244Z","iopub.status.idle":"2026-02-19T16:40:29.302260Z","shell.execute_reply.started":"2026-02-19T16:40:28.763215Z","shell.execute_reply":"2026-02-19T16:40:29.301137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import numpy as np\nimport matplotlib.pyplot as plt\nimport tensorflow as tf\n\nprint(\"\\nRandom Inference (5 Cats and 5 Dogs):\")\nplt.figure(figsize=(15, 6))\n\ntest_it.reset()\n\ncat_images, dog_images = [], []\ncat_labels, dog_labels = [], []\n\nfor imgs, labels in test_it:\n    for i in range(len(imgs)):\n        if labels[i] == 0 and len(cat_images) < 5:\n            cat_images.append(imgs[i])\n            cat_labels.append(labels[i])\n        elif labels[i] == 1 and len(dog_images) < 5:\n            dog_images.append(imgs[i])\n            dog_labels.append(labels[i])\n\n    if len(cat_images) == 5 and len(dog_images) == 5:\n        break\n\nall_imgs = cat_images + dog_images\nall_labels = cat_labels + dog_labels\n\nfor i in range(10):\n    plt.subplot(2, 5, i + 1)\n\n    img = all_imgs[i]\n\n    # 🔧 undo preprocess_input ของ VGG16\n    img_display = img.copy()\n    img_display[:, :, 0] += 103.939\n    img_display[:, :, 1] += 116.779\n    img_display[:, :, 2] += 123.68\n    img_display = img_display[:, :, ::-1]\n    img_display = np.clip(img_display, 0, 255).astype(\"uint8\")\n\n    plt.imshow(img_display)\n\n    img_input = tf.expand_dims(img, 0)\n    prediction = model.predict(img_input, verbose=0)[0][0]\n\n    pred_label = \"Dog\" if prediction > 0.5 else \"Cat\"\n    true_label = \"Dog\" if all_labels[i] == 1 else \"Cat\"\n    color = \"green\" if pred_label == true_label else \"red\"\n    score = prediction if prediction > 0.5 else 1 - prediction\n\n    plt.title(\n        f\"True: {true_label}\\nPred: {pred_label}\\n({score:.2%})\",\n        color=color,\n        fontsize=10\n    )\n    plt.axis(\"off\")\n\nplt.tight_layout()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-02-19T16:43:07.933403Z","iopub.execute_input":"2026-02-19T16:43:07.933900Z","iopub.status.idle":"2026-02-19T16:43:09.825056Z","shell.execute_reply.started":"2026-02-19T16:43:07.933838Z","shell.execute_reply":"2026-02-19T16:43:09.823862Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}