{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31040,"isInternetEnabled":false,"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","trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:46:18.330268Z","iopub.execute_input":"2025-07-25T11:46:18.330551Z","iopub.status.idle":"2025-07-25T11:46:20.833829Z","shell.execute_reply.started":"2025-07-25T11:46:18.330526Z","shell.execute_reply":"2025-07-25T11:46:20.833119Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 環境準備と必要なライブラリのインポート\n\nimport os\nimport zipfile\nimport shutil\nimport random\nimport tensorflow as tf\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.layers import Input, Conv2D, MaxPooling2D, Flatten, Dense, Dropout\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.optimizers import Adam\nimport numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\n\n# GPUの有無確認\nprint(\"Num GPUs Available:\", len(tf.config.list_physical_devices('GPU')))\n\nprint(\"--- 0. 環境準備とライブラリのインポートが完了しました ---\")\n ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:46:20.834736Z","iopub.execute_input":"2025-07-25T11:46:20.835351Z","iopub.status.idle":"2025-07-25T11:46:41.659795Z","shell.execute_reply.started":"2025-07-25T11:46:20.835329Z","shell.execute_reply":"2025-07-25T11:46:41.658926Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"データの解凍と整理を開始\")\n\n# Kaggleデータセットのベースパス\nbase_data_path = '/kaggle/input/dogs-vs-cats-redux-kernels-edition'\n\n# 解凍先および作業ディレクトリの設定\n# /kaggle/working はノートブックが作業できる永続的なストレージ\nworking_dir = '/kaggle/working/dogs-vs-cats-data'\nif not os.path.exists(working_dir):\n    os.makedirs(working_dir)\n\n# 訓練データのZIPファイルパス\ntrain_zip_path = os.path.join(base_data_path, 'train.zip')\n# テストデータのZIPファイルパス\ntest_zip_path = os.path.join(base_data_path, 'test.zip')\n\n\n# 訓練データの解凍\nprint(f\"'{os.path.basename(train_zip_path)}' を解凍中...\")\nwith zipfile.ZipFile(train_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(working_dir)\nprint(\"訓練データの解凍が完了しました。\")\n\n# テストデータの解凍 (予測フェーズで必要)\nprint(f\"'{os.path.basename(test_zip_path)}' を解凍中...\")\nwith zipfile.ZipFile(test_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(working_dir)\nprint(\"テストデータの解凍が完了しました。\")\n\n# 解凍された元の訓練画像があるディレクトリ\noriginal_train_dir = os.path.join(working_dir, 'train')\noriginal_test_dir = os.path.join(working_dir, 'test')\n\n# 訓練/検証データ分割用の新しいディレクトリ構造を作成\n# structure: split_data/train/cats, split_data/train/dogs, split_data/val/cats, split_data/val/dogs\nsplit_data_dir = os.path.join(working_dir, 'split_data')\n\ntrain_cats_dir = os.path.join(split_data_dir, 'train/cats')\ntrain_dogs_dir = os.path.join(split_data_dir, 'train/dogs')\nval_cats_dir = os.path.join(split_data_dir, 'val/cats')\nval_dogs_dir = os.path.join(split_data_dir, 'val/dogs')\n\n# 必要なディレクトリを全て作成\nfor path in [train_cats_dir, train_dogs_dir, val_cats_dir, val_dogs_dir]:\n    os.makedirs(path, exist_ok=True)\n\n# 画像を犬と猫に分類し、訓練セットと検証セットに分割して移動\nall_fnames = os.listdir(original_train_dir)\nrandom.shuffle(all_fnames) # ランダムにシャッフルして分割を公平にする\n\ntrain_split_ratio = 0.9 # 90%を訓練、10%を検証\n\nnum_train_dogs = 0\nnum_train_cats = 0\nnum_val_dogs = 0\nnum_val_cats = 0\n\nfor i, fname in enumerate(all_fnames):\n    src_path = os.path.join(original_train_dir, fname)\n    if 'cat' in fname:\n        if i < len(all_fnames) * train_split_ratio:\n            dst_path = os.path.join(train_cats_dir, fname)\n            num_train_cats += 1\n        else:\n            dst_path = os.path.join(val_cats_dir, fname)\n            num_val_cats += 1\n    elif 'dog' in fname:\n        if i < len(all_fnames) * train_split_ratio:\n            dst_path = os.path.join(train_dogs_dir, fname)\n            num_train_dogs += 1\n        else:\n            dst_path = os.path.join(val_dogs_dir, fname)\n            num_val_dogs += 1\n    \n    shutil.copyfile(src_path, dst_path)\n\nprint(f\"訓練セット: 犬 {num_train_dogs}枚, 猫 {num_train_cats}枚\")\nprint(f\"検証セット: 犬 {num_val_dogs}枚, 猫 {num_val_cats}枚\")\nprint(\"--- 1. データの解凍と整理が完了しました ---\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:46:41.661785Z","iopub.execute_input":"2025-07-25T11:46:41.662254Z","iopub.status.idle":"2025-07-25T11:46:58.035630Z","shell.execute_reply.started":"2025-07-25T11:46:41.662234Z","shell.execute_reply":"2025-07-25T11:46:58.034976Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"データ拡張と画像データのジェネレータ作成を開始します\")\n\n# 画像のリサイズサイズとバッチサイズを設定\nIMAGE_WIDTH, IMAGE_HEIGHT = 150, 150 # モデルの入力サイズ\nBATCH_SIZE = 32\n\n# 訓練データ用ImageDataGenerator: データ拡張と正規化\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,          # ピクセル値を0-1に正規化\n    rotation_range=40,       # ランダムな回転 (0-40度)\n    width_shift_range=0.2,   # 水平方向のシフト (画像の幅の20%以内)\n    height_shift_range=0.2,  # 垂直方向のシフト (画像の高さの20%以内)\n    shear_range=0.2,         # シアー変換\n    zoom_range=0.2,          # ランダムなズーム\n    horizontal_flip=True,    # 水平方向の反転\n    fill_mode='nearest'      # 新しく生成されたピクセルの埋め方\n)\n\n# 検証データ用ImageDataGenerator: 正規化のみ (データ拡張は適用しない)\nvalidation_datagen = ImageDataGenerator(rescale=1./255)\n\n# テストデータ用ImageDataGenerator: 正規化のみ\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\n# 訓練データジェネレータの作成\ntrain_generator = train_datagen.flow_from_directory(\n    os.path.join(split_data_dir, 'train'), # 訓練データディレクトリ\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=BATCH_SIZE,\n    class_mode='binary' # 犬/猫の二値分類のため\n)\n\n# 検証データジェネレータの作成\nvalidation_generator = validation_datagen.flow_from_directory(\n    os.path.join(split_data_dir, 'val'), # 検証データディレクトリ\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=BATCH_SIZE,\n    class_mode='binary'\n)\n\n# テストデータジェネレータの作成\n# テストデータはラベルを持たないため、class_mode=None、予測順序を保つためshuffle=False\ntest_generator = test_datagen.flow_from_directory(\n    original_test_dir, # 解凍したテストデータディレクトリ\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=1, # 1枚ずつ予測するため\n    class_mode=None, # ラベルがないため\n    shuffle=False # 予測順序を保持するため\n)\n\nprint(\"データ拡張と画像データのジェネレータ作成が完了しました\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:46:58.036318Z","iopub.execute_input":"2025-07-25T11:46:58.036560Z","iopub.status.idle":"2025-07-25T11:46:58.325295Z","shell.execute_reply.started":"2025-07-25T11:46:58.036542Z","shell.execute_reply":"2025-07-25T11:46:58.324710Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\" モデルの構築を開始します\")\n\nmodel = Sequential([\n    Input(shape=(IMAGE_WIDTH, IMAGE_HEIGHT, 3)), # Input層を明示的に定義\n    # 第1畳み込みブロック\n    Conv2D(32, (3, 3), activation='relu'),\n    MaxPooling2D((2, 2)),\n    # 第2畳み込みブロック\n    Conv2D(64, (3, 3), activation='relu'),\n    MaxPooling2D((2, 2)),\n    # 第3畳み込みブロック\n    Conv2D(128, (3, 3), activation='relu'),\n    MaxPooling2D((2, 2)),\n    # 第4畳み込みブロック\n    Conv2D(128, (3, 3), activation='relu'),\n    MaxPooling2D((2, 2)),\n    # 特徴マップを平坦化\n    Flatten(),\n    # 過学習を防ぐためのドロップアウト層\n    Dropout(0.5), # 50%のニューロンをランダムに無効化\n    # 全結合層\n    Dense(512, activation='relu'),\n    # 出力層: 二値分類のためsigmoid活性化関数\n    Dense(1, activation='sigmoid') # 犬である確率 (0-1) を出力\n])\n\n# モデルのコンパイル: オプティマイザ、損失関数、評価指標を設定\n# 学習率を小さめに設定し、安定した学習を目指す\nmodel.compile(optimizer=Adam(learning_rate=1e-4),\n              loss='binary_crossentropy',       # 二値分類の標準的な損失関数\n              metrics=['accuracy'])             # 評価指標として精度を使用\n\n# モデルの概要を表示\nmodel.summary()\n\nprint(\"モデルの構築が完了しました\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:46:58.325930Z","iopub.execute_input":"2025-07-25T11:46:58.326158Z","iopub.status.idle":"2025-07-25T11:47:00.443797Z","shell.execute_reply.started":"2025-07-25T11:46:58.326132Z","shell.execute_reply":"2025-07-25T11:47:00.443267Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"--- 4. モデルの訓練を開始します ---\")\n\n# 訓練エポック数を設定\nEPOCHS = 50 # 適切な値は試行錯誤により決定。GPU環境ならもう少し多く試せる\n\n# モデルの訓練を実行\nhistory = model.fit(\n    train_generator,\n    steps_per_epoch=train_generator.samples // BATCH_SIZE, # 1エポックあたりのステップ数\n    epochs=EPOCHS,\n    validation_data=validation_generator,\n    validation_steps=validation_generator.samples // BATCH_SIZE # 検証データでのステップ数\n)\n\nprint(\"--- 4. モデルの訓練が完了しました ---\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:47:00.444495Z","iopub.execute_input":"2025-07-25T11:47:00.444701Z","iopub.status.idle":"2025-07-25T12:41:32.745988Z","shell.execute_reply.started":"2025-07-25T11:47:00.444685Z","shell.execute_reply":"2025-07-25T12:41:32.745420Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\"---テスト画像の準備と予測、提出ファイルの生成を開始\")\n\n# テストデータのZIPファイルパス\ntest_zip_path = os.path.join(base_data_path, 'test.zip')\n\n# テストデータ解凍先のディレクトリを設定\ntest_extract_dir = '/kaggle/working/test_images_for_prediction'\nif os.path.exists(test_extract_dir):\n    shutil.rmtree(test_extract_dir) # 既存のディレクトリがあれば削除\nos.makedirs(test_extract_dir)\n\n# テストデータの解凍\nprint(f\"'{os.path.basename(test_zip_path)}' を '{test_extract_dir}' に解凍中...\")\nwith zipfile.ZipFile(test_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(test_extract_dir) # test_images_for_prediction/test/ の中に画像がある状態\nprint(\"テストデータの解凍が完了しました。\")\n\n# ImageDataGenerator for test data (正規化のみ)\ntest_datagen = ImageDataGenerator(rescale=1.0/255)\n\n# テストデータジェネレータの作成\ntest_generator = test_datagen.flow_from_directory(\n    directory=test_extract_dir, # 親ディレクトリを指定\n    classes=['test'],           # このサブディレクトリを読み込む\n    target_size=(IMAGE_WIDTH, IMAGE_HEIGHT),\n    batch_size=1,               # 1枚ずつ予測するため\n    class_mode=None,            # ラベルがないため\n    shuffle=False               # 予測順序を保持するため\n)\n\nprint(f\"Test Generator found {test_generator.samples} images.\")\nif test_generator.samples == 0:\n    print(\"CRITICAL ERROR: Test generator found 0 images. Please check the 'test_extract_dir' path and its contents.\")\n    print(\"Expected structure: test_images_for_prediction/test/1.jpg, 2.jpg, ...\")\n\n\n# モデルによる推論 (予測)\nprint(\"テスト画像に対する予測を開始\")\n# len(test_generator) は test_generator.samples // batch_size と同等 (batch_size=1なので samples と同じ)\npred = model.predict(test_generator, steps=len(test_generator), verbose=1)\nprint(\"予測が完了\")\n\n# 提出ファイルの作成\nprint(\"提出ファイルを生成中\")\n# ファイル名からIDを取得\nfilenames = test_generator.filenames # 例: ['test/1.jpg', 'test/10.jpg', ...]\nids = [int(os.path.basename(name).split('.')[0]) for name in filenames]\n\nlabels = pred.flatten() # 確率をそのまま提出\n\nsubmission = pd.DataFrame({'id': ids, 'label': labels})\n\n# id順に並べる\nsubmission = submission.sort_values('id')\nsubmission.to_csv('/kaggle/working/submission.csv', index=False)\n\nprint(f\"提出ファイル '/kaggle/working/submission.csv' が作成\")\n\nprint(\"---テスト画像の準備と予測、提出ファイルの生成が完了 ---\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T12:41:32.746810Z","iopub.execute_input":"2025-07-25T12:41:32.747039Z","iopub.status.idle":"2025-07-25T12:42:12.389282Z","shell.execute_reply.started":"2025-07-25T12:41:32.747022Z","shell.execute_reply":"2025-07-25T12:42:12.388622Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(\" 学習曲線の描画を開始\")\n\n# 精度 (Accuracy) の学習曲線\nplt.figure(figsize=(10, 5))\nplt.plot(history.history['accuracy'], label='Train Accuracy')\nplt.plot(history.history['val_accuracy'], label='Validation Accuracy')\nplt.legend()\nplt.title('Model Accuracy')\nplt.xlabel('Epoch')\nplt.ylabel('Accuracy')\nplt.grid(True)\nplt.show()\n\n# 損失 (Loss) の学習曲線\nplt.figure(figsize=(10, 5))\nplt.plot(history.history['loss'], label='Train Loss')\nplt.plot(history.history['val_loss'], label='Validation Loss')\nplt.legend()\nplt.title('Model Loss')\nplt.xlabel('Epoch')\nplt.ylabel('Loss')\nplt.grid(True)\nplt.show()\n\nprint(\"---学習曲線の描画が完了 ---\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T12:42:12.389994Z","iopub.execute_input":"2025-07-25T12:42:12.390286Z","iopub.status.idle":"2025-07-25T12:42:12.867439Z","shell.execute_reply.started":"2025-07-25T12:42:12.390247Z","shell.execute_reply":"2025-07-25T12:42:12.866711Z"}},"outputs":[],"execution_count":null}]}