{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","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":31041,"isInternetEnabled":false,"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:49:54.336187Z","iopub.execute_input":"2025-07-11T10:49:54.336463Z","iopub.status.idle":"2025-07-11T10:49:54.596032Z","shell.execute_reply.started":"2025-07-11T10:49:54.336441Z","shell.execute_reply":"2025-07-11T10:49:54.595429Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Cat と Dog の画像の分類をCNNを使用して行う","metadata":{}},{"cell_type":"markdown","source":"## 学習用データとテストデータを読み込む\nデータの解凍，中身の確認，学習に使える形に前処理&データセットの作成","metadata":{}},{"cell_type":"code","source":"# 使用する外部ライブラリーのインポート\nimport zipfile\nimport os\nimport tensorflow as tf\nimport numpy as np\nimport matplotlib.pyplot as plt\nfrom PIL import Image\nimport pandas as pd\nimport glob\nfrom sklearn.model_selection import train_test_split\nimport shutil\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers, models\n# from tensorflow.keras.applications import EfficientNetB3\nfrom tensorflow.keras.callbacks import EarlyStopping\nfrom tensorflow.keras.utils import Sequence\nfrom tensorflow.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.losses import binary_crossentropy\nimport cv2\n\nprint('Library loading complete!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:31:05.250806Z","iopub.execute_input":"2025-07-18T08:31:05.251098Z","iopub.status.idle":"2025-07-18T08:31:05.256969Z","shell.execute_reply.started":"2025-07-18T08:31:05.251078Z","shell.execute_reply":"2025-07-18T08:31:05.256072Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# データ分析に大きく影響するハイパーパラメータ\nPicSize   = 224\nBatchSize = 16\nSplitRate = 0.2\nLr        = 1e-4\nEpochs    = 20\nPatience  = 10\nprint('Parameters determined!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:06:38.884072Z","iopub.execute_input":"2025-07-18T08:06:38.884774Z","iopub.status.idle":"2025-07-18T08:06:38.88961Z","shell.execute_reply.started":"2025-07-18T08:06:38.884739Z","shell.execute_reply":"2025-07-18T08:06:38.888936Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 訓練データ（train.zip）とテストデータ(test.zip)をそれぞれ，/kaggle/working/train,/kaggle/working/testに解凍\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/train.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\nwith zipfile.ZipFile('/kaggle/input/dogs-vs-cats-redux-kernels-edition/test.zip', 'r') as zip_ref:\n    zip_ref.extractall('/kaggle/working/')\n\nprint('train.zip and test.zip unzipped!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:06:46.398664Z","iopub.execute_input":"2025-07-18T08:06:46.399371Z","iopub.status.idle":"2025-07-18T08:06:59.623713Z","shell.execute_reply.started":"2025-07-18T08:06:46.39934Z","shell.execute_reply":"2025-07-18T08:06:59.622979Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 本当にファイルが生成されているのかの確認\nfile_list = os.listdir('/kaggle/working')\nprint(file_list)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:07:48.697481Z","iopub.execute_input":"2025-07-18T08:07:48.697793Z","iopub.status.idle":"2025-07-18T08:07:48.702361Z","shell.execute_reply.started":"2025-07-18T08:07:48.697769Z","shell.execute_reply":"2025-07-18T08:07:48.701788Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # データの中身の確認\n# # 解凍先のパス\ntrain_dir = '/kaggle/working/train'\n\n# ファイル一覧取得（catとdog両方含む）\nfile_list = os.listdir(train_dir)\nfile_list.sort()  # 確認しやすくするためソート\nprint(file_list[0:10])\n\ncat_count = sum(s.startswith(\"cat\") for s in file_list)\ndog_count = sum(s.startswith(\"dog\") for s in file_list)\nprint(f'The number of Train data : {len(file_list)}')\nprint(f'Cats : {cat_count} ,Dog : {dog_count}')\n\n# 最初の画像ファイル\nimg_path = os.path.join(train_dir, file_list[0])\n\n# 画像読み込み\nimg = Image.open(img_path)\nprint(\"Image size:\", img.size)\n\n# 表示\nfig = plt.figure(figsize=(5, 5))\nax = fig.add_subplot(1, 1, 1)\nax.imshow(img)\nax.set_title(f\"Filename: {os.path.basename(img_path)}\")\nax.axis('off')  # 軸を非表示\nplt.show()\n\n\n# 特定の10枚\ndog_list = [f for f in file_list if f.startswith(\"dog.\")]  # dogで始まるやつのみ\nselected_files = dog_list[0:10]\n\n# プロット準備\nfig, axes = plt.subplots(2, 5, figsize=(15, 6))\nfig.tight_layout(pad=2.0)\n\n# 各画像を表示\nfor i, ax in enumerate(axes.flat):\n    img_path = os.path.join(train_dir, selected_files[i])\n    img = Image.open(img_path)\n    ax.imshow(img)\n    ax.set_title(os.path.basename(img_path), fontsize=8)\n    ax.axis('off')\nfig.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:07:52.524448Z","iopub.execute_input":"2025-07-18T08:07:52.524748Z","iopub.status.idle":"2025-07-18T08:07:53.974823Z","shell.execute_reply.started":"2025-07-18T08:07:52.52471Z","shell.execute_reply":"2025-07-18T08:07:53.97362Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# 前処理＆データセット作成\nsrc_dir = '/kaggle/working/train'\ncat_dir = os.path.join(src_dir, 'cat')\ndog_dir = os.path.join(src_dir, 'dog')\n\nos.makedirs(cat_dir, exist_ok=True)\nos.makedirs(dog_dir, exist_ok=True)\n\nfor filename in os.listdir(src_dir):\n    if filename.endswith('.jpg'):\n        if filename.startswith('cat'):\n            shutil.move(os.path.join(src_dir, filename), os.path.join(cat_dir, filename))\n        elif filename.startswith('dog'):\n            shutil.move(os.path.join(src_dir, filename), os.path.join(dog_dir, filename))\n\n# ファイルパスとラベルを取得（cat=0, dog=1）\ncat_images = glob.glob(os.path.join('/kaggle/working/train', 'cat', '*.jpg'))\ndog_images = glob.glob(os.path.join('/kaggle/working/train', 'dog', '*.jpg'))\nprint(\"cat images:\", len(cat_images))\nprint(\"dog images:\", len(dog_images))\n\nall_images = cat_images + dog_images\nlabels = [0]*len(cat_images) + [1]*len(dog_images)\n\n\ntrain_datagen = ImageDataGenerator(rescale=1./255, validation_split=SplitRate)\n\nDs_train = train_datagen.flow_from_directory(\n    '/kaggle/working/train',\n    target_size=(PicSize, PicSize),\n    batch_size=BatchSize,\n    class_mode='binary',\n    subset='training'\n)\n\nDs_val = train_datagen.flow_from_directory(\n    '/kaggle/working/train',\n    target_size=(PicSize, PicSize),\n    batch_size=BatchSize,\n    class_mode='binary',\n    subset='validation'\n)\n\n# # 学習・検証データに分割\n# train_paths, val_paths, train_labels, val_labels = train_test_split(\n#     all_images, labels, test_size=SplitRate, stratify=labels, random_state=42\n# )\n\n# # 画像を読み込み、リサイズ・正規化する関数\n# def decode_img(path, label):\n#     img = tf.io.read_file(path)  # 画像ファイルをバイナリとして読み込む\n#     img = tf.image.decode_jpeg(img, channels=3)  # JPEG形式の画像をTensorにデコード（RGBの3チャンネル）\n#     img = tf.image.resize(img, (PicSize, PicSize))  # 画像を224×224にリサイズ\n#     img = img / 255.0  # ピクセル値を0〜1の範囲に正規化（float32型になる）\n#     return img, label  # 画像とラベルを返す\n\n# # 学習・検証用の tf.data.Dataset を構築する関数\n# def build_dataset(paths, labels, is_train=True):\n#     ds = tf.data.Dataset.from_tensor_slices((paths, labels))  # パスとラベルのペアからデータセットを作成\n#     ds = ds.map(decode_img, num_parallel_calls=tf.data.AUTOTUNE)  # decode_img関数で画像を前処理（並列実行で高速化）\n\n#     if is_train:\n#         ds = ds.shuffle(buffer_size=1024, seed=42)  # 学習時はシャッフル（バッファサイズ1024でランダムに並べ替え）\n\n#     ds = ds.batch(BatchSize)  # BatchSizeずつのバッチに分割\n#     ds = ds.prefetch(tf.data.AUTOTUNE)  # 学習中に次のバッチを先読み（非同期処理でパフォーマンス向上）\n#     return ds  # データセットを返す\n\n# データセット作成\n# Ds_train = build_dataset(train_paths, train_labels, is_train=True)\n# Ds_val = build_dataset(val_paths, val_labels, is_train=False)\n\nprint('Data preprocessing and creation of training and validation data completed!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:07:58.066649Z","iopub.execute_input":"2025-07-18T08:07:58.067258Z","iopub.status.idle":"2025-07-18T08:07:59.120792Z","shell.execute_reply.started":"2025-07-18T08:07:58.067234Z","shell.execute_reply":"2025-07-18T08:07:59.120033Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## 使用するモデルを定義＆作成する","metadata":{}},{"cell_type":"code","source":"# --- RandomErasingLayer の定義 ---\n# このクラス定義をあなたのスクリプトの適切な場所（例: build_model 関数の前）に配置してください。\nclass RandomErasingLayer(tf.keras.layers.Layer):\n    def __init__(self, probability=0.5, sl=0.02, sh=0.4, r1=0.3, **kwargs):\n        super().__init__(**kwargs)\n        self.probability = probability # 適用確率\n        self.sl = sl # マスク領域の最小比率\n        self.sh = sh # マスク領域の最大比率\n        self.r1 = r1 # マスクのアスペクト比の最小値\n\n    @tf.function # パフォーマンス向上のため\n    def call(self, inputs, training=None):\n        if not training: # 推論時には適用しない\n            return inputs\n\n        batch_size = tf.shape(inputs)[0]\n        img_h = tf.shape(inputs)[1]\n        img_w = tf.shape(inputs)[2]\n        channels = tf.shape(inputs)[3]\n\n        # 各画像に対してRandom Erasingを適用するかどうかを決定\n        apply_erasing = tf.random.uniform(shape=[batch_size]) < self.probability\n\n        # ゼロで埋める値を決定 (例えば、0.0 または入力画像の平均ピクセル値)\n        erase_value = tf.constant(0.0, dtype=inputs.dtype) # 黒で埋める場合 (0-1正規化画像用)\n\n        output_images = []\n        for i in tf.range(batch_size):\n            if apply_erasing[i]:\n                # 領域のサイズとアスペクト比をランダムに決定\n                area = tf.cast(img_h * img_w, tf.float32)\n                target_area = tf.random.uniform(minval=self.sl, maxval=self.sh) * area\n                aspect_ratio = tf.random.uniform(minval=self.r1, maxval=1.0 / self.r1)\n\n                h_erasing = tf.cast(tf.sqrt(target_area * aspect_ratio), tf.int32)\n                w_erasing = tf.cast(tf.sqrt(target_area / aspect_ratio), tf.int32)\n\n                # 領域の位置をランダムに決定\n                x_start = tf.random.uniform(minval=0, maxval=img_w - w_erasing + 1, dtype=tf.int32) # tf.int33 はなく tf.int32 を使用\n                y_start = tf.random.uniform(minval=0, maxval=img_h - h_erasing + 1, dtype=tf.int32) # tf.int33 はなく tf.int32 を使用\n\n                # マスク領域のインデックスを生成\n                indices = tf.stack(tf.meshgrid(\n                    tf.range(y_start, y_start + h_erasing),\n                    tf.range(x_start, x_start + w_erasing),\n                    tf.range(channels),\n                    indexing='ij' # 行、列の順\n                ), axis=-1)\n                indices = tf.reshape(indices, [-1, 3]) # (N, 3)の形式に変換\n\n                # 塗りつぶす値で更新\n                updates = tf.fill(tf.shape(indices)[0], erase_value)\n                \n                erased_image = tf.tensor_scatter_nd_update(inputs[i], indices, updates)\n                output_images.append(erased_image)\n            else:\n                output_images.append(inputs[i])\n        \n        return tf.stack(output_images)\n\n    def get_config(self):\n        config = super().get_config()\n        config.update({\n            \"probability\": self.probability,\n            \"sl\": self.sl,\n            \"sh\": self.sh,\n            \"r1\": self.r1,\n        })\n        return config\n# --- RandomErasingLayer の定義はここまで ---\n\ndef residual_block_2d(x, filters, downsample=False):\n    shortcut = x\n    \n    if downsample:\n        shortcut = layers.Conv2D(filters, kernel_size=1, strides=2, padding='same', use_bias=False)(shortcut)\n        shortcut = layers.BatchNormalization()(shortcut)\n\n    x = layers.Conv2D(filters, kernel_size=3, strides=1 if not downsample else 2, padding='same', use_bias=False)(x)\n    x = layers.BatchNormalization()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.Conv2D(filters, kernel_size=3, strides=1, padding='same', use_bias=False)(x)\n    x = layers.BatchNormalization()(x)\n\n    x = layers.add([x, shortcut])\n    x = layers.ReLU()(x)\n    \n    return x\n\ndef build_model(input_shape=(224, 224, 3)):\n    inputs = tf.keras.Input(shape=input_shape)\n\n    x = RandomErasingLayer(probability=0.5, sl=0.02, sh=0.4, r1=0.3)(inputs) \n    \n    x = layers.Conv2D(64, kernel_size=7, strides=2, padding='same', use_bias=False)(inputs)\n    x = layers.BatchNormalization()(x)\n    x = layers.ReLU()(x)\n\n    x = layers.MaxPooling2D(pool_size=3, strides=2, padding='same')(x)\n\n    x = residual_block_2d(x, 64)\n    x = residual_block_2d(x, 64)\n\n    x = residual_block_2d(x, 128, downsample=True)\n    x = residual_block_2d(x, 128)\n\n    x = residual_block_2d(x, 256, downsample=True)\n    x = residual_block_2d(x, 256)\n\n    x = residual_block_2d(x, 512, downsample=True)\n    x = residual_block_2d(x, 512)\n\n    x = layers.GlobalAveragePooling2D()(x)\n\n    outputs = layers.Dense(1, activation='sigmoid')(x) \n\n    model = models.Model(inputs, outputs)\n    return model\n\ninput_shape = (PicSize, PicSize, 3)\nmodel = build_model(input_shape)\n\n# 最適化とコンパイル\noptimizer = tf.keras.optimizers.Adam(learning_rate=Lr)\nmodel.compile(\n    optimizer=optimizer,\n    loss='binary_crossentropy',\n    metrics=['accuracy']\n)\n\n# モデルの構造を表示\nmodel.summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:08:19.544155Z","iopub.execute_input":"2025-07-18T08:08:19.544432Z","iopub.status.idle":"2025-07-18T08:08:22.430347Z","shell.execute_reply.started":"2025-07-18T08:08:19.544412Z","shell.execute_reply":"2025-07-18T08:08:22.429695Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# EarlyStopping\n# early_stopping = EarlyStopping(\n#     monitor='val_loss',\n#     patience=Patience,  # 10エポック改善がなければ止める\n#     restore_best_weights=True  # 最も良かった時の重みを復元\n# )\n\nhistory = model.fit(\n    Ds_train,\n    validation_data=Ds_val,\n    epochs=Epochs,\n    # callbacks=[early_stopping],\n    verbose=1\n)\n\n# model.save('/kaggle/working/ResNet2D.keras')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:08:29.164191Z","iopub.execute_input":"2025-07-18T08:08:29.164958Z","iopub.status.idle":"2025-07-18T08:10:31.492021Z","shell.execute_reply.started":"2025-07-18T08:08:29.16493Z","shell.execute_reply":"2025-07-18T08:10:31.491456Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_training_history(history):\n    fig, axes = plt.subplots(1, 2, figsize=(14, 5))  # 横長のグラフにする\n\n    # Loss Curve\n    axes[0].plot(history.history['loss'], label='Train Loss', linewidth=2)\n    axes[0].plot(history.history['val_loss'], label='Validation Loss', linewidth=2)\n    axes[0].set_xlabel('Epochs',loc='right')\n    axes[0].set_ylabel('Loss',loc='top')\n    axes[0].set_title('Loss Curve')\n    axes[0].legend(loc='upper right', fontsize=16)\n    axes[0].grid(True)\n\n    # Accuracy Curve\n    axes[1].plot(history.history['accuracy'], label='Train Accuracy', linewidth=2)\n    axes[1].plot(history.history['val_accuracy'], label='Validation Accuracy', linewidth=2)\n    axes[1].set_xlabel('Epochs',loc='right')\n    axes[1].set_ylabel('Accuracy',loc='top')\n    axes[1].set_title('Accuracy Curve')\n    # axes[1].set_ylim(0.9, 1.0)  # Y軸の範囲を制限（0〜1の範囲で見やすく）\n    axes[1].legend(loc='lower right', fontsize=16)\n    axes[1].grid(True)\n\n    plt.tight_layout()  # レイアウト調整\n    plt.show()\n\nplot_training_history(history)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:10:40.886481Z","iopub.execute_input":"2025-07-18T08:10:40.886805Z","iopub.status.idle":"2025-07-18T08:10:41.269277Z","shell.execute_reply.started":"2025-07-18T08:10:40.886783Z","shell.execute_reply":"2025-07-18T08:10:41.268427Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# import numpy as np\n# import tensorflow as tf\n# from tensorflow.keras import layers, models\n# import matplotlib.pyplot as plt\n# import cv2\n\n# 最後の畳み込み層の名前を調べてください（model.summary() で確認）\nlast_conv_layer_name = 'conv2d_19'\n\n# Grad-CAM のヒートマップ生成関数（Keras example に基づく）\ndef make_gradcam_heatmap(img_array, model, last_conv_layer_name, pred_index=None):\n    grad_model = tf.keras.models.Model(\n        [model.inputs],\n        [model.get_layer(last_conv_layer_name).output, model.output]\n    )\n    with tf.GradientTape() as tape:\n        conv_outputs, predictions = grad_model(img_array)\n        if pred_index is None:\n            pred_index = tf.argmax(predictions[0])\n        class_channel = predictions[:, pred_index]\n    grads = tape.gradient(class_channel, conv_outputs)\n    pooled_grads = tf.reduce_mean(grads, axis=(0, 1, 2))\n    conv_outputs = conv_outputs[0]\n    heatmap = conv_outputs @ pooled_grads[..., tf.newaxis]\n    heatmap = tf.squeeze(heatmap)\n    heatmap = tf.maximum(heatmap, 0) / (tf.math.reduce_max(heatmap) + 1e-8)\n    return heatmap.numpy()\n\ndef display_gradcam(img, heatmap, alpha=0.4):\n    # img は 0-255 の uint8 を想定しているため、必要に応じて変換します\n    if img.dtype != np.uint8:\n        img_display = (img * 255).astype(np.uint8)\n    else:\n        img_display = img\n\n    heatmap = cv2.resize(heatmap, (img_display.shape[1], img_display.shape[0]))\n    heatmap = np.uint8(255 * heatmap)\n    jet = plt.colormaps.get_cmap(\"jet\")\n    jet_colors = jet(np.arange(256))[:, :3]\n    jet_heatmap = jet_colors[heatmap][:, :, ::-1]  # BGR\n    jet_heatmap = np.uint8(jet_heatmap * 255)\n    superimposed = cv2.addWeighted(img_display, 1-alpha, jet_heatmap, alpha, 0)\n    plt.figure(figsize=(8, 4))\n    plt.subplot(1,2,1); plt.imshow(img_display); plt.axis('off'); plt.title('Original')\n    plt.subplot(1,2,2); plt.imshow(superimposed); plt.axis('off'); plt.title('Grad-CAM')\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:10:48.197199Z","iopub.execute_input":"2025-07-18T08:10:48.197897Z","iopub.status.idle":"2025-07-18T08:10:48.206141Z","shell.execute_reply.started":"2025-07-18T08:10:48.19787Z","shell.execute_reply":"2025-07-18T08:10:48.205417Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"name='dog'\nnum = '2'\n\n# 画像読み込み・前処理の例\nimg_path = f'/kaggle/working/train/dog/{name}.{num}.jpg'  # ここを実際の画像パスに変更\nimg = tf.keras.preprocessing.image.load_img(img_path, target_size=(PicSize, PicSize))\nimg_array = tf.keras.preprocessing.image.img_to_array(img)\nimg_batch = np.expand_dims(img_array, axis=0) / 255.0\n\n# ヒートマップ作成・表示\nheatmap = make_gradcam_heatmap(img_batch, model, last_conv_layer_name)\ndisplay_gradcam(img_array.astype(np.uint8), heatmap)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:11:06.993796Z","iopub.execute_input":"2025-07-18T08:11:06.99434Z","iopub.status.idle":"2025-07-18T08:11:07.335127Z","shell.execute_reply.started":"2025-07-18T08:11:06.994315Z","shell.execute_reply":"2025-07-18T08:11:07.33448Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"max_loss = -1\nworst_img = None\nworst_label = None\nworst_pred = None\n\n# for x_batch, y_batch in Ds_val:\nfor x in range(50):\n    x_batch, y_batch = Ds_val[x]\n    # モデルで予測\n    preds = model.predict(x_batch, verbose=0)\n\n    # バイナリ交差エントロピー（損失）をサンプル単位で計算\n    batch_losses = binary_crossentropy(tf.reshape(y_batch, (-1, 1)), preds).numpy()\n    \n    for i in range(len(batch_losses)):\n        loss = batch_losses[i]\n        if loss > max_loss:\n            max_loss = loss\n            worst_img = x_batch[i]\n            worst_label = int(y_batch[i]),x\n            worst_pred = preds[i][0]\n\nprint(f\"最大損失: {max_loss:.4f}, ラベル:Batch{worst_label[1]},{worst_label[0]}, 予測: {worst_pred:.4f}\")\n\n# 最も悪かった画像を表示\nplt.imshow(worst_img)\nplt.title(f\"Loss: {max_loss:.4f} | Label: Batch {worst_label[1]},{worst_label[0]} | Pred: {worst_pred:.4f}\")\nplt.axis('off')\nplt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-11T11:12:49.345885Z","iopub.execute_input":"2025-07-11T11:12:49.346152Z","iopub.status.idle":"2025-07-11T11:12:57.905186Z","shell.execute_reply.started":"2025-07-11T11:12:49.346133Z","shell.execute_reply":"2025-07-11T11:12:57.904338Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_batch_for_gradcam = np.expand_dims(worst_img, axis=0)\n\n# Grad-CAM ヒートマップの作成\nheatmap = make_gradcam_heatmap(img_batch_for_gradcam, model, last_conv_layer_name)\ndisplay_gradcam(worst_img, heatmap)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:29:34.562372Z","iopub.execute_input":"2025-07-18T08:29:34.562674Z","iopub.status.idle":"2025-07-18T08:29:34.853662Z","shell.execute_reply.started":"2025-07-18T08:29:34.562653Z","shell.execute_reply":"2025-07-18T08:29:34.852633Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## テストデータを予測する","metadata":{}},{"cell_type":"code","source":"\n\n# テスト画像の準備\ntest_dir = '/kaggle/working/test'\ntest_files = sorted([f for f in os.listdir(test_dir) if f.endswith('.jpg')])\ntest_paths = [os.path.join(test_dir, f) for f in test_files]\nids = [int(f.split('.')[0]) for f in test_files]\n\nclass TestImageGenerator(Sequence):\n    def __init__(self, file_paths, batch_size=BatchSize, target_size=(PicSize, PicSize)):\n        self.file_paths = file_paths\n        self.batch_size = batch_size\n        self.target_size = target_size\n\n    def __len__(self):\n        return int(np.ceil(len(self.file_paths) / self.batch_size))\n\n    def __getitem__(self, idx):\n        batch_files = self.file_paths[idx * self.batch_size : (idx + 1) * self.batch_size]\n        batch_images = []\n        for path in batch_files:\n            img = load_img(path, target_size=self.target_size)\n            img_array = img_to_array(img) / 255.0\n            batch_images.append(img_array)\n        return np.array(batch_images)\n        \nDs_test = TestImageGenerator(test_paths, batch_size=BatchSize)\n\n# # 前処理関数（ラベル不要なので）\n# def decode_test_img(path):\n#     img = tf.io.read_file(path)\n#     img = tf.image.decode_jpeg(img, channels=3)\n#     img = tf.image.resize(img, (PicSize, PicSize))\n#     img = img / 255.0\n#     return img\n\n# # tf.data.Dataset 作成\n# Ds_test = tf.data.Dataset.from_tensor_slices(test_paths)\n# Ds_test = test_ds.map(decode_test_img, num_parallel_calls=tf.data.AUTOTUNE)\n# Ds_test = test_ds.batch(BatchSize).prefetch(tf.data.AUTOTUNE)\n\n# 予測実行（確率出力）\npred_probs = model.predict(Ds_test).flatten()\n\n# 提出用CSV\nsubmission = pd.DataFrame({'id': ids, 'label': pred_probs})\nsubmission = submission.sort_values('id')\nsubmission.to_csv('submission.csv', index=False)\n# print(submission)\nprint('Done!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-07-18T08:31:14.948264Z","iopub.execute_input":"2025-07-18T08:31:14.948955Z","iopub.status.idle":"2025-07-18T08:31:37.618298Z","shell.execute_reply.started":"2025-07-18T08:31:14.948923Z","shell.execute_reply":"2025-07-18T08:31:37.617303Z"}},"outputs":[],"execution_count":null}]}