{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","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":31089,"isInternetEnabled":true,"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-16T01:06:07.545803Z","iopub.execute_input":"2025-07-16T01:06:07.546051Z","iopub.status.idle":"2025-07-16T01:06:07.836365Z","shell.execute_reply.started":"2025-07-16T01:06:07.546023Z","shell.execute_reply":"2025-07-16T01:06:07.835687Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **データの準備**","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\nimport glob\nimport shutil\nimport random","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:07.837150Z","iopub.execute_input":"2025-07-16T01:06:07.837470Z","iopub.status.idle":"2025-07-16T01:06:07.841790Z","shell.execute_reply.started":"2025-07-16T01:06:07.837450Z","shell.execute_reply":"2025-07-16T01:06:07.841178Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#解凍先パス\nbase_dir = \"/kaggle/working\"\ntrain_zip_path = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip\"\ntest_zip_path = \"/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:07.843748Z","iopub.execute_input":"2025-07-16T01:06:07.843992Z","iopub.status.idle":"2025-07-16T01:06:07.861275Z","shell.execute_reply.started":"2025-07-16T01:06:07.843975Z","shell.execute_reply":"2025-07-16T01:06:07.860654Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#zipファイルの展開\nwith zipfile.ZipFile(train_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(os.path.join(base_dir, 'train_raw'))\n\nwith zipfile.ZipFile(test_zip_path, 'r') as zip_ref:\n    zip_ref.extractall(os.path.join(base_dir, 'test'))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:07.862076Z","iopub.execute_input":"2025-07-16T01:06:07.862274Z","iopub.status.idle":"2025-07-16T01:06:21.925309Z","shell.execute_reply.started":"2025-07-16T01:06:07.862258Z","shell.execute_reply":"2025-07-16T01:06:21.924769Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#ディレクトリ構成を作成\nfolders = [\n    'images/train/dog', 'images/train/cat',\n    'images/val/dog', 'images/val/cat',\n    'images/test/test'\n]\nfor folder in folders:\n    os.makedirs(os.path.join(base_dir, folder), exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:21.926020Z","iopub.execute_input":"2025-07-16T01:06:21.926214Z","iopub.status.idle":"2025-07-16T01:06:21.931079Z","shell.execute_reply.started":"2025-07-16T01:06:21.926198Z","shell.execute_reply":"2025-07-16T01:06:21.930451Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"#ファイルパス取得 & シャッフル\nall_dogs = glob.glob(os.path.join(base_dir, 'train_raw/train/dog.*.jpg'))\nall_cats = glob.glob(os.path.join(base_dir, 'train_raw/train/cat.*.jpg'))\n\nrandom.seed(42)\nrandom.shuffle(all_dogs)\nrandom.shuffle(all_cats)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:21.931640Z","iopub.execute_input":"2025-07-16T01:06:21.931842Z","iopub.status.idle":"2025-07-16T01:06:22.021248Z","shell.execute_reply.started":"2025-07-16T01:06:21.931818Z","shell.execute_reply":"2025-07-16T01:06:22.020737Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 4. train : val = 8 : 2 に分割\nsplit_idx_dog = int(0.8 * len(all_dogs))\nsplit_idx_cat = int(0.8 * len(all_cats))\n\ntrain_dogs = all_dogs[:split_idx_dog]\nval_dogs = all_dogs[split_idx_dog:]\n\ntrain_cats = all_cats[:split_idx_cat]\nval_cats = all_cats[split_idx_cat:]","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:22.021786Z","iopub.execute_input":"2025-07-16T01:06:22.021982Z","iopub.status.idle":"2025-07-16T01:06:22.025889Z","shell.execute_reply.started":"2025-07-16T01:06:22.021966Z","shell.execute_reply":"2025-07-16T01:06:22.025199Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 画像をフォルダに移動\ndef move_images(file_list, target_dir):\n    for file in file_list:\n        shutil.copy(file, os.path.join(base_dir, target_dir))\n\n# train\nmove_images(train_dogs, 'images/train/dog')\nmove_images(train_cats, 'images/train/cat')\n\n# val\nmove_images(val_dogs, 'images/val/dog')\nmove_images(val_cats, 'images/val/cat')\n\n# test\ntest_images = glob.glob(os.path.join(base_dir, 'test/test/*.jpg'))\nmove_images(test_images, 'images/test/test')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:22.026647Z","iopub.execute_input":"2025-07-16T01:06:22.026946Z","iopub.status.idle":"2025-07-16T01:06:26.299943Z","shell.execute_reply.started":"2025-07-16T01:06:22.026919Z","shell.execute_reply":"2025-07-16T01:06:26.299171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ファイル一覧を取得 (犬と猫の画像をそれぞれ取得し、結合)\ntrain_dog_images = glob.glob('/kaggle/working/images/train/dog/*.jpg')\ntrain_cat_images = glob.glob('/kaggle/working/images/train/cat/*.jpg')\nimages = train_dog_images + train_cat_images # 両方のリストを結合\n\n# ファイル名を確認(先頭3枚)\nprint(images[:3])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:26.302631Z","iopub.execute_input":"2025-07-16T01:06:26.302847Z","iopub.status.idle":"2025-07-16T01:06:26.344339Z","shell.execute_reply.started":"2025-07-16T01:06:26.302828Z","shell.execute_reply":"2025-07-16T01:06:26.343704Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 画像表示に必要なモジュールをインポート\nimport matplotlib.pyplot as plt\nfrom PIL import Image\n\n# 画像の表示\nif images: # リストが空でないことを確認\n    image_sample = images[0]\n    img = Image.open(image_sample)\n    print('filename:{}, size:{}'.format(image_sample, img.size))\n    plt.imshow(img)\n    plt.show()\nelse:\n    print(\"画像ファイルが見つかりませんでした。\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:26.345276Z","iopub.execute_input":"2025-07-16T01:06:26.345528Z","iopub.status.idle":"2025-07-16T01:06:26.722760Z","shell.execute_reply.started":"2025-07-16T01:06:26.345504Z","shell.execute_reply":"2025-07-16T01:06:26.722178Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **ImageDataGenerator**","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.preprocessing.image import ImageDataGenerator","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:26.723318Z","iopub.execute_input":"2025-07-16T01:06:26.723536Z","iopub.status.idle":"2025-07-16T01:06:47.189517Z","shell.execute_reply.started":"2025-07-16T01:06:26.723517Z","shell.execute_reply":"2025-07-16T01:06:47.188908Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 共通パラメータ\nimg_width, img_height = 224, 224\nbatch_size = 32\nclasses = ['cat', 'dog']","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.190331Z","iopub.execute_input":"2025-07-16T01:06:47.190899Z","iopub.status.idle":"2025-07-16T01:06:47.194849Z","shell.execute_reply.started":"2025-07-16T01:06:47.190852Z","shell.execute_reply":"2025-07-16T01:06:47.194219Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 学習用：データ拡張あり + 正規化\ntrain_datagen = ImageDataGenerator(\n    rescale=1.0 / 255,\n    rotation_range=20,\n    #width_shift_range=0.2,\n    #height_shift_range=0.2,\n    #shear_range=0.2,\n    #zoom_range=0.2,\n    horizontal_flip=True\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.195671Z","iopub.execute_input":"2025-07-16T01:06:47.195962Z","iopub.status.idle":"2025-07-16T01:06:47.214966Z","shell.execute_reply.started":"2025-07-16T01:06:47.195936Z","shell.execute_reply":"2025-07-16T01:06:47.214454Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 検証用：正規化のみ\nval_datagen = ImageDataGenerator(rescale=1.0 / 255)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.215580Z","iopub.execute_input":"2025-07-16T01:06:47.215840Z","iopub.status.idle":"2025-07-16T01:06:47.232708Z","shell.execute_reply.started":"2025-07-16T01:06:47.215818Z","shell.execute_reply":"2025-07-16T01:06:47.232171Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# テスト用：正規化のみ（ラベルなし、shuffle=False）\ntest_datagen = ImageDataGenerator(rescale=1.0 / 255)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.233421Z","iopub.execute_input":"2025-07-16T01:06:47.233614Z","iopub.status.idle":"2025-07-16T01:06:47.255648Z","shell.execute_reply.started":"2025-07-16T01:06:47.233600Z","shell.execute_reply":"2025-07-16T01:06:47.255166Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ジェネレーターの作成\ntrain_generator = train_datagen.flow_from_directory(\n    directory='/kaggle/working/images/train',\n    target_size=(img_width, img_height),\n    batch_size=batch_size,\n    class_mode='categorical',\n    shuffle=True,\n    classes=classes\n)\n\nval_generator = val_datagen.flow_from_directory(\n    directory='/kaggle/working/images/val',\n    target_size=(img_width, img_height),\n    batch_size=batch_size,\n    class_mode='categorical',\n    shuffle=False,\n    classes=classes\n)\n\ntest_generator = test_datagen.flow_from_directory(\n    directory='/kaggle/working/images/test',\n    target_size=(img_width, img_height),\n    batch_size=batch_size,\n    class_mode=None,\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.256344Z","iopub.execute_input":"2025-07-16T01:06:47.256590Z","iopub.status.idle":"2025-07-16T01:06:47.620068Z","shell.execute_reply.started":"2025-07-16T01:06:47.256570Z","shell.execute_reply":"2025-07-16T01:06:47.619408Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **モデル構築（ResNet50）＋動作確認**","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import ResNet50\nfrom tensorflow.keras.models import Model\nfrom tensorflow.keras.layers import Dense, GlobalAveragePooling2D, Input\nfrom tensorflow.keras.optimizers import Adam","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.620805Z","iopub.execute_input":"2025-07-16T01:06:47.621030Z","iopub.status.idle":"2025-07-16T01:06:47.639919Z","shell.execute_reply.started":"2025-07-16T01:06:47.621012Z","shell.execute_reply":"2025-07-16T01:06:47.639271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 入力サイズ（224x224x3）\ninput_tensor = Input(shape=(224, 224, 3))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.640618Z","iopub.execute_input":"2025-07-16T01:06:47.640796Z","iopub.status.idle":"2025-07-16T01:06:47.712733Z","shell.execute_reply.started":"2025-07-16T01:06:47.640771Z","shell.execute_reply":"2025-07-16T01:06:47.712137Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ResNet50のベースモデル（全結合層は除く）\nbase_model = ResNet50(\n    include_top=False,          # 出力層は含めない\n    weights='imagenet',         # ImageNetの学習済み重みを使用\n    input_tensor=input_tensor\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:47.713390Z","iopub.execute_input":"2025-07-16T01:06:47.713571Z","iopub.status.idle":"2025-07-16T01:06:53.123353Z","shell.execute_reply.started":"2025-07-16T01:06:47.713556Z","shell.execute_reply":"2025-07-16T01:06:53.122733Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# パラメータを全て固定（学習させない）\nfor layer in base_model.layers:\n    layer.trainable = True","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.123989Z","iopub.execute_input":"2025-07-16T01:06:53.124170Z","iopub.status.idle":"2025-07-16T01:06:53.128699Z","shell.execute_reply.started":"2025-07-16T01:06:53.124156Z","shell.execute_reply":"2025-07-16T01:06:53.128023Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 転移学習用に新しい層を追加\nfrom tensorflow.keras.layers import Dropout\nx = base_model.output\nx = GlobalAveragePooling2D()(x)\nx = Dense(256, activation='relu')(x)  # 中間層\nx = Dropout(0.5)(x)\noutput = Dense(2, activation='softmax')(x)  # 最終出力（2クラス）","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.129347Z","iopub.execute_input":"2025-07-16T01:06:53.129540Z","iopub.status.idle":"2025-07-16T01:06:53.168699Z","shell.execute_reply.started":"2025-07-16T01:06:53.129526Z","shell.execute_reply":"2025-07-16T01:06:53.168098Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# モデルの構築\nmodel = Model(inputs=base_model.input, outputs=output)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.169649Z","iopub.execute_input":"2025-07-16T01:06:53.170035Z","iopub.status.idle":"2025-07-16T01:06:53.180964Z","shell.execute_reply.started":"2025-07-16T01:06:53.170016Z","shell.execute_reply":"2025-07-16T01:06:53.180326Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# モデルのコンパイル\nmodel.compile(\n    optimizer=Adam(learning_rate=1e-6),\n    loss='categorical_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.181645Z","iopub.execute_input":"2025-07-16T01:06:53.182344Z","iopub.status.idle":"2025-07-16T01:06:53.209575Z","shell.execute_reply.started":"2025-07-16T01:06:53.182322Z","shell.execute_reply":"2025-07-16T01:06:53.209037Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# **モデル学習**","metadata":{}},{"cell_type":"code","source":"# エポック数とステップ数の設定\nepochs = 20\nsteps_per_epoch = train_generator.samples // train_generator.batch_size\nvalidation_steps = val_generator.samples // val_generator.batch_size","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.210536Z","iopub.execute_input":"2025-07-16T01:06:53.210764Z","iopub.status.idle":"2025-07-16T01:06:53.216200Z","shell.execute_reply.started":"2025-07-16T01:06:53.210738Z","shell.execute_reply":"2025-07-16T01:06:53.215659Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# モデルの学習\nhistory = model.fit(\n    train_generator,\n    steps_per_epoch=steps_per_epoch,\n    epochs=epochs,\n    validation_data=val_generator,\n    validation_steps=validation_steps\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T01:06:53.216775Z","iopub.execute_input":"2025-07-16T01:06:53.216962Z","iopub.status.idle":"2025-07-16T02:26:40.228448Z","shell.execute_reply.started":"2025-07-16T01:06:53.216948Z","shell.execute_reply":"2025-07-16T02:26:40.227840Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# テスト用 ImageDataGenerator（ラベルなしの推論用）\ntest_datagen = ImageDataGenerator(rescale=1./255)\n\ntest_generator = test_datagen.flow_from_directory(\n    directory=os.path.join(base_dir, 'images/test'),\n    target_size=(224, 224),\n    batch_size=32,\n    class_mode=None,\n    shuffle=False  # ファイル名順に固定\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T02:26:40.229480Z","iopub.execute_input":"2025-07-16T02:26:40.229732Z","iopub.status.idle":"2025-07-16T02:26:40.328137Z","shell.execute_reply.started":"2025-07-16T02:26:40.229716Z","shell.execute_reply":"2025-07-16T02:26:40.327591Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 推論\npredictions = model.predict(test_generator, verbose=1)\n\n# 犬 = 1, 猫 = 0 として扱う前提で、犬の確率を抽出\ndog_probs = predictions[:, 1]  # Softmaxの出力2列目（犬の確率）\n\n# ファイル名からidを抽出\nimport os\nimage_ids = [int(os.path.basename(fname).split('.')[0]) for fname in test_generator.filenames]\n\n# submission DataFrame\nsubmission_df = pd.DataFrame({\n    'id': image_ids,\n    'label': dog_probs\n})\n\n# id順にソート（Kaggleの仕様に合わせる）\nsubmission_df = submission_df.sort_values('id')\n\n# CSVとして保存\nsubmission_df.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T02:26:40.330725Z","iopub.execute_input":"2025-07-16T02:26:40.330940Z","iopub.status.idle":"2025-07-16T02:27:21.884094Z","shell.execute_reply.started":"2025-07-16T02:26:40.330924Z","shell.execute_reply":"2025-07-16T02:27:21.883349Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 保存されたファイル一覧を表示して確認\n!ls -lh submission.csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-16T02:27:21.884805Z","iopub.execute_input":"2025-07-16T02:27:21.885089Z","iopub.status.idle":"2025-07-16T02:27:22.105486Z","shell.execute_reply.started":"2025-07-16T02:27:21.885064Z","shell.execute_reply":"2025-07-16T02:27:22.104784Z"}},"outputs":[],"execution_count":null}]}