{"metadata":{"kernelspec":{"name":"python3","display_name":"Python 3","language":"python"},"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":[],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n\n# =========================\n# CONFIG\n# =========================\nIMG_SIZE = (224, 224)\nBATCH_SIZE = 64\nEPOCHS = 10\nMODEL_NAME = \"MobileNetV2\"\n\nUSE_AUGMENTATION = False   # 🔥 TOGGLE HERE\n\ntrain_dir = '/kaggle/input/datasets/samuelcortinhas/muffin-vs-chihuahua-image-classification/train'\n\n# =========================\n# COUNT IMAGES\n# =========================\nclass_counts = {}\ntotal_images = 0\n\nfor class_name in os.listdir(train_dir):\n    class_path = os.path.join(train_dir, class_name)\n    if os.path.isdir(class_path):\n        count = len(os.listdir(class_path))\n        class_counts[class_name] = count\n        total_images += count\n\nprint(\"📊 Class Distribution:\")\nfor k, v in class_counts.items():\n    print(f\"{k}: {v} images\")\n\nprint(\"Total images:\", total_images)\n\n# =========================\n# MIXED PRECISION\n# =========================\nfrom tensorflow.keras import mixed_precision\nmixed_precision.set_global_policy('mixed_float16')\n\n# =========================\n# MULTI-GPU\n# =========================\nstrategy = tf.distribute.MirroredStrategy()\nprint(\"Number of GPUs:\", strategy.num_replicas_in_sync)\n\nGLOBAL_BATCH_SIZE = BATCH_SIZE * strategy.num_replicas_in_sync\n\n# =========================\n# LOAD DATA\n# =========================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    validation_split=0.2,\n    subset=\"training\",\n    seed=123,\n    image_size=IMG_SIZE,\n    batch_size=GLOBAL_BATCH_SIZE\n)\n\nval_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    validation_split=0.2,\n    subset=\"validation\",\n    seed=123,\n    image_size=IMG_SIZE,\n    batch_size=GLOBAL_BATCH_SIZE\n)\n\nprint(\"\\nDetected Classes:\", train_ds.class_names)\n\n# =========================\n# PREPROCESSING\n# =========================\ndef get_preprocess_function(model_name):\n    if model_name == \"MobileNetV2\":\n        return tf.keras.applications.mobilenet_v2.preprocess_input\n    elif model_name == \"ResNet50\":\n        return tf.keras.applications.resnet50.preprocess_input\n    elif model_name == \"EfficientNetB0\":\n        return tf.keras.applications.efficientnet.preprocess_input\n    else:\n        return lambda x: x / 255.0\n\npreprocess_input = get_preprocess_function(MODEL_NAME)\n\nAUTOTUNE = tf.data.AUTOTUNE\n\ndef preprocess(x, y):\n    x = preprocess_input(x)\n    return x, y\n\n# =========================\n# AUGMENTATION (TOGGLE)\n# =========================\ndata_augmentation = tf.keras.Sequential([\n    layers.RandomFlip(\"horizontal\"),\n    layers.RandomRotation(0.1),\n    layers.RandomZoom(0.1),\n])\n\ndef augment(x, y):\n    x = data_augmentation(x)\n    return x, y\n\n# =========================\n# PIPELINE\n# =========================\ntrain_ds = train_ds.map(preprocess, num_parallel_calls=AUTOTUNE)\n\nif USE_AUGMENTATION:\n    print(\"\\n✅ Data Augmentation: ON\")\n    train_ds = train_ds.map(augment, num_parallel_calls=AUTOTUNE)\n    print(\"Effective samples:\", total_images * EPOCHS)\nelse:\n    print(\"\\n❌ Data Augmentation: OFF\")\n    print(\"Effective samples:\", total_images)\n\nval_ds = val_ds.map(preprocess, num_parallel_calls=AUTOTUNE)\n\ntrain_ds = train_ds.shuffle(1000).prefetch(AUTOTUNE)\nval_ds = val_ds.prefetch(AUTOTUNE)\n\n# =========================\n# MODEL\n# =========================\nwith strategy.scope():\n\n    MODEL_MAP = {\n        \"MobileNetV2\": tf.keras.applications.MobileNetV2,\n        \"ResNet50\": tf.keras.applications.ResNet50,\n        \"EfficientNetB0\": tf.keras.applications.EfficientNetB0\n    }\n\n    BaseModelClass = MODEL_MAP[MODEL_NAME]\n\n    try:\n        base_model = BaseModelClass(\n            input_shape=(224,224,3),\n            include_top=False,\n            weights='imagenet'\n        )\n        print(\"Loaded pretrained weights for\", MODEL_NAME)\n    except:\n        print(\"Using random weights\")\n        base_model = BaseModelClass(\n            input_shape=(224,224,3),\n            include_top=False,\n            weights=None\n        )\n\n    base_model.trainable = False\n\n    x = base_model.output\n    x = layers.GlobalAveragePooling2D()(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.Dense(128, activation='relu')(x)\n    x = layers.Dropout(0.5)(x)\n\n    output = layers.Dense(1, activation='sigmoid', dtype='float32')(x)\n\n    model = models.Model(inputs=base_model.input, outputs=output)\n\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(1e-4),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n\nmodel.summary()\n\n# =========================\n# CALLBACKS\n# =========================\ncallbacks = [\n    EarlyStopping(patience=3, restore_best_weights=True),\n    ModelCheckpoint(\"best_model.h5\", save_best_only=True)\n]\n\n# =========================\n# TRAIN\n# =========================\nhistory = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=EPOCHS,\n    callbacks=callbacks\n)\n\n# =========================\n# FINE-TUNE\n# =========================\nwith strategy.scope():\n    base_model.trainable = True\n\n    for layer in base_model.layers[:-20]:\n        layer.trainable = False\n\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(1e-5),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n\nhistory_fine = model.fit(\n    train_ds,\n    validation_data=val_ds,\n    epochs=10,\n    callbacks=callbacks\n)\n\n# =========================\n# PLOT\n# =========================\ndef plot_history(history):\n    plt.figure(figsize=(12,5))\n\n    plt.subplot(1,2,1)\n    plt.plot(history.history['accuracy'], label='Train')\n    plt.plot(history.history['val_accuracy'], label='Val')\n    plt.title(\"Accuracy\")\n    plt.legend()\n\n    plt.subplot(1,2,2)\n    plt.plot(history.history['loss'], label='Train')\n    plt.plot(history.history['val_loss'], label='Val')\n    plt.title(\"Loss\")\n    plt.legend()\n\n    plt.show()\n\nplot_history(history)\nimport os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nfrom tqdm import tqdm   # 🔥 progress bar\n\n# =========================\n# CONFIG\n# =========================\nIMG_SIZE = (224, 224)\nMODEL_NAME = \"MobileNetV2\"\n\ntrain_dir = '/kaggle/input/datasets/samuelcortinhas/muffin-vs-chihuahua-image-classification/train'\ntest_dir  = \"/kaggle/input/datasets/otaku09/chihuahua/kaggle_test_final\"\n\n# =========================\n# AUTO DETECT CLASS NAMES\n# =========================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    image_size=IMG_SIZE,\n    batch_size=1\n)\n\nclass_names = train_ds.class_names\nprint(\"✅ Detected class order:\", class_names)\n\n# =========================\n# PREPROCESS FUNCTION\n# =========================\ndef get_preprocess_function(model_name):\n    if model_name == \"MobileNetV2\":\n        return tf.keras.applications.mobilenet_v2.preprocess_input\n    elif model_name == \"ResNet50\":\n        return tf.keras.applications.resnet50.preprocess_input\n    elif model_name == \"EfficientNetB0\":\n        return tf.keras.applications.efficientnet.preprocess_input\n    else:\n        return lambda x: x / 255.0\n\npreprocess_input = get_preprocess_function(MODEL_NAME)\n\n# =========================\n# LOAD MODEL\n# =========================\nmodel = load_model(\"best_model.h5\")\nprint(\"✅ Model loaded\")\n\n# =========================\n# GET IMAGE LIST\n# =========================\nimage_files = sorted([\n    f for f in os.listdir(test_dir)\n    if f.lower().endswith(('.jpg', '.jpeg', '.png'))\n])\n\nprint(f\"Total test images: {len(image_files)}\")\n\n# =========================\n# PREDICTION LOOP WITH PROGRESS\n# =========================\nresults = []\n\nfor img_name in tqdm(image_files, desc=\"🔍 Predicting\"):\n\n    img_path = os.path.join(test_dir, img_name)\n\n    img = Image.open(img_path).convert(\"RGB\")\n    img = img.resize(IMG_SIZE)\n\n    img_array = np.array(img)\n    img_array = np.expand_dims(img_array, axis=0)\n\n    img_array = preprocess_input(img_array)\n\n    pred_prob = model.predict(img_array, verbose=0)\n    pred = int(pred_prob > 0.5)\n\n    label_name = class_names[pred]\n\n    results.append([img_name, label_name])\n\n# =========================\n# SAVE CSV\n# =========================\ndf = pd.DataFrame(results, columns=[\"id\", \"label\"])\ndf = df.sort_values(\"id\").reset_index(drop=True)\n\ndf.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"✅ Submission file created!\")\ndf = pd.read_csv(\"submission.csv\")\nprint(df.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom PIL import Image\nimport tensorflow as tf\nfrom tensorflow.keras.models import load_model\nfrom tqdm import tqdm\n\n# =========================\n# CONFIG\n# =========================\nIMG_SIZE = (224, 224)\nMODEL_NAME = \"MobileNetV2\"\n\ntrain_dir = '/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train'\ntest_dir  = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/kaggle_test_final\"\nsample_path = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/sample_submission.csv\"\n\n# =========================\n# AUTO DETECT CLASS NAMES\n# =========================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    image_size=IMG_SIZE,\n    batch_size=1\n)\n\nclass_names = train_ds.class_names\nprint(\"✅ Detected class order:\", class_names)\n\n# =========================\n# PREPROCESS FUNCTION\n# =========================\ndef get_preprocess_function(model_name):\n    if model_name == \"MobileNetV2\":\n        return tf.keras.applications.mobilenet_v2.preprocess_input\n    elif model_name == \"ResNet50\":\n        return tf.keras.applications.resnet50.preprocess_input\n    elif model_name == \"EfficientNetB0\":\n        return tf.keras.applications.efficientnet.preprocess_input\n    else:\n        return lambda x: x / 255.0\n\npreprocess_input = get_preprocess_function(MODEL_NAME)\n\n# =========================\n# LOAD MODEL\n# =========================\nmodel = load_model(\"best_model.h5\")\nprint(\"✅ Model loaded\")\n\n# =========================\n# GET IMAGE LIST\n# =========================\nimage_files = sorted([\n    f for f in os.listdir(test_dir)\n    if f.lower().endswith(('.jpg', '.jpeg', '.png'))\n])\n\nprint(f\"Total test images: {len(image_files)}\")\n\n# =========================\n# PREDICTION LOOP\n# =========================\nresults = []\n\nfor img_name in tqdm(image_files, desc=\"🔍 Predicting\"):\n\n    img_path = os.path.join(test_dir, img_name)\n\n    img = Image.open(img_path).convert(\"RGB\")\n    img = img.resize(IMG_SIZE)\n\n    img_array = np.array(img)\n    img_array = np.expand_dims(img_array, axis=0)\n\n    img_array = preprocess_input(img_array)\n\n    pred_prob = model.predict(img_array, verbose=0)\n    pred = int(pred_prob > 0.5)\n\n    label_name = class_names[pred]\n\n    results.append([img_name, label_name])\n\n# =========================\n# MATCH SAMPLE SUBMISSION FORMAT\n# =========================\nsample = pd.read_csv(sample_path)\nprint(\"Required columns:\", sample.columns.tolist())\n\nsubmission = pd.DataFrame({\n    sample.columns[0]: [r[0] for r in results],  # ID\n    sample.columns[1]: [r[1] for r in results]   # Label\n})\n\n# Sort correctly\nsubmission = submission.sort_values(sample.columns[0]).reset_index(drop=True)\n\n# Save\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"✅ Submission file created successfully!\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\n\n# =========================\n# CONFIG\n# =========================\nIMG_SIZE = (224,224)\nBATCH_SIZE = 32\nEPOCHS = 5\ntrain_dir = '/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/train'\n\n# =========================\n# MODEL CONFIGS\n# =========================\nMODEL_CONFIGS = {\n    \"MobileNetV2\": {\n        \"model\": tf.keras.applications.MobileNetV2,\n        \"preprocess\": tf.keras.applications.mobilenet_v2.preprocess_input\n    },\n    \"ResNet50\": {\n        \"model\": tf.keras.applications.ResNet50,\n        \"preprocess\": tf.keras.applications.resnet50.preprocess_input\n    },\n    \"EfficientNetB0\": {\n        \"model\": tf.keras.applications.EfficientNetB0,\n        \"preprocess\": tf.keras.applications.efficientnet.preprocess_input\n    }\n}\n\nresults = {}\nhistories = {}\n\nbest_acc = 0\nbest_model = None\nbest_model_name = \"\"\n\n# =========================\n# TRAIN LOOP\n# =========================\nfor name, config in MODEL_CONFIGS.items():\n\n    print(f\"\\n🚀 Training {name}\")\n\n    preprocess_input = config[\"preprocess\"]\n\n    # DATA\n    train_ds = tf.keras.utils.image_dataset_from_directory(\n        train_dir,\n        validation_split=0.2,\n        subset=\"training\",\n        seed=123,\n        image_size=IMG_SIZE,\n        batch_size=BATCH_SIZE\n    )\n\n    val_ds = tf.keras.utils.image_dataset_from_directory(\n        train_dir,\n        validation_split=0.2,\n        subset=\"validation\",\n        seed=123,\n        image_size=IMG_SIZE,\n        batch_size=BATCH_SIZE\n    )\n\n    AUTOTUNE = tf.data.AUTOTUNE\n\n    def preprocess(x, y):\n        x = preprocess_input(x)\n        return x, y\n\n    train_ds = train_ds.map(preprocess).prefetch(AUTOTUNE)\n    val_ds = val_ds.map(preprocess).prefetch(AUTOTUNE)\n\n    # MODEL\n    base_model = config[\"model\"](\n        input_shape=(224,224,3),\n        include_top=False,\n        weights='imagenet'\n    )\n\n    base_model.trainable = False\n\n    x = base_model.output\n    x = tf.keras.layers.GlobalAveragePooling2D()(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.Dense(128, activation='relu')(x)\n    x = tf.keras.layers.Dropout(0.5)(x)\n    output = tf.keras.layers.Dense(1, activation='sigmoid')(x)\n\n    model = tf.keras.Model(inputs=base_model.input, outputs=output)\n\n    model.compile(\n        optimizer=tf.keras.optimizers.Adam(1e-4),\n        loss='binary_crossentropy',\n        metrics=['accuracy']\n    )\n\n    # TRAIN\n    history = model.fit(\n        train_ds,\n        validation_data=val_ds,\n        epochs=EPOCHS,\n        verbose=1\n    )\n\n    histories[name] = history.history\n\n    val_acc = max(history.history['val_accuracy'])\n    results[name] = val_acc\n\n    print(f\"✅ {name} Val Accuracy: {val_acc:.4f}\")\n\n    if val_acc > best_acc:\n        best_acc = val_acc\n        best_model = model\n        best_model_name = name\n\n# =========================\n# SAVE BEST MODEL\n# =========================\nbest_model.save(\"best_model.h5\")\n\nprint(\"\\n🏆 Best Model:\", best_model_name)\nprint(\"Best Accuracy:\", best_acc)\n\n# =========================\n# 📊 PLOTTING\n# =========================\nepochs_range = range(1, EPOCHS+1)\n\nplt.figure(figsize=(14,6))\n\n# Accuracy Plot\nplt.subplot(1,2,1)\nfor name, hist in histories.items():\n    plt.plot(epochs_range, hist['val_accuracy'], marker='o', label=name)\n\nplt.title(\"Validation Accuracy Comparison\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.legend()\nplt.grid(True)\n\n# Loss Plot\nplt.subplot(1,2,2)\nfor name, hist in histories.items():\n    plt.plot(epochs_range, hist['val_loss'], marker='o', label=name)\n\nplt.title(\"Validation Loss Comparison\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.legend()\nplt.grid(True)\n\nplt.show()\n\n# =========================\n# PRINT RESULTS\n# =========================\nprint(\"\\n📊 Final Comparison:\")\nfor k, v in results.items():\n    print(f\"{k}: {v:.4f}\")\n    \n# =========================\n# CONFIG FOR CSV\n# =========================\nUSE_SAMPLE_FORMAT = True   # 🔥 Toggle\nMANUAL_COLUMNS = [\"Id\", \"Label\"]   # used if above = False\n\ntest_dir = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/kaggle_test_final\"\nsample_path = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/test_solution_01.csv\"\n\nprint(\"\\n🚀 Generating submission using:\", best_model_name)\n\n# =========================\n# PREPROCESS (BEST MODEL)\n# =========================\npreprocess_input = MODEL_CONFIGS[best_model_name][\"preprocess\"]\n\n# =========================\n# CLASS NAMES (AUTO)\n# =========================\ntrain_ds = tf.keras.utils.image_dataset_from_directory(\n    train_dir,\n    image_size=IMG_SIZE,\n    batch_size=1\n)\nclass_names = train_ds.class_names\n\n# =========================\n# IMAGE LIST\n# =========================\nimage_files = sorted([\n    f for f in os.listdir(test_dir)\n    if f.lower().endswith(('.jpg', '.jpeg', '.png'))\n])\n\n# =========================\n# PREDICTION\n# =========================\nfrom PIL import Image\nimport numpy as np\nfrom tqdm import tqdm\n\nresults = []\n\nfor img_name in tqdm(image_files, desc=\"🔍 Predicting\"):\n\n    img_path = os.path.join(test_dir, img_name)\n\n    img = Image.open(img_path).convert(\"RGB\")\n    img = img.resize(IMG_SIZE)\n\n    img_array = np.array(img)\n    img_array = np.expand_dims(img_array, axis=0)\n\n    img_array = preprocess_input(img_array)\n\n    pred_prob = best_model.predict(img_array, verbose=0)\n    pred = int(pred_prob > 0.5)\n\n    label = class_names[pred]\n\n    results.append([img_name, label])\n\n# =========================\n# CREATE CSV (FLEXIBLE)\n# =========================\nimport pandas as pd\n\nif USE_SAMPLE_FORMAT:\n    sample = pd.read_csv(sample_path)\n    print(\"Using sample format:\", sample.columns.tolist())\n\n    submission = pd.DataFrame({\n        sample.columns[0]: [r[0] for r in results],\n        sample.columns[1]: [r[1] for r in results]\n    })\n\n    sort_col = sample.columns[0]\n\nelse:\n    print(\"Using manual columns:\", MANUAL_COLUMNS)\n\n    submission = pd.DataFrame({\n        MANUAL_COLUMNS[0]: [r[0] for r in results],\n        MANUAL_COLUMNS[1]: [r[1] for r in results]\n    })\n\n    sort_col = MANUAL_COLUMNS[0]\n\n# =========================\n# SORT + SAVE\n# =========================\nsubmission = submission.sort_values(sort_col).reset_index(drop=True)\n\nsubmission.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"✅ submission.csv created successfully!\")  \nprint(df.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================\n# 1. IMPORTS\n# =============================\nimport torch\nimport torch.nn as nn\nimport torch.optim as optim\nfrom torchvision import datasets, transforms, models\nfrom torch.utils.data import DataLoader, random_split\nfrom PIL import Image\nimport pandas as pd\nimport os\n\ndevice = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n\n# =============================\n# 2. DATASET (AUTO SPLIT)\n# =============================\ndata_dir = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge\"\n\ntransform = transforms.Compose([\n    transforms.Resize((224, 224)),\n    transforms.ToTensor(),\n])\n\nfull_dataset = datasets.ImageFolder(\n    os.path.join(data_dir, \"train\"),\n    transform=transform\n)\n\ntrain_size = int(0.8 * len(full_dataset))\nval_size = len(full_dataset) - train_size\n\ntrain_data, val_data = random_split(full_dataset, [train_size, val_size])\n\ntrain_loader = DataLoader(train_data, batch_size=32, shuffle=True)\nval_loader = DataLoader(val_data, batch_size=32)\n\n# =============================\n# 3. MODEL FUNCTION\n# =============================\ndef get_model(name):\n    if name == \"resnet\":\n        model = models.resnet18(pretrained=True)\n        model.fc = nn.Linear(model.fc.in_features, 2)\n\n    elif name == \"efficientnet\":\n        model = models.efficientnet_b0(pretrained=True)\n        model.classifier[1] = nn.Linear(model.classifier[1].in_features, 2)\n\n    elif name == \"mobilenet\":\n        model = models.mobilenet_v2(pretrained=True)\n        model.classifier[1] = nn.Linear(model.classifier[1].in_features, 2)\n\n    elif name == \"densenet\":\n        model = models.densenet121(pretrained=True)\n        model.classifier = nn.Linear(model.classifier.in_features, 2)\n\n    elif name == \"vit\":\n        model = models.vit_b_16(pretrained=True)\n        model.heads.head = nn.Linear(model.heads.head.in_features, 2)\n\n    return model.to(device)\n\n# =============================\n# 4. TRAIN + VALIDATE FUNCTION\n# =============================\ndef train_model(model, name, epochs=5):\n    criterion = nn.CrossEntropyLoss()\n    optimizer = optim.Adam(model.parameters(), lr=0.0001)\n\n    best_acc = 0\n\n    for epoch in range(epochs):\n        # ---- TRAIN ----\n        model.train()\n        train_loss = 0\n        correct = 0\n        total = 0\n\n        for images, labels in train_loader:\n            images, labels = images.to(device), labels.to(device)\n\n            optimizer.zero_grad()\n            outputs = model(images)\n            loss = criterion(outputs, labels)\n\n            loss.backward()\n            optimizer.step()\n\n            train_loss += loss.item()\n            _, pred = torch.max(outputs, 1)\n            total += labels.size(0)\n            correct += (pred == labels).sum().item()\n\n        train_acc = 100 * correct / total\n\n        # ---- VALIDATION ----\n        model.eval()\n        correct = 0\n        total = 0\n\n        with torch.no_grad():\n            for images, labels in val_loader:\n                images, labels = images.to(device), labels.to(device)\n                outputs = model(images)\n\n                _, pred = torch.max(outputs, 1)\n                total += labels.size(0)\n                correct += (pred == labels).sum().item()\n\n        val_acc = 100 * correct / total\n\n        print(f\"{name} | Epoch {epoch+1}\")\n        print(f\"Train Loss: {train_loss:.4f} | Train Acc: {train_acc:.2f}%\")\n        print(f\"Val Acc: {val_acc:.2f}%\\n\")\n\n        # Save best model\n        if val_acc > best_acc:\n            best_acc = val_acc\n            torch.save(model.state_dict(), f\"/kaggle/working/best_{name}.pth\")\n\n    return best_acc\n\n# =============================\n# 5. TRAIN ALL MODELS\n# =============================\nmodel_names = [\"resnet\", \"efficientnet\", \"mobilenet\", \"densenet\", \"vit\"]\nresults = {}\n\nfor name in model_names:\n    print(f\"\\n🔥 Training {name}...\\n\")\n    model = get_model(name)\n\n    best_acc = train_model(model, name, epochs=5)\n    results[name] = best_acc\n\nprint(\"\\nAll Results:\", results)\n\n# =============================\n# 6. SELECT BEST MODEL\n# =============================\nbest_model_name = max(results, key=results.get)\nprint(\"\\n🏆 Best Model:\", best_model_name)\n\nmodel = get_model(best_model_name)\nmodel.load_state_dict(torch.load(f\"/kaggle/working/best_{best_model_name}.pth\"))\nmodel.eval()\n\n# =============================\n# 7. TEST PREDICTION\n# =============================\n# =============================\n# 7. TEST PREDICTION\n# =============================\ntest_dir = \"/kaggle/input/competitions/cs-460-muffin-vs-chihuahua-classification-challenge/kaggle_test_final\"\n\nclass_names = full_dataset.classes  # ['chihuahua', 'muffin']\n\nresults = []\n\nfor img_name in os.listdir(test_dir):\n    img_path = os.path.join(test_dir, img_name)\n\n    image = Image.open(img_path).convert(\"RGB\")\n    image = transform(image).unsqueeze(0).to(device)\n\n    with torch.no_grad():\n        output = model(image)\n        _, pred = torch.max(output, 1)\n\n    label_name = class_names[pred.item()]\n    results.append([img_name, label_name])\n\n# =============================\n# 8. SAVE CSV\n# =============================\ndf = pd.DataFrame(results, columns=[\"ID\", \"Label\"])\ndf.to_csv(\"/kaggle/working/submission.csv\", index=False)\n\nprint(\"✅ Submission file created!\")\nprint(df.head())","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}