{"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-04T08:50:59.954460Z","iopub.execute_input":"2026-07-04T08:50:59.954765Z","iopub.status.idle":"2026-07-04T08:51:18.041597Z","shell.execute_reply.started":"2026-07-04T08:50:59.954736Z","shell.execute_reply":"2026-07-04T08:51:18.040417Z"},"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Cat vs Dog Image Classification\n\n# このノートブックでは、猫と犬の画像を分類する画像分類タスクを行う。  \n# 元のKaggleノートブックを参考にしながら、自分の工夫として、データ確認、疑似ラベルの作成、MobileNetV2を用いた転移学習、データ拡張、学習曲線の可視化、検証精度の確認、submission.csvの作成を追加した。\n\n# 今回の目的は、画像分類モデルの基本的な流れを理解し、学習済みモデルを利用して猫と犬を判別できるモデルを作成することである。","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T08:51:49.320970Z","iopub.execute_input":"2026-07-04T08:51:49.321603Z","iopub.status.idle":"2026-07-04T08:51:49.326462Z","shell.execute_reply.started":"2026-07-04T08:51:49.321559Z","shell.execute_reply":"2026-07-04T08:51:49.325145Z"},"editable":false},"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-04T09:18:05.645712Z","iopub.execute_input":"2026-07-04T09:18:05.646554Z","iopub.status.idle":"2026-07-04T09:18:25.434180Z","shell.execute_reply.started":"2026-07-04T09:18:05.646517Z","shell.execute_reply":"2026-07-04T09:18:25.433227Z"},"editable":false},"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-04T09:18:51.561624Z","iopub.execute_input":"2026-07-04T09:18:51.562284Z","iopub.status.idle":"2026-07-04T09:19:01.451154Z","shell.execute_reply.started":"2026-07-04T09:18:51.562249Z","shell.execute_reply":"2026-07-04T09:19:01.450077Z"},"editable":false},"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-04T09:19:01.453532Z","iopub.execute_input":"2026-07-04T09:19:01.453830Z","iopub.status.idle":"2026-07-04T09:19:01.503225Z","shell.execute_reply.started":"2026-07-04T09:19:01.453804Z","shell.execute_reply":"2026-07-04T09:19:01.501941Z"},"editable":false},"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-04T09:19:01.504410Z","iopub.execute_input":"2026-07-04T09:19:01.504668Z","iopub.status.idle":"2026-07-04T09:19:02.362527Z","shell.execute_reply.started":"2026-07-04T09:19:01.504645Z","shell.execute_reply":"2026-07-04T09:19:02.361280Z"},"editable":false},"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-04T09:39:34.010065Z","iopub.execute_input":"2026-07-04T09:39:34.012499Z","iopub.status.idle":"2026-07-04T10:00:30.046573Z","shell.execute_reply.started":"2026-07-04T09:39:34.012428Z","shell.execute_reply":"2026-07-04T10:00:30.045367Z"},"editable":false},"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:00:30.048412Z","iopub.execute_input":"2026-07-04T10:00:30.048761Z","iopub.status.idle":"2026-07-04T10:00:30.266839Z","shell.execute_reply.started":"2026-07-04T10:00:30.048730Z","shell.execute_reply":"2026-07-04T10:00:30.265913Z"},"editable":false},"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-04T10:00:40.426145Z","iopub.execute_input":"2026-07-04T10:00:40.426822Z","iopub.status.idle":"2026-07-04T10:00:40.454097Z","shell.execute_reply.started":"2026-07-04T10:00:40.426785Z","shell.execute_reply":"2026-07-04T10:00:40.452851Z"},"editable":false},"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-04T10:00:40.994003Z","iopub.execute_input":"2026-07-04T10:00:40.994373Z","iopub.status.idle":"2026-07-04T10:00:55.900389Z","shell.execute_reply.started":"2026-07-04T10:00:40.994345Z","shell.execute_reply":"2026-07-04T10:00:55.899214Z"},"editable":false},"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-04T10:00:55.902153Z","iopub.execute_input":"2026-07-04T10:00:55.902510Z","iopub.status.idle":"2026-07-04T10:00:57.278856Z","shell.execute_reply.started":"2026-07-04T10:00:55.902479Z","shell.execute_reply":"2026-07-04T10:00:57.278024Z"},"editable":false},"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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:01:04.942506Z","iopub.execute_input":"2026-07-04T10:01:04.942929Z","iopub.status.idle":"2026-07-04T10:21:10.315904Z","shell.execute_reply.started":"2026-07-04T10:01:04.942896Z","shell.execute_reply":"2026-07-04T10:21:10.314534Z"},"editable":false},"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:21:10.318208Z","iopub.execute_input":"2026-07-04T10:21:10.318550Z","iopub.status.idle":"2026-07-04T10:21:10.655843Z","shell.execute_reply.started":"2026-07-04T10:21:10.318520Z","shell.execute_reply":"2026-07-04T10:21:10.654661Z"},"editable":false},"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)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:21:10.657508Z","iopub.execute_input":"2026-07-04T10:21:10.657944Z","iopub.status.idle":"2026-07-04T10:21:29.955661Z","shell.execute_reply.started":"2026-07-04T10:21:10.657900Z","shell.execute_reply":"2026-07-04T10:21:29.954567Z"},"editable":false},"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:21:29.957836Z","iopub.execute_input":"2026-07-04T10:21:29.958217Z","iopub.status.idle":"2026-07-04T10:23:20.702906Z","shell.execute_reply.started":"2026-07-04T10:21:29.958188Z","shell.execute_reply":"2026-07-04T10:23:20.701973Z"},"editable":false},"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:23:20.704139Z","iopub.execute_input":"2026-07-04T10:23:20.704477Z","iopub.status.idle":"2026-07-04T10:23:20.735410Z","shell.execute_reply.started":"2026-07-04T10:23:20.704448Z","shell.execute_reply":"2026-07-04T10:23:20.734073Z"},"editable":false},"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()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:23:20.736745Z","iopub.execute_input":"2026-07-04T10:23:20.737363Z","iopub.status.idle":"2026-07-04T10:23:20.881346Z","shell.execute_reply.started":"2026-07-04T10:23:20.737331Z","shell.execute_reply":"2026-07-04T10:23:20.879923Z"},"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.save(\"my_cat_dog_mobilenetv2_model.keras\")\n\nprint(\"Model saved as my_cat_dog_mobilenetv2_model.keras\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-07-04T10:23:20.883424Z","iopub.execute_input":"2026-07-04T10:23:20.883744Z","iopub.status.idle":"2026-07-04T10:23:21.504963Z","shell.execute_reply.started":"2026-07-04T10:23:20.883711Z","shell.execute_reply":"2026-07-04T10:23:21.504084Z"},"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true,"editable":false},"outputs":[],"execution_count":null}]}