{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[],"dockerImageVersionId":28755,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"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\n\n# Use the kagglehub client library to attach Kaggle resources like competitions, datasets, and models to your session\n# Learn more about kagglehub: https://github.com/Kaggle/kagglehub/blob/main/README.md\n\nimport kagglehub\n# kagglehub.dataset_download('<owner>/<dataset-slug>')","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:04.487793Z","iopub.execute_input":"2026-07-26T17:37:04.488299Z","iopub.status.idle":"2026-07-26T17:37:16.957228Z","shell.execute_reply.started":"2026-07-26T17:37:04.488257Z","shell.execute_reply":"2026-07-26T17:37:16.956005Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import os\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\nfrom PIL import Image\n\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\n\nfrom tensorflow.keras.applications import EfficientNetB0, MobileNetV2\nfrom tensorflow.keras.applications.efficientnet import preprocess_input as efficientnet_preprocess\nfrom tensorflow.keras.applications.efficientnet import decode_predictions\nfrom tensorflow.keras.applications.mobilenet_v2 import preprocess_input as mobilenet_preprocess\n\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n\nfrom sklearn.model_selection import train_test_split\n\nprint(\"TensorFlow version:\", tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:16.959173Z","iopub.execute_input":"2026-07-26T17:37:16.95949Z","iopub.status.idle":"2026-07-26T17:37:16.967176Z","shell.execute_reply.started":"2026-07-26T17:37:16.959457Z","shell.execute_reply":"2026-07-26T17:37:16.966369Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_dirs = []\n\nfor dirname, _, filenames in os.walk(\"/kaggle/input\"):\n    image_files = [\n        f for f in filenames\n        if f.lower().endswith((\".jpg\", \".jpeg\", \".png\"))\n    ]\n    \n    if len(image_files) > 0:\n        image_dirs.append((dirname, len(image_files)))\n\nprint(\"Found image folders:\")\nfor folder, count in image_dirs:\n    print(folder, \":\", count, \"images\")\n\n# 画像が一番多いフォルダをデータセットフォルダとして使う\nDATASET_DIRECTORY = sorted(image_dirs, key=lambda x: x[1], reverse=True)[0][0]\n\nprint(\"\\nSelected dataset path:\")\nprint(DATASET_DIRECTORY)\n\nfile_list = os.listdir(DATASET_DIRECTORY)\nprint(\"\\nNumber of files:\", len(file_list))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:16.968184Z","iopub.execute_input":"2026-07-26T17:37:16.968486Z","iopub.status.idle":"2026-07-26T17:37:18.357116Z","shell.execute_reply.started":"2026-07-26T17:37:16.968463Z","shell.execute_reply":"2026-07-26T17:37:18.356278Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"image_paths = []\nimage_ids = []\n\nfor filename in os.listdir(DATASET_DIRECTORY):\n    if filename.lower().endswith((\".jpg\", \".jpeg\", \".png\")):\n        image_paths.append(os.path.join(DATASET_DIRECTORY, filename))\n        image_ids.append(os.path.splitext(filename)[0])\n\ndf = pd.DataFrame({\n    \"img\": image_ids,\n    \"filepath\": image_paths\n})\n\ndef sort_key(x):\n    try:\n        return int(x)\n    except:\n        return x\n\ndf[\"sort_id\"] = df[\"img\"].apply(sort_key)\ndf = df.sort_values(\"sort_id\").drop(columns=[\"sort_id\"]).reset_index(drop=True)\n\nprint(\"Total images:\", len(df))\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:18.359074Z","iopub.execute_input":"2026-07-26T17:37:18.359851Z","iopub.status.idle":"2026-07-26T17:37:18.408009Z","shell.execute_reply.started":"2026-07-26T17:37:18.359817Z","shell.execute_reply":"2026-07-26T17:37:18.407093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10, 6))\n\nsample_df = df.sample(min(9, len(df)), random_state=42)\n\nfor i, path in enumerate(sample_df[\"filepath\"]):\n    img = Image.open(path).convert(\"RGB\")\n    plt.subplot(3, 3, i + 1)\n    plt.imshow(img)\n    plt.axis(\"off\")\n    plt.title(sample_df.iloc[i][\"img\"])\n\nplt.tight_layout()\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:18.409079Z","iopub.execute_input":"2026-07-26T17:37:18.409384Z","iopub.status.idle":"2026-07-26T17:37:19.074349Z","shell.execute_reply.started":"2026-07-26T17:37:18.409353Z","shell.execute_reply":"2026-07-26T17:37:19.073398Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pseudo_model = EfficientNetB0(weights=\"imagenet\")\n\ndef create_pseudo_label(image_path):\n    img = Image.open(image_path).convert(\"RGB\")\n    img = img.resize((224, 224))\n    \n    arr = np.array(img)\n    arr = np.expand_dims(arr, axis=0)\n    arr = efficientnet_preprocess(arr)\n    \n    preds = pseudo_model.predict(arr, verbose=0)\n    decoded = decode_predictions(preds, top=5)[0]\n    \n    class_names = [item[1].lower() for item in decoded]\n    \n    dog_keywords = [\n        \"dog\", \"terrier\", \"retriever\", \"poodle\", \"shepherd\", \"hound\",\n        \"spaniel\", \"chihuahua\", \"beagle\", \"collie\", \"boxer\", \"husky\",\n        \"malamute\", \"doberman\", \"rottweiler\", \"dalmatian\"\n    ]\n    \n    cat_keywords = [\n        \"cat\", \"tabby\", \"tiger_cat\", \"persian_cat\", \"siamese_cat\",\n        \"egyptian_cat\", \"lynx\"\n    ]\n    \n    if any(keyword in name for name in class_names for keyword in dog_keywords):\n        return 1\n    \n    if any(keyword in name for name in class_names for keyword in cat_keywords):\n        return 0\n    \n    top_name = class_names[0]\n    \n    if \"cat\" in top_name:\n        return 0\n    else:\n        return 1\n\ndf[\"is_dog\"] = df[\"filepath\"].apply(create_pseudo_label)\n\ndf.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:37:19.075536Z","iopub.execute_input":"2026-07-26T17:37:19.075908Z","iopub.status.idle":"2026-07-26T17:50:50.981007Z","shell.execute_reply.started":"2026-07-26T17:37:19.07588Z","shell.execute_reply":"2026-07-26T17:50:50.980195Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df[\"label\"] = df[\"is_dog\"].map({\n    0: \"cat\",\n    1: \"dog\"\n})\n\nprint(df[\"label\"].value_counts())\n\nplt.figure(figsize=(5, 4))\ndf[\"label\"].value_counts().plot(kind=\"bar\")\nplt.title(\"Pseudo Label Distribution\")\nplt.xlabel(\"Class\")\nplt.ylabel(\"Number of Images\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:50:50.982216Z","iopub.execute_input":"2026-07-26T17:50:50.982608Z","iopub.status.idle":"2026-07-26T17:50:51.15181Z","shell.execute_reply.started":"2026-07-26T17:50:50.982584Z","shell.execute_reply":"2026-07-26T17:50:51.151187Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_df, val_df = train_test_split(\n    df,\n    test_size=0.2,\n    stratify=df[\"label\"],\n    random_state=42\n)\n\nprint(\"Training samples:\", len(train_df))\nprint(\"Validation samples:\", len(val_df))\n\nprint(\"\\nTraining label distribution:\")\nprint(train_df[\"label\"].value_counts())\n\nprint(\"\\nValidation label distribution:\")\nprint(val_df[\"label\"].value_counts())\n\ntrain_df.head()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:50:51.152702Z","iopub.execute_input":"2026-07-26T17:50:51.153618Z","iopub.status.idle":"2026-07-26T17:50:51.177149Z","shell.execute_reply.started":"2026-07-26T17:50:51.153578Z","shell.execute_reply":"2026-07-26T17:50:51.176506Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMAGE_SIZE = 160\nBATCH_SIZE = 32\n\ntrain_datagen = ImageDataGenerator(\n    preprocessing_function=mobilenet_preprocess,\n    rotation_range=15,\n    width_shift_range=0.10,\n    height_shift_range=0.10,\n    zoom_range=0.10,\n    horizontal_flip=True\n)\n\nval_datagen = ImageDataGenerator(\n    preprocessing_function=mobilenet_preprocess\n)\n\ntrain_generator = train_datagen.flow_from_dataframe(\n    dataframe=train_df,\n    x_col=\"filepath\",\n    y_col=\"label\",\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"binary\",\n    shuffle=True\n)\n\nval_generator = val_datagen.flow_from_dataframe(\n    dataframe=val_df,\n    x_col=\"filepath\",\n    y_col=\"label\",\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=\"binary\",\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:50:51.178127Z","iopub.execute_input":"2026-07-26T17:50:51.178491Z","iopub.status.idle":"2026-07-26T17:51:01.614299Z","shell.execute_reply.started":"2026-07-26T17:50:51.178468Z","shell.execute_reply":"2026-07-26T17:51:01.613425Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"base_model = MobileNetV2(\n    weights=\"imagenet\",\n    include_top=False,\n    input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)\n)\n\nbase_model.trainable = False\n\nmodel = Sequential([\n    base_model,\n    GlobalAveragePooling2D(),\n    Dropout(0.4),\n    Dense(64, activation=\"relu\"),\n    Dropout(0.3),\n    Dense(1, activation=\"sigmoid\")\n])\n\nmodel.compile(\n    optimizer=tf.keras.optimizers.Adam(learning_rate=0.001),\n    loss=\"binary_crossentropy\",\n    metrics=[\"accuracy\"]\n)\n\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:51:01.617238Z","iopub.execute_input":"2026-07-26T17:51:01.618121Z","iopub.status.idle":"2026-07-26T17:51:02.781874Z","shell.execute_reply.started":"2026-07-26T17:51:01.618086Z","shell.execute_reply":"2026-07-26T17:51:02.781267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks = [\n    EarlyStopping(\n        monitor=\"val_loss\",\n        patience=3,\n        restore_best_weights=True\n    ),\n    ReduceLROnPlateau(\n        monitor=\"val_loss\",\n        factor=0.5,\n        patience=2,\n        min_lr=1e-6\n    )\n]\n\nhistory = model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=15,\n    callbacks=callbacks\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:51:02.78288Z","iopub.execute_input":"2026-07-26T17:51:02.783518Z","iopub.status.idle":"2026-07-26T17:57:15.623377Z","shell.execute_reply.started":"2026-07-26T17:51:02.783496Z","shell.execute_reply":"2026-07-26T17:57:15.622648Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(6, 4))\nplt.plot(history.history[\"accuracy\"], label=\"Train Accuracy\")\nplt.plot(history.history[\"val_accuracy\"], label=\"Validation Accuracy\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Accuracy\")\nplt.title(\"Training and Validation Accuracy\")\nplt.legend()\nplt.show()\n\nplt.figure(figsize=(6, 4))\nplt.plot(history.history[\"loss\"], label=\"Train Loss\")\nplt.plot(history.history[\"val_loss\"], label=\"Validation Loss\")\nplt.xlabel(\"Epoch\")\nplt.ylabel(\"Loss\")\nplt.title(\"Training and Validation Loss\")\nplt.legend()\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:57:15.624473Z","iopub.execute_input":"2026-07-26T17:57:15.624872Z","iopub.status.idle":"2026-07-26T17:57:15.897069Z","shell.execute_reply.started":"2026-07-26T17:57:15.62485Z","shell.execute_reply":"2026-07-26T17:57:15.896313Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"val_loss, val_acc = model.evaluate(val_generator)\n\nprint(\"Validation Loss:\", val_loss)\nprint(\"Validation Accuracy:\", val_acc)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:57:15.898195Z","iopub.execute_input":"2026-07-26T17:57:15.898459Z","iopub.status.idle":"2026-07-26T17:57:23.30312Z","shell.execute_reply.started":"2026-07-26T17:57:15.898436Z","shell.execute_reply":"2026-07-26T17:57:23.302484Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_datagen = ImageDataGenerator(\n    preprocessing_function=mobilenet_preprocess\n)\n\ntest_generator = test_datagen.flow_from_dataframe(\n    dataframe=df,\n    x_col=\"filepath\",\n    y_col=None,\n    target_size=(IMAGE_SIZE, IMAGE_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode=None,\n    shuffle=False\n)\n\npred_prob = model.predict(test_generator)\npred_label = (pred_prob.reshape(-1) >= 0.5).astype(int)\n\ndf[\"pred_is_dog\"] = pred_label\n\ndf[[\"img\", \"filepath\", \"pred_is_dog\"]].head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:57:23.304112Z","iopub.execute_input":"2026-07-26T17:57:23.304443Z","iopub.status.idle":"2026-07-26T17:58:03.325349Z","shell.execute_reply.started":"2026-07-26T17:57:23.304421Z","shell.execute_reply":"2026-07-26T17:58:03.324633Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"submission = pd.DataFrame({\n    \"img\": df[\"img\"],\n    \"is_dog\": df[\"pred_is_dog\"]\n})\n\nsubmission.to_csv(\"submission.csv\", index=False)\n\nprint(\"submission.csv has been saved.\")\nsubmission.head()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:58:03.326397Z","iopub.execute_input":"2026-07-26T17:58:03.326755Z","iopub.status.idle":"2026-07-26T17:58:03.350634Z","shell.execute_reply.started":"2026-07-26T17:58:03.326731Z","shell.execute_reply":"2026-07-26T17:58:03.349993Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(submission[\"is_dog\"].value_counts())\n\nplt.figure(figsize=(5, 4))\nsubmission[\"is_dog\"].map({\n    0: \"cat\",\n    1: \"dog\"\n}).value_counts().plot(kind=\"bar\")\n\nplt.title(\"Final Prediction Distribution\")\nplt.xlabel(\"Predicted Class\")\nplt.ylabel(\"Number of Images\")\nplt.show()\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-26T17:58:03.351591Z","iopub.execute_input":"2026-07-26T17:58:03.351911Z","iopub.status.idle":"2026-07-26T17:58:03.462381Z","shell.execute_reply.started":"2026-07-26T17:58:03.351889Z","shell.execute_reply":"2026-07-26T17:58:03.461654Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## model.save(\"my_cat_dog_mobilenetv2_model.keras\")\n\nprint(\"Model saved as my_cat_dog_mobilenetv2_model.keras\")\n","metadata":{}}]}