{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.18","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"tpu1vmV38","dataSources":[{"sourceId":5441,"databundleVersionId":38425,"sourceType":"competition"}],"dockerImageVersionId":31089,"isInternetEnabled":true,"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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:11:59.862656Z","iopub.execute_input":"2025-07-18T03:11:59.862928Z","iopub.status.idle":"2025-07-18T03:12:00.328406Z","shell.execute_reply.started":"2025-07-18T03:11:59.862890Z","shell.execute_reply":"2025-07-18T03:12:00.322950Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import zipfile # zipファイルの解凍に必要\nimport tensorflow as tf # 機械学習に必要\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator # データの正規化に必要\nfrom tensorflow.keras import layers, models, regularizers # レイヤークラス, 学習モデル\nfrom tensorflow.keras.preprocessing import image # 画像の前処理に必要\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.applications import EfficientNetB5 # 転移学習\nfrom tensorflow.keras.optimizers import Adam\nimport shutil # クラス分けに必要\nimport glob # ファイルのソートに必要\nimport re\nfrom sklearn.metrics import accuracy_score # 評価","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:00.330064Z","iopub.execute_input":"2025-07-18T03:12:00.330344Z","iopub.status.idle":"2025-07-18T03:12:22.304764Z","shell.execute_reply.started":"2025-07-18T03:12:00.330315Z","shell.execute_reply":"2025-07-18T03:12:22.299126Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"try:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver(tpu='local')  # TPUの検出\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.TPUStrategy(tpu)\nexcept ValueError as e:\n    strategy = tf.distribute.get_strategy()  # CPU or single GPU fallback\n    print(\"TPU not found, using default strategy\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:22.305773Z","iopub.execute_input":"2025-07-18T03:12:22.306181Z","iopub.status.idle":"2025-07-18T03:12:30.832424Z","shell.execute_reply.started":"2025-07-18T03:12:22.306158Z","shell.execute_reply":"2025-07-18T03:12:30.827809Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMG_SIZE = 456\nBATCH_SIZE = 32\nEPOCHS_0 = 10\nEPOCHS_1 = 5\nVALIDATION_SPLIT = 0.2","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:30.834955Z","iopub.execute_input":"2025-07-18T03:12:30.835199Z","iopub.status.idle":"2025-07-18T03:12:30.845039Z","shell.execute_reply.started":"2025-07-18T03:12:30.835175Z","shell.execute_reply":"2025-07-18T03:12:30.840089Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"zip_file = '/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip'\nout_dir = '/kaggle/working'\n\nwith zipfile.ZipFile(zip_file, 'r') as file:\n    file.extractall(out_dir)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:30.847010Z","iopub.execute_input":"2025-07-18T03:12:30.847238Z","iopub.status.idle":"2025-07-18T03:12:40.160621Z","shell.execute_reply.started":"2025-07-18T03:12:30.847216Z","shell.execute_reply":"2025-07-18T03:12:40.156337Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_dir = '/kaggle/working/train'\n\ncat_dir = os.path.join(train_dir, 'cats')\ndog_dir = os.path.join(train_dir, 'dogs')\n\nos.makedirs(cat_dir, exist_ok=True)\nos.makedirs(dog_dir, exist_ok=True)\n\nfor file_name in os.listdir(train_dir):\n    file = os.path.join(train_dir, file_name)\n    if not os.path.isfile(file):\n        continue\n\n    if file_name.lower().startswith('cat'):\n        shutil.move(os.path.join(train_dir, file_name), os.path.join(cat_dir, file_name))\n    elif file_name.lower().startswith('dog'):\n        shutil.move(os.path.join(train_dir, file_name), os.path.join(dog_dir, file_name))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:40.163013Z","iopub.execute_input":"2025-07-18T03:12:40.163295Z","iopub.status.idle":"2025-07-18T03:12:41.284825Z","shell.execute_reply.started":"2025-07-18T03:12:40.163274Z","shell.execute_reply":"2025-07-18T03:12:41.279898Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=VALIDATION_SPLIT,\n    rotation_range=20,            # 回転\n    zoom_range=0.2,               # ズーム\n    horizontal_flip=True,         # 水平方向の反転\n    fill_mode='nearest'\n)\n\ntrain_generator = train_datagen.flow_from_directory(\n    '/kaggle/working/train',\n    target_size=(IMG_SIZE, IMG_SIZE),\n    batch_size=BATCH_SIZE,\n    class_mode='binary',\n    subset='training',\n    shuffle=True\n)\n\nval_generator = train_datagen.flow_from_directory(\n    '/kaggle/working/train',\n    target_size=(IMG_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-18T03:12:41.285561Z","iopub.execute_input":"2025-07-18T03:12:41.285783Z","iopub.status.idle":"2025-07-18T03:12:41.774025Z","shell.execute_reply.started":"2025-07-18T03:12:41.285761Z","shell.execute_reply":"2025-07-18T03:12:41.768975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    base_model = EfficientNetB5(weights='imagenet', include_top=False)\n    base_model.trainable = False\n    \n    model = models.Sequential([\n        layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3)),\n        base_model,\n        layers.GlobalAveragePooling2D(),\n        layers.Dropout(0.4),\n        layers.Dense(1, activation='sigmoid') # sigmoidにより0-1の確率を算出\n    ])\n    model.compile(\n        loss=tf.keras.losses.BinaryCrossentropy(),\n        optimizer=Adam(learning_rate=1e-3),\n        metrics=['accuracy']\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:12:41.776297Z","iopub.execute_input":"2025-07-18T03:12:41.776517Z","iopub.status.idle":"2025-07-18T03:13:16.431730Z","shell.execute_reply.started":"2025-07-18T03:12:41.776491Z","shell.execute_reply":"2025-07-18T03:13:16.426491Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks = [\n    EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True, verbose=1),\n    ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=3, min_lr=1e-6, verbose=1)\n]\n\nmodel.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=EPOCHS_0,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:13:16.433676Z","iopub.execute_input":"2025-07-18T03:13:16.433981Z","iopub.status.idle":"2025-07-18T03:15:32.195853Z","shell.execute_reply.started":"2025-07-18T03:13:16.433956Z","shell.execute_reply":"2025-07-18T03:15:32.191678Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with strategy.scope():\n    for layer in base_model.layers[-20:]:\n        layer.trainable = True\n    \n    model.compile(\n        loss=tf.keras.losses.BinaryCrossentropy(),\n        optimizer=Adam(learning_rate=1e-5),\n        metrics=['accuracy']\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:15:32.199445Z","iopub.execute_input":"2025-07-18T03:15:32.199700Z","iopub.status.idle":"2025-07-18T03:15:32.422398Z","shell.execute_reply.started":"2025-07-18T03:15:32.199676Z","shell.execute_reply":"2025-07-18T03:15:32.417511Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model.fit(\n    train_generator,\n    validation_data=val_generator,\n    epochs=EPOCHS_1,\n    callbacks=callbacks\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:15:32.426203Z","iopub.execute_input":"2025-07-18T03:15:32.426480Z","iopub.status.idle":"2025-07-18T03:17:55.167843Z","shell.execute_reply.started":"2025-07-18T03:15:32.426453Z","shell.execute_reply":"2025-07-18T03:17:55.161980Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"pred = model.predict(val_generator)\npred = (pred > 0.5).astype(int).flatten()\nlabel = val_generator.classes\nacc = accuracy_score(pred, label)\n\ndf = pd.DataFrame({'acc': [acc]})\ndf.to_csv('accuracy.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:17:55.170638Z","iopub.execute_input":"2025-07-18T03:17:55.171455Z","iopub.status.idle":"2025-07-18T03:18:27.784641Z","shell.execute_reply.started":"2025-07-18T03:17:55.171423Z","shell.execute_reply":"2025-07-18T03:18:27.779271Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# テストデータファイルの解凍\nzip_file = '/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip'\nout_dir = '/kaggle/working'\n\nwith zipfile.ZipFile(zip_file, 'r') as file:\n    file.extractall(out_dir)\n\n# ファイル一覧を所得\ntest_dir = '/kaggle/working/test'\ntest_files = os.listdir(test_dir)\n\n# 数値順にソートする関数\ndef extract_number(filename):\n    match = re.search(r'\\d+', filename)\n    return int(match.group()) if match else -1\n\n# 数値順にソート\nsorted_files = sorted(test_files, key=extract_number)\n\n# 画像ファイルのフルパスリスト\nimage_path_list = [os.path.join(test_dir, image) for image in sorted_files]\n\n# 前処理\nimages = [image.img_to_array(image.load_img(path, target_size=(IMG_SIZE, IMG_SIZE))) / 255.0 for path in image_path_list]\nbatch = np.array(images)\n\n# 推論\npred = model.predict(batch)\n\n# IDの取得\nids = [os.path.splitext(os.path.basename(path))[0] for path in image_path_list]\n\n# ラベルの取得\nlabels = np.clip(pred, 1e-6, 1 - 1e-6).ravel()\n\n# CSVで保存\ndf = pd.DataFrame({'id': ids, 'label': labels})\ndf.to_csv('submission.csv', index=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T03:18:27.787071Z","iopub.execute_input":"2025-07-18T03:18:27.787336Z","iopub.status.idle":"2025-07-18T03:19:27.553870Z","shell.execute_reply.started":"2025-07-18T03:18:27.787297Z","shell.execute_reply":"2025-07-18T03:19:27.547067Z"}},"outputs":[],"execution_count":null}]}