{"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":"gpu","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31090,"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-11T10:52:17.657754Z","iopub.execute_input":"2025-07-11T10:52:17.658390Z","iopub.status.idle":"2025-07-11T10:52:17.938632Z","shell.execute_reply.started":"2025-07-11T10:52:17.658367Z","shell.execute_reply":"2025-07-11T10:52:17.937829Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# データの順備\ntrain.zip と test.zip を展開して、画像を抽出。","metadata":{}},{"cell_type":"code","source":"import os\nimport zipfile\nimport glob\nimport shutil\nimport random","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T10:52:17.940052Z","iopub.execute_input":"2025-07-11T10:52:17.940850Z","iopub.status.idle":"2025-07-11T10:52:17.944117Z","shell.execute_reply.started":"2025-07-11T10:52:17.940828Z","shell.execute_reply":"2025-07-11T10:52:17.943441Z"}},"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-11T10:52:17.945070Z","iopub.execute_input":"2025-07-11T10:52:17.945470Z","iopub.status.idle":"2025-07-11T10:52:17.961316Z","shell.execute_reply.started":"2025-07-11T10:52:17.945394Z","shell.execute_reply":"2025-07-11T10:52:17.960600Z"}},"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-11T10:52:17.963003Z","iopub.execute_input":"2025-07-11T10:52:17.963267Z","iopub.status.idle":"2025-07-11T10:52:33.526540Z","shell.execute_reply.started":"2025-07-11T10:52:17.963248Z","shell.execute_reply":"2025-07-11T10:52:33.525770Z"}},"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-11T10:52:33.527413Z","iopub.execute_input":"2025-07-11T10:52:33.527701Z","iopub.status.idle":"2025-07-11T10:52:33.532927Z","shell.execute_reply.started":"2025-07-11T10:52:33.527672Z","shell.execute_reply":"2025-07-11T10:52:33.532226Z"}},"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-11T10:52:33.533770Z","iopub.execute_input":"2025-07-11T10:52:33.534003Z","iopub.status.idle":"2025-07-11T10:52:34.058330Z","shell.execute_reply.started":"2025-07-11T10:52:33.533975Z","shell.execute_reply":"2025-07-11T10:52:34.057532Z"}},"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-11T10:52:34.059202Z","iopub.execute_input":"2025-07-11T10:52:34.059420Z","iopub.status.idle":"2025-07-11T10:52:34.063981Z","shell.execute_reply.started":"2025-07-11T10:52:34.059403Z","shell.execute_reply":"2025-07-11T10:52:34.063321Z"}},"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-11T10:52:34.064680Z","iopub.execute_input":"2025-07-11T10:52:34.064857Z","iopub.status.idle":"2025-07-11T10:52:38.322103Z","shell.execute_reply.started":"2025-07-11T10:52:34.064841Z","shell.execute_reply":"2025-07-11T10:52:38.321422Z"}},"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-11T10:52:38.322922Z","iopub.execute_input":"2025-07-11T10:52:38.323170Z","iopub.status.idle":"2025-07-11T10:52:38.364356Z","shell.execute_reply.started":"2025-07-11T10:52:38.323145Z","shell.execute_reply":"2025-07-11T10:52:38.363626Z"}},"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-11T10:52:38.367104Z","iopub.execute_input":"2025-07-11T10:52:38.369635Z","iopub.status.idle":"2025-07-11T10:52:39.064972Z","shell.execute_reply.started":"2025-07-11T10:52:38.369612Z","shell.execute_reply":"2025-07-11T10:52:39.064020Z"}},"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-11T10:52:39.065835Z","iopub.execute_input":"2025-07-11T10:52:39.066090Z","iopub.status.idle":"2025-07-11T10:52:53.695059Z","shell.execute_reply.started":"2025-07-11T10:52:39.066065Z","shell.execute_reply":"2025-07-11T10:52:53.694249Z"}},"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-11T10:52:53.695888Z","iopub.execute_input":"2025-07-11T10:52:53.696402Z","iopub.status.idle":"2025-07-11T10:52:53.700226Z","shell.execute_reply.started":"2025-07-11T10:52:53.696379Z","shell.execute_reply":"2025-07-11T10:52:53.699422Z"}},"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-11T10:52:53.701083Z","iopub.execute_input":"2025-07-11T10:52:53.701353Z","iopub.status.idle":"2025-07-11T10:52:53.815028Z","shell.execute_reply.started":"2025-07-11T10:52:53.701330Z","shell.execute_reply":"2025-07-11T10:52:53.814341Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 検証用：正規化のみ\nval_datagen = ImageDataGenerator(rescale=1.0 / 255)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T10:52:53.815975Z","iopub.execute_input":"2025-07-11T10:52:53.816497Z","iopub.status.idle":"2025-07-11T10:52:53.831395Z","shell.execute_reply.started":"2025-07-11T10:52:53.816446Z","shell.execute_reply":"2025-07-11T10:52:53.830700Z"}},"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-11T10:52:53.832198Z","iopub.execute_input":"2025-07-11T10:52:53.832443Z","iopub.status.idle":"2025-07-11T10:52:53.851878Z","shell.execute_reply.started":"2025-07-11T10:52:53.832419Z","shell.execute_reply":"2025-07-11T10:52:53.851116Z"}},"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-11T10:52:53.852725Z","iopub.execute_input":"2025-07-11T10:52:53.852968Z","iopub.status.idle":"2025-07-11T10:52:54.163597Z","shell.execute_reply.started":"2025-07-11T10:52:53.852942Z","shell.execute_reply":"2025-07-11T10:52:54.162821Z"}},"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-11T10:52:54.164456Z","iopub.execute_input":"2025-07-11T10:52:54.164760Z","iopub.status.idle":"2025-07-11T10:52:54.175047Z","shell.execute_reply.started":"2025-07-11T10:52:54.164737Z","shell.execute_reply":"2025-07-11T10:52:54.174494Z"}},"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-11T10:52:54.175910Z","iopub.execute_input":"2025-07-11T10:52:54.176154Z","iopub.status.idle":"2025-07-11T10:52:54.186907Z","shell.execute_reply.started":"2025-07-11T10:52:54.176127Z","shell.execute_reply":"2025-07-11T10:52:54.186136Z"}},"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-11T10:52:54.187766Z","iopub.execute_input":"2025-07-11T10:52:54.188022Z","iopub.status.idle":"2025-07-11T10:52:58.133262Z","shell.execute_reply.started":"2025-07-11T10:52:54.188004Z","shell.execute_reply":"2025-07-11T10:52:58.132693Z"}},"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-11T10:52:58.134080Z","iopub.execute_input":"2025-07-11T10:52:58.134431Z","iopub.status.idle":"2025-07-11T10:52:58.138954Z","shell.execute_reply.started":"2025-07-11T10:52:58.134409Z","shell.execute_reply":"2025-07-11T10:52:58.138376Z"}},"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-11T10:52:58.139803Z","iopub.execute_input":"2025-07-11T10:52:58.140025Z","iopub.status.idle":"2025-07-11T10:52:58.177222Z","shell.execute_reply.started":"2025-07-11T10:52:58.140002Z","shell.execute_reply":"2025-07-11T10:52:58.176683Z"}},"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-11T10:52:58.178179Z","iopub.execute_input":"2025-07-11T10:52:58.178440Z","iopub.status.idle":"2025-07-11T10:52:58.189162Z","shell.execute_reply.started":"2025-07-11T10:52:58.178417Z","shell.execute_reply":"2025-07-11T10:52:58.188428Z"}},"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-11T10:52:58.190059Z","iopub.execute_input":"2025-07-11T10:52:58.190295Z","iopub.status.idle":"2025-07-11T10:52:58.216016Z","shell.execute_reply.started":"2025-07-11T10:52:58.190270Z","shell.execute_reply":"2025-07-11T10:52:58.215331Z"}},"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-11T10:52:58.216809Z","iopub.execute_input":"2025-07-11T10:52:58.217014Z","iopub.status.idle":"2025-07-11T10:52:58.226757Z","shell.execute_reply.started":"2025-07-11T10:52:58.216999Z","shell.execute_reply":"2025-07-11T10:52:58.225901Z"}},"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-11T10:52:58.227499Z","iopub.execute_input":"2025-07-11T10:52:58.227747Z","execution_failed":"2025-07-11T13:42:48.271Z"}},"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":{"execution_failed":"2025-07-11T13:42:48.271Z"}},"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":{"execution_failed":"2025-07-11T13:42:48.271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 保存されたファイル一覧を表示して確認\n!ls -lh submission.csv","metadata":{"trusted":true,"execution":{"execution_failed":"2025-07-11T13:42:48.271Z"}},"outputs":[],"execution_count":null}]}