{"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":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-25T01:42:52.167529Z","iopub.execute_input":"2025-07-25T01:42:52.167793Z","iopub.status.idle":"2025-07-25T01:42:53.914639Z","shell.execute_reply.started":"2025-07-25T01:42:52.167767Z","shell.execute_reply":"2025-07-25T01:42:53.913752Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## zipファイルの解凍と画像の振り分け","metadata":{}},{"cell_type":"code","source":"import os, zipfile, shutil, pathlib\n\n# 解凍先ディレクトリ\nWORK_DIR = '/kaggle/working'\n\n# train.zip の解凍\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall(f'{WORK_DIR}')\n\n# 犬・猫を分類するためのフォルダ作成\nbase_dir = pathlib.Path(f'{WORK_DIR}/dataset')\ncat_dir = base_dir / 'cats'\ndog_dir = base_dir / 'dogs'\ncat_dir.mkdir(parents=True, exist_ok=True)\ndog_dir.mkdir(parents=True, exist_ok=True)\n\n# trainフォルダの画像をcats/dogsに仕分け\ntrain_dir = pathlib.Path(f'{WORK_DIR}/train')\nfor file in train_dir.iterdir():\n    if 'cat' in file.name:\n        shutil.move(str(file), cat_dir / file.name)\n    elif 'dog' in file.name:\n        shutil.move(str(file), dog_dir / file.name)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T01:42:53.915887Z","iopub.execute_input":"2025-07-25T01:42:53.916196Z","iopub.status.idle":"2025-07-25T01:43:06.058984Z","shell.execute_reply.started":"2025-07-25T01:42:53.916178Z","shell.execute_reply":"2025-07-25T01:43:06.058179Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## データの読み込み","metadata":{}},{"cell_type":"code","source":"import cv2\nimport numpy as np\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.applications.convnext import preprocess_input\n\nIMG_SIZE = (224, 224)\nBATCH_SIZE = 32\n\nimport cv2\nimport numpy as np\nfrom tensorflow.keras.applications.convnext import preprocess_input\n\n# 画像の中心以外をぼかす\ndef focus_blur_preprocessing(img):\n    # BGR変換\n    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)\n\n    # 中心にフォーカス\n    h, w = img.shape[:2]\n    mask = np.zeros((h, w), dtype=np.uint8)\n    cv2.circle(mask, (w // 2, h // 2), int(min(h, w) * 0.4), 255, -1)  # 半径は40%程度\n\n    blurred = cv2.GaussianBlur(img, (25, 25), 0)\n    focused = np.where(mask[..., None] == 255, img, blurred)\n\n    # BGR → RGB\n    focused = cv2.cvtColor(focused, cv2.COLOR_BGR2RGB)\n\n    # ConvNeXt用の前処理\n    return preprocess_input(focused.astype(np.float32))\n\n# ImageDataGeneratorの設定\ndatagen = ImageDataGenerator(\n    preprocessing_function=focus_blur_preprocessing,\n    validation_split=0.2,\n    horizontal_flip=True,\n    rotation_range=15,\n    zoom_range=0.2\n)\n\ntrain_gen = datagen.flow_from_directory(\n    directory='/kaggle/working/dataset',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    subset='training',\n    shuffle=True\n)\n\nval_gen = datagen.flow_from_directory(\n    directory='/kaggle/working/dataset',\n    target_size=IMG_SIZE,\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    subset='validation',\n    shuffle=False\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T01:43:06.061896Z","iopub.execute_input":"2025-07-25T01:43:06.062629Z","iopub.status.idle":"2025-07-25T01:43:25.058333Z","shell.execute_reply.started":"2025-07-25T01:43:06.062585Z","shell.execute_reply":"2025-07-25T01:43:25.057757Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ConvNeXt-Small の構築","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.applications import ConvNeXtSmall\nfrom tensorflow.keras import layers, models, optimizers\n\nbase_model = ConvNeXtSmall(\n    weights='imagenet',\n    include_top=False,\n    input_shape=(224, 224, 3)\n)\nbase_model.trainable = False  # 転移学習初期は凍結\n\nmodel = models.Sequential([\n    base_model,\n    layers.GlobalAveragePooling2D(),\n    layers.Dropout(0.3),\n    layers.Dense(1, activation='sigmoid')\n])\n\nmodel.compile(\n    optimizer=optimizers.Adam(learning_rate=1e-4),\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T01:43:25.059864Z","iopub.execute_input":"2025-07-25T01:43:25.060337Z","iopub.status.idle":"2025-07-25T01:43:31.313589Z","shell.execute_reply.started":"2025-07-25T01:43:25.060318Z","shell.execute_reply":"2025-07-25T01:43:31.312810Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## モデルの学習","metadata":{}},{"cell_type":"code","source":"from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint\n\ncallbacks = [\n    EarlyStopping(patience=3, restore_best_weights=True),\n    ModelCheckpoint('convnext_small_best.h5', save_best_only=True)\n]\n\nhistory = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=18,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T01:43:31.314436Z","iopub.execute_input":"2025-07-25T01:43:31.314725Z","iopub.status.idle":"2025-07-25T03:16:14.629265Z","shell.execute_reply.started":"2025-07-25T01:43:31.314696Z","shell.execute_reply":"2025-07-25T03:16:14.628658Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# ConvNeXt の全層を再学習させる\nbase_model.trainable = True  # 凍結解除\n\nfor layer in base_model.layers[:-20]:\n    layer.trainable = False\n\n# 再コンパイル\nmodel.compile(\n    optimizer=optimizers.Adam(learning_rate=1e-6),\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\n# エポック数を定義\ninitial_epochs = len(history.epoch)\nfine_tune_epochs = 4\ntotal_epochs = initial_epochs + fine_tune_epochs\n\n# ファインチューニング実行\nhistory_fine = model.fit(\n    train_gen,\n    validation_data=val_gen,\n    epochs=total_epochs,\n    initial_epoch=initial_epochs,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T03:16:14.630225Z","iopub.execute_input":"2025-07-25T03:16:14.630500Z","iopub.status.idle":"2025-07-25T03:38:20.450068Z","shell.execute_reply.started":"2025-07-25T03:16:14.630480Z","shell.execute_reply":"2025-07-25T03:38:20.449438Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 学習済みモデルを保存\nmodel.save(\"/kaggle/working/best_model.h5\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T03:38:20.451209Z","iopub.execute_input":"2025-07-25T03:38:20.451434Z","iopub.status.idle":"2025-07-25T03:38:21.272998Z","shell.execute_reply.started":"2025-07-25T03:38:20.451415Z","shell.execute_reply":"2025-07-25T03:38:21.272164Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## テスト画像の読み込みと推論","metadata":{}},{"cell_type":"code","source":"# test.zip の解凍\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/test')\n\ntest_datagen = ImageDataGenerator(\n    preprocessing_function=focus_blur_preprocessing  # 学習と同じ前処理関数\n)\n\n# ImageDataGeneratorでテスト画像を読み込み\ntest_gen = ImageDataGenerator(preprocessing_function=focus_blur_preprocessing).flow_from_directory(\n    directory='/kaggle/working',\n    classes=['test'],\n    target_size=IMG_SIZE,\n    batch_size=1,\n    shuffle=False,\n    class_mode=None\n)\n\n# 推論（0〜1の確率）\npreds = model.predict(test_gen, verbose=1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T03:38:21.274146Z","iopub.execute_input":"2025-07-25T03:38:21.274423Z","iopub.status.idle":"2025-07-25T03:41:03.366351Z","shell.execute_reply.started":"2025-07-25T03:38:21.274394Z","shell.execute_reply":"2025-07-25T03:41:03.365695Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 提出用ファイル作成","metadata":{}},{"cell_type":"code","source":"import pandas as pd\n\n# sampleSubmission.csv 読み込み\nsubmission = pd.read_csv('/kaggle/input/dogs-vs-cats-redux-kernels-edition/sample_submission.csv')\n\n# ファイル名からIDを抽出\ntest_ids = [int(fname.split('/')[-1].split('.')[0]) for fname in test_gen.filenames]\n\n# 推論結果をDataFrameにする\nsubmission = pd.DataFrame({\n    'id': test_ids,\n    'label': np.clip(preds.flatten(), 1e-6, 1 - 1e-6)\n})\n\n# id順にソート\nsubmission = submission.sort_values('id')\nsubmission.to_csv('submission.csv', index=False)\n\n# 表示\nsubmission.head(10)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T03:41:03.367465Z","iopub.execute_input":"2025-07-25T03:41:03.367741Z","iopub.status.idle":"2025-07-25T03:41:03.515070Z","shell.execute_reply.started":"2025-07-25T03:41:03.367721Z","shell.execute_reply":"2025-07-25T03:41:03.514326Z"}},"outputs":[],"execution_count":null}]}