{"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":"none","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},"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\nimport shutil # クラス分けに必要\nimport glob # ファイルのソートに必要\nimport re\nfrom sklearn.metrics import accuracy_score # 評価","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:12:17.759169Z","iopub.execute_input":"2025-07-25T11:12:17.759500Z","iopub.status.idle":"2025-07-25T11:12:17.764846Z","shell.execute_reply.started":"2025-07-25T11:12:17.759475Z","shell.execute_reply":"2025-07-25T11:12:17.763813Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## ハイパラメータの定義","metadata":{}},{"cell_type":"code","source":"IMG_SIZE = 150\nBATCH_SIZE = 32\nEPOCHS = 1\nVALIDATION_SPLIT = 0.2\nMODEL_NUM = 1","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:12:20.745018Z","iopub.execute_input":"2025-07-25T11:12:20.745362Z","iopub.status.idle":"2025-07-25T11:12:20.750727Z","shell.execute_reply.started":"2025-07-25T11:12:20.745335Z","shell.execute_reply":"2025-07-25T11:12:20.749481Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 学習データファイルを解凍","metadata":{}},{"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-25T11:12:23.181043Z","iopub.execute_input":"2025-07-25T11:12:23.181381Z","iopub.status.idle":"2025-07-25T11:12:31.193711Z","shell.execute_reply.started":"2025-07-25T11:12:23.181358Z","shell.execute_reply":"2025-07-25T11:12:31.192701Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 画像ファイルをクラスで分類する\n犬の画像を\"/kaggle/working/train/*/dogs\"に、猫の画像を\"/kaggle/working/train/*/cats\"に分ける。\n\ncat:0\ndog:1","metadata":{}},{"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-25T11:12:35.213671Z","iopub.execute_input":"2025-07-25T11:12:35.214529Z","iopub.status.idle":"2025-07-25T11:12:36.115740Z","shell.execute_reply.started":"2025-07-25T11:12:35.214480Z","shell.execute_reply":"2025-07-25T11:12:36.114842Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## データの前処理","metadata":{}},{"cell_type":"code","source":"train_datagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=VALIDATION_SPLIT,\n    #rotation_range=30,            # 回転\n    #width_shift_range=0.2,        # 横移動\n    #height_shift_range=0.2,       # 縦移動\n    #shear_range=0.2,              # シアー変形\n    #zoom_range=0.3,               # ズーム\n    #horizontal_flip=True,         # 水平方向の反転\n    #brightness_range=[0.6, 1.4],\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-25T11:12:42.179987Z","iopub.execute_input":"2025-07-25T11:12:42.180276Z","iopub.status.idle":"2025-07-25T11:12:42.495814Z","shell.execute_reply.started":"2025-07-25T11:12:42.180253Z","shell.execute_reply":"2025-07-25T11:12:42.494901Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## CNNモデルの構築¶\n学習モデルを構築する。Seqential()は順番に積み重ねるタイプのニューラルネットワークである。\n\nConv2D（畳み込み層）\n第一引数：フィルター数\n第二引数：フィルターサイズ\n第三引数：活性化関数\n第四引数：入力サイズ\nMaxPooling2D（プーリング層）\nn*nの領域ごとに最大値を取って縮小する。\nFlatten\n出力を1次元のベクトルに変換\nDense（全結合層）\n第一引数：ノード数\n第二引数：活性化関数","metadata":{}},{"cell_type":"code","source":"def se_block(input_tensor, reduction=16):\n    filters = input_tensor.shape[-1]\n    se = layers.GlobalAveragePooling2D()(input_tensor)\n    se = layers.Dense(filters // reduction, activation='relu')(se)\n    se = layers.Dense(filters, activation='sigmoid')(se)\n    se = layers.Reshape((1, 1, filters))(se)\n    return layers.Multiply()([input_tensor, se])\n    \ndef build_model():\n    model = models.Sequential([\n        layers.Input(shape=(IMG_SIZE, IMG_SIZE, 3)),\n            \n        layers.Conv2D(32, (3, 3), padding='same', activation='relu', kernel_regularizer=regularizers.l2(0.001)), # 画像化から特徴を抽出する\n        layers.BatchNormalization(),\n        layers.MaxPooling2D(2, 2), # 重要な情報を残しつつ、画像サイズを圧縮\n            \n        layers.Conv2D(64, (3, 3), padding='same', activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n        layers.BatchNormalization(),\n        layers.MaxPooling2D(2, 2),\n            \n        layers.Conv2D(128, (3, 3), padding='same', activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n        layers.BatchNormalization(),\n        layers.MaxPooling2D(2, 2),\n\n        layers.Conv2D(256, (3, 3), padding='same', activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n        layers.BatchNormalization(),\n        layers.MaxPooling2D(2, 2),\n            \n        layers.GlobalAveragePooling2D(),\n        layers.BatchNormalization(),\n        \n        layers.Dense(512, activation='relu', kernel_regularizer=regularizers.l2(0.001)), # 中間特徴を学習し、分析精度を向上させる\n        layers.Dropout(0.5),\n        layers.Dense(256, activation='relu', kernel_regularizer=regularizers.l2(0.001)),\n        layers.Dropout(0.4),\n        layers.Dense(1, activation='sigmoid') # sigmoidにより0-1の確率を算出\n    ])\n    model.compile(\n        loss=tf.keras.losses.BinaryCrossentropy(label_smoothing=0.05),\n        optimizer='adam',\n        metrics=['accuracy']\n    )\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:12:45.683830Z","iopub.execute_input":"2025-07-25T11:12:45.684103Z","iopub.status.idle":"2025-07-25T11:12:45.696289Z","shell.execute_reply.started":"2025-07-25T11:12:45.684084Z","shell.execute_reply":"2025-07-25T11:12:45.695236Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## アンサンブル学習","metadata":{}},{"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\nfor i in range(MODEL_NUM):\n    print(f\"model : {i}\")\n    model = build_model()\n    model.fit(\n        train_generator,\n        validation_data=val_generator,\n        epochs=EPOCHS,\n        callbacks=callbacks\n    )\n    model.save(f\"model{i}.h5\")","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 評価","metadata":{}},{"cell_type":"code","source":"sum_pred = None\nfor i in range(MODEL_NUM):\n    print(f\"model : {i}\")\n    model = models.load_model(f\"model{i}.h5\")\n    pred = model.predict(val_generator)\n\n    if sum_pred is None:\n        sum_pred = pred\n    else:\n        sum_pred += pred\n\navg_pred = sum_pred / MODEL_NUM\ny_pred = (avg_pred > 0.5).astype(int).flatten()\ny_true = val_generator.classes\nacc = accuracy_score(y_true, y_pred)\nprint(f\"精度評価 : {acc:.4f}\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-25T11:08:06.489017Z","iopub.execute_input":"2025-07-25T11:08:06.490102Z","iopub.status.idle":"2025-07-25T11:08:06.558827Z","shell.execute_reply.started":"2025-07-25T11:08:06.490072Z","shell.execute_reply":"2025-07-25T11:08:06.557496Z"}},"outputs":[],"execution_count":null}]}