{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"language_info":{"name":"python","version":"3.13.15","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[],"dockerImageVersionId":28755,"isGPUEnabled":true,"isInternetEnabled":true,"language":"python","sourceType":"notebook"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport shutil\nimport zipfile\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import classification_report\n\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications import MobileNetV2, ResNet50V2, EfficientNetB0\nfrom tensorflow.keras import layers, models, optimizers\n\nprint(\"TensorFlow Version:\", tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T13:46:35.184871Z","iopub.execute_input":"2026-10-03T13:46:35.185293Z","iopub.status.idle":"2026-10-03T13:46:55.361552Z","shell.execute_reply.started":"2026-10-03T13:46:35.18527Z","shell.execute_reply":"2026-10-03T13:46:55.360575Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 1. ค้นหาและจัดโครงสร้างรูปภาพให้อยู่ใน subfolder (cats/dogs) เพื่อป้องกัน FileNotFoundError\nbase_input_dir = '/kaggle/input'\nextract_dir = '/kaggle/working/data'\ntrain_dir = os.path.join(extract_dir, 'train')\n\ncats_dir = os.path.join(train_dir, 'cats')\ndogs_dir = os.path.join(train_dir, 'dogs')\nos.makedirs(cats_dir, exist_ok=True)\nos.makedirs(dogs_dir, exist_ok=True)\n\nzip_path = None\nraw_images_dir = None\n\nfor root, dirs, files in os.walk(base_input_dir):\n    for file in files:\n        if file.endswith('.zip') and ('train' in file.lower() or 'dog' in file.lower()):\n            zip_path = os.path.join(root, file)\n            break\n        elif file.lower().startswith(('cat.', 'dog.')):\n            raw_images_dir = root\n            break\n\nif zip_path:\n    print(f\"Extracting zip file: {zip_path}\")\n    with zipfile.ZipFile(zip_path, 'r') as zip_ref:\n        zip_ref.extractall('/kaggle/working/tmp')\n    for root, dirs, files in os.walk('/kaggle/working/tmp'):\n        if any(f.startswith(('cat.', 'dog.')) for f in files):\n            raw_images_dir = root\n            break\n\nif not raw_images_dir:\n    raise FileNotFoundError(\"ไม่พบรูปภาพ! กรุณากด '+ Add Input' ด้านขวามือ แล้วเพิ่ม Dataset เช่น 'dogs-vs-cats'\")\n\nprint(f\"Organizing images from: {raw_images_dir}\")\nfor fname in os.listdir(raw_images_dir):\n    src_path = os.path.join(raw_images_dir, fname)\n    if fname.lower().startswith('cat.'):\n        shutil.move(src_path, os.path.join(cats_dir, fname))\n    elif fname.lower().startswith('dog.'):\n        shutil.move(src_path, os.path.join(dogs_dir, fname))\n\nprint(f\"Cats: {len(os.listdir(cats_dir))}, Dogs: {len(os.listdir(dogs_dir))}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T13:46:55.363316Z","iopub.execute_input":"2026-10-03T13:46:55.363877Z","iopub.status.idle":"2026-10-03T13:47:16.792422Z","shell.execute_reply.started":"2026-10-03T13:46:55.363852Z","shell.execute_reply":"2026-10-03T13:47:16.791684Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 2. Data Augmentation & Data Flow Generators\nIMAGE_SIZE = (224, 224)\nBATCH_SIZE = 32\n\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,\n    rotation_range=20,\n    width_shift_range=0.15,\n    height_shift_range=0.15,\n    horizontal_flip=True,\n    validation_split=0.2\n)\n\ntrain_generator = train_datagen.flow_from_directory(\n    train_dir,\n    target_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    subset='training'\n)\n\ntest_generator = train_datagen.flow_from_directory(\n    train_dir,\n    target_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    subset='validation',\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T13:47:16.793424Z","iopub.execute_input":"2026-10-03T13:47:16.793828Z","iopub.status.idle":"2026-10-03T13:47:17.12223Z","shell.execute_reply.started":"2026-10-03T13:47:16.793805Z","shell.execute_reply":"2026-10-03T13:47:17.121538Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 3. Function สร้างโครงสร้างโมเดล Transfer Learning\ndef create_model(base_model_class):\n    base_model = base_model_class(weights='imagenet', include_top=False, input_shape=(224, 224, 3))\n    base_model.trainable = False\n\n    model = models.Sequential([\n        base_model,\n        layers.GlobalAveragePooling2D(),\n        layers.BatchNormalization(),\n        layers.Dropout(0.3),\n        layers.Dense(128, activation='relu'),\n        layers.Dense(1, activation='sigmoid')\n    ])\n    return model, base_model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T13:47:17.123615Z","iopub.execute_input":"2026-10-03T13:47:17.123856Z","iopub.status.idle":"2026-10-03T13:47:17.135198Z","shell.execute_reply.started":"2026-10-03T13:47:17.123837Z","shell.execute_reply":"2026-10-03T13:47:17.134555Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 4. เทรนและ Fine-Tune Pre-trained Models ทั้ง 3 ตัว\nmodels_dict = {\n    'MobileNetV2': MobileNetV2,\n    'ResNet50V2': ResNet50V2,\n    'EfficientNetB0': EfficientNetB0\n}\n\ntrained_models = {}\n\nfor name, model_class in models_dict.items():\n    print(f\"\\n================ Training: {name} ================\")\n    model, base_model = create_model(model_class)\n    \n    # Phase 1: Train Top Layers\n    model.compile(optimizer=optimizers.Adam(1e-3), loss='binary_crossentropy', metrics=['accuracy'])\n    model.fit(train_generator, epochs=3, validation_data=test_generator)\n    \n    # Phase 2: Fine-Tuning\n    base_model.trainable = True\n    for layer in base_model.layers[:-30]:\n        layer.trainable = False\n        \n    model.compile(optimizer=optimizers.Adam(1e-5), loss='binary_crossentropy', metrics=['accuracy'])\n    model.fit(train_generator, epochs=5, validation_data=test_generator)\n    \n    trained_models[name] = model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T13:47:17.136098Z","iopub.execute_input":"2026-10-03T13:47:17.136404Z","iopub.status.idle":"2026-10-03T15:39:03.329553Z","shell.execute_reply.started":"2026-10-03T13:47:17.136375Z","shell.execute_reply":"2026-10-03T15:39:03.328865Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 5. ประเมินผลด้วย Classification Report บน Test Set\nprint(\"\\n================ FINAL CLASSIFICATION REPORTS ================\")\nfor name, model in trained_models.items():\n    print(f\"\\n>>> Classification Report for Model: {name} <<<\")\n    test_generator.reset()\n    preds = model.predict(test_generator)\n    y_pred = (preds > 0.5).astype(int).ravel()\n    y_true = test_generator.classes\n    print(classification_report(y_true, y_pred, target_names=['Cat', 'Dog'], digits=4))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-10-03T15:39:03.330643Z","iopub.execute_input":"2026-10-03T15:39:03.330964Z"}},"outputs":[],"execution_count":null}]}