{"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":[{"sourceType":"competition","sourceId":113558,"databundleVersionId":14878066,"isSourceIdPinned":false},{"sourceType":"datasetVersion","sourceId":15072966,"datasetId":8681584,"databundleVersionId":15955253}],"dockerImageVersionId":31192,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# 1. Import packages and setup environment (CPU/GPU/TPU)","metadata":{}},{"cell_type":"code","source":"## For TPU environment (install missing packages / reinstall tensorflow to solve NaN topic during training / restart kernel)\n\nimport IPython\nimport tensorflow as tf\nIPython.display.clear_output() # Workaround for error messages leading to Failed notebook\n\nif len(tf.config.experimental.list_logical_devices('TPU')) > 0:\n    !pip install -q tensorflow-tpu -f https://storage.googleapis.com/libtpu-tf-releases/index.html --force-reinstall\n    !pip install -q pydot\n    !pip install -q -U keras-tuner\n    !pip install -q polars\n    !pip install -q protobuf==5.29.5 # to solve tuner compatibility issue\n    !pip install vit_keras\n    !pip install -q natsort\n    IPython.Application.instance().kernel.do_shutdown(True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:25.974872Z","iopub.execute_input":"2026-03-08T09:42:25.975479Z","iopub.status.idle":"2026-03-08T09:42:47.391155Z","shell.execute_reply.started":"2026-03-08T09:42:25.975452Z","shell.execute_reply":"2026-03-08T09:42:47.390376Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Import packages\n\n# General purpose modules\nimport os\nimport math\nfrom tqdm import tqdm\nimport time\nfrom pathlib import Path\nfrom natsort import natsorted\nimport cv2\n\n# Data handling and visualization modules\nimport json\nimport numpy as np\nimport pandas as pd\nimport polars as pl\nimport matplotlib.pyplot as plt\nfrom matplotlib.pyplot import imshow\n\n# Skikit-learn preprocessing modules\nfrom sklearn.model_selection import StratifiedKFold\n\n# Tensorflow modules\nimport tensorflow as tf\nfrom tensorflow.keras import backend as K\nimport keras_tuner as kt\nfrom vit_keras import vit, utils, visualize\nprint('Tensorflow version: '+ tf.__version__)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:47.392397Z","iopub.execute_input":"2026-03-08T09:42:47.393190Z","iopub.status.idle":"2026-03-08T09:42:48.629666Z","shell.execute_reply.started":"2026-03-08T09:42:47.393170Z","shell.execute_reply":"2026-03-08T09:42:48.629018Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Detect hardware (CPU/GPU/TPU), setup environment and return appropriate distribution strategy\n\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver.connect(tpu='local') # set tpu is local as it should be available in the VM\n    print('✅ Running on TPU ', tpu.master())\nexcept:\n    print('❌ Using CPU/GPU')\n    tpu = None\n\nif tpu:\n    strategy = tf.distribute.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy() # default distribution strategy in Tensorflow. Works on CPU and single GPU.\n\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:48.630372Z","iopub.execute_input":"2026-03-08T09:42:48.630702Z","iopub.status.idle":"2026-03-08T09:42:48.854710Z","shell.execute_reply.started":"2026-03-08T09:42:48.630681Z","shell.execute_reply":"2026-03-08T09:42:48.854071Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. Load and explore data","metadata":{}},{"cell_type":"code","source":"## Preprocessing functions\n\nimage_size = 224 # input image size fo neural network model\nCLASSES = {0 : 'autentic', 1: 'forged'}\n\n# Zoom/rotate/translate images and adjust coordinates accordingly\ndef image_augmentation(image, augmentation=True):\n    if augmentation:\n        zoom_fac = np.random.uniform(0.0, 0.0)\n        rot_fac = np.random.uniform(-0.1, 0.1)\n        trans_fac = np.random.uniform(-0.05, 0.05)\n        z = tf.keras.layers.RandomZoom(height_factor=(zoom_fac, zoom_fac), fill_mode='constant', name='auglay1')(image)\n        z = tf.keras.layers.RandomRotation(factor=(rot_fac, rot_fac), fill_mode='constant', name='auglay2')(z)\n        image = tf.keras.layers.RandomTranslation(height_factor=(trans_fac, trans_fac), width_factor=(trans_fac, trans_fac),\n                                                  interpolation='nearest', fill_mode='constant', name='auglay3')(z)\n    return image\n\n# Preprocess image or masking (Padding and resizing to image_size x image_size x channel)\ndef preprocess_images(image, augmentation=False):\n    image_scaled = image.astype(dtype=np.float32)/255\n    image_aug = image_augmentation(image_scaled, augmentation=augmentation)\n    image_resized =tf.image.resize(image_aug, [image_size, image_size])\n    image_resized = tf.cast(image_resized*255, dtype=tf.uint8)\n    return image_resized\n\n# Postprocess image or masking (Resizing and croping to original image/mask size)\ndef postprocess_images(image, orig_image):\n    image_scaled = tf.cast(image, dtype=np.float32)/255\n    image_size_rows, image_size_cols, _ = orig_image.shape\n    image_resized = tf.image.resize(image_scaled, [image_size_rows, image_size_cols])\n    image_rescaled = tf.cast(image_resized*255, dtype=tf.uint8)\n    return image_rescaled","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:48.856139Z","iopub.execute_input":"2026-03-08T09:42:48.856388Z","iopub.status.idle":"2026-03-08T09:42:48.863436Z","shell.execute_reply.started":"2026-03-08T09:42:48.856370Z","shell.execute_reply":"2026-03-08T09:42:48.862607Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Load and preprocess images\nSUBMISSIONING = True\n\nfolder_path_au = Path(\"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/authentic\")\nfolder_path_fo = Path(\"/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged\")\n\ndef load_images(folder_path, nr_images):\n    images = []\n    labels = []\n    for file_path in tqdm(folder_path.glob(\"*.png\"), total=nr_images):\n        image = cv2.cvtColor(cv2.imread(str(file_path)), cv2.COLOR_BGR2RGB)\n        if 'authentic' in str(folder_path):\n            label = np.zeros((image.shape[0], image.shape[1], 1))\n        else:\n            mask_filename = str(file_path).split('/')[-1].replace('.png', '.npy')\n            mask_raw = np.load('/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks/' + mask_filename)\n            label = np.transpose(mask_raw, axes=[1,2,0])\n            label = np.sum(label, axis=2, keepdims=True)\n        if image is not None:\n            image = preprocess_images(image, False)\n            label = preprocess_images(label, False)\n            images.append(image)\n            labels.append(label)\n    images = np.stack(images, axis=0)\n    labels = np.stack(labels, axis=0)\n    return images, labels\n\ndef load_dataset():\n    images_au, labels_au = load_images(folder_path_au, 2377)\n    images_fo, labels_fo = load_images(folder_path_fo, 2751)\n    images = np.concatenate((images_au, images_fo), axis=0)\n    labels = np.concatenate((labels_au, labels_fo), axis=0)\n    return images, labels\n\nif not SUBMISSIONING:\n    trainval_images, trainval_labels = load_dataset()\nelse: # Dummy data for speeding up submission\n    trainval_images = np.ones((640,image_size,image_size,3))\n    trainval_labels = np.zeros((640,image_size,image_size,1))","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:48.864092Z","iopub.execute_input":"2026-03-08T09:42:48.864406Z","iopub.status.idle":"2026-03-08T09:42:49.051525Z","shell.execute_reply.started":"2026-03-08T09:42:48.864389Z","shell.execute_reply":"2026-03-08T09:42:49.050873Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Spliting trainval data into train and validation data with StratifiedKFold\n\nskf = StratifiedKFold(n_splits=10, shuffle=True, random_state=42)\nbool_labels = (trainval_labels.sum(axis=(1,2,3))>0).astype(dtype=np.float32)\n\nfor fold, (train_idx, val_idx) in enumerate(skf.split(trainval_images, y=bool_labels)):\n    train_images, val_images = trainval_images[train_idx], trainval_images[val_idx]\n    train_labels, val_labels = trainval_labels[train_idx], trainval_labels[val_idx]\n    print(f\"✅ Fold {fold}: Train size = {len(train_idx)}, Val size = {len(val_idx)}\")\n    break  # Use only the first fold for now","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:49.052387Z","iopub.execute_input":"2026-03-08T09:42:49.052637Z","iopub.status.idle":"2026-03-08T09:42:49.428827Z","shell.execute_reply.started":"2026-03-08T09:42:49.052614Z","shell.execute_reply":"2026-03-08T09:42:49.428176Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Zoom/rotate/translate images and adjust labels accordingly\n\nzoom_fac = 0.1\nrot_fac = 0.1\ntrans_fac = 0.05\nrandom_zoom = tf.keras.layers.RandomZoom(height_factor=(-zoom_fac, zoom_fac), width_factor=(-zoom_fac, zoom_fac),\n                                         fill_mode='constant', name='auglay1')\nrandom_rotation = tf.keras.layers.RandomRotation(factor=(-rot_fac, rot_fac), fill_mode='constant', name='auglay2')\nrandom_trans = tf.keras.layers.RandomTranslation(height_factor=(-trans_fac, trans_fac), width_factor=(-trans_fac, trans_fac),\n                                                 interpolation='nearest', fill_mode='constant', name='auglay3')\n\ndef image_augmentation_ds(image, label, augmentation=True):\n    if augmentation:\n        merged = tf.concat([image, label], axis=-1)\n        merged = tf.cast(merged, tf.float32) / 255.0\n        \n        merged = random_zoom(merged)\n        merged = random_rotation(merged)\n        merged = random_trans(merged)\n        \n        merged = tf.cast(merged*255.0, dtype=tf.uint8)\n        image = tf.gather(merged, indices=[0,1,2], axis=-1)\n        label = tf.gather(merged, indices=[3], axis=-1)\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:49.429538Z","iopub.execute_input":"2026-03-08T09:42:49.429776Z","iopub.status.idle":"2026-03-08T09:42:49.466451Z","shell.execute_reply.started":"2026-03-08T09:42:49.429749Z","shell.execute_reply":"2026-03-08T09:42:49.465719Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Create train and validation datasets\n\nSEED=42\nbatch_size=256\nbatch_size_val=256\n\ntrain_ds = tf.data.Dataset.from_tensor_slices((train_images, train_labels))\ntrain_ds = train_ds.shuffle(len(train_labels), seed=SEED).batch(batch_size, drop_remainder=True).map(image_augmentation_ds,\n                num_parallel_calls=tf.data.AUTOTUNE).cache().repeat().prefetch(tf.data.AUTOTUNE)\nval_ds = tf.data.Dataset.from_tensor_slices((val_images, val_labels))\nval_ds = val_ds.batch(batch_size_val, drop_remainder=True).cache().prefetch(tf.data.AUTOTUNE)\n\nprint('Size of train dataset: '+ str(len(train_labels)))\nprint('Number of batches in train dataset: '+ f'{len(train_labels)//batch_size}')\nprint('Size of validation dataset: '+ str(len(val_labels)))\nprint('Number of batches in val dataset: '+ f'{len(val_labels)//batch_size_val}')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:49.467085Z","iopub.execute_input":"2026-03-08T09:42:49.467244Z","iopub.status.idle":"2026-03-08T09:42:53.769428Z","shell.execute_reply.started":"2026-03-08T09:42:49.467231Z","shell.execute_reply":"2026-03-08T09:42:53.768838Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Check train dataset batch dimensions\n\nfor X, y in train_ds.take(1):\n    print(X.shape)\n    print(y.shape)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:53.770126Z","iopub.execute_input":"2026-03-08T09:42:53.770411Z","iopub.status.idle":"2026-03-08T09:42:58.984908Z","shell.execute_reply.started":"2026-03-08T09:42:53.770393Z","shell.execute_reply":"2026-03-08T09:42:58.984320Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Check image augmentation\n\nimage_idx = 0\nX_aug, y_aug = image_augmentation_ds(X[image_idx], y[image_idx])\n\nfig, axes = plt.subplots(1, 2, figsize=(8, 4))\naxes[0].imshow(X_aug)\naxes[0].set_title(\"Augmented image\")\naxes[1].imshow(y_aug)\naxes[1].set_title(\"Augmented label\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:42:58.987006Z","iopub.execute_input":"2026-03-08T09:42:58.987261Z","iopub.status.idle":"2026-03-08T09:43:00.334626Z","shell.execute_reply.started":"2026-03-08T09:42:58.987245Z","shell.execute_reply":"2026-03-08T09:43:00.333875Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Explore Data","metadata":{}},{"cell_type":"code","source":"## Visualize data\n\ntrain_ds_vis = train_ds.unbatch() #.shuffle(2048,seed=43)\nnum_examples = 36\nnum_columns = 6\nnum_rows = math.ceil(num_examples/num_columns)\nplt.figure(figsize=(16, 16))\nfor i, (image, label) in enumerate(train_ds_vis.take(num_examples)):\n    if i == -1: # Set to 0 in case of interest\n        print(image.shape)\n        print('class id: '+str(label.numpy()))\n        print('class name: '+str(CLASSES[label.numpy()]))\n    bool_label = (label.numpy().sum()>0).astype(dtype=np.float32)\n    class_id = str(bool_label)\n    class_name = str(CLASSES[bool_label])\n    plt.subplot(num_rows, num_columns, i + 1)\n    plt.imshow(image)\n    if bool_label:\n        mask = np.ma.masked_where(label == 0, label*255)\n        plt.imshow(mask, cmap='Set1', alpha=0.5)\n    plt.title(f\"{class_name}({class_id})\", fontsize=10)\n    plt.suptitle(\"Examples from train dataset\")\n    plt.xticks([])\n    plt.yticks([])","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:00.335398Z","iopub.execute_input":"2026-03-08T09:43:00.335663Z","iopub.status.idle":"2026-03-08T09:43:02.595244Z","shell.execute_reply.started":"2026-03-08T09:43:00.335637Z","shell.execute_reply":"2026-03-08T09:43:02.594597Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. Build and explore neural network","metadata":{}},{"cell_type":"code","source":"## Custom F1 class to handle imbalanced label classes\n\ndef f1_score(y_true, y_pred):\n    y_pred = tf.round(y_pred)  # Round predictions to 0 or 1\n    tp = K.sum(K.cast(y_true * y_pred, 'float'), axis=0)  # True positives\n    fp = K.sum(K.cast((1 - y_true) * y_pred, 'float'), axis=0)  # False positives\n    fn = K.sum(K.cast(y_true * (1 - y_pred), 'float'), axis=0)  # False negatives\n\n    precision = tp / (tp + fp + K.epsilon())  # Precision calculation\n    recall = tp / (tp + fn + K.epsilon())  # Recall calculation\n\n    f1 = 2 * precision * recall / (precision + recall + K.epsilon())  # F1 score\n    return K.mean(f1)\n\n@tf.keras.utils.register_keras_serializable()\nclass CustomF1(tf.keras.metrics.Metric):\n    def __init__(self, name='cf1_score', **kwargs):\n        super().__init__(name=name, **kwargs)\n        self.f1_score_fn = f1_score\n        self.total = self.add_weight(shape=(), name=\"total\", initializer=\"zeros\")\n        self.count = self.add_weight(shape=(), name=\"count\", initializer=\"zeros\")\n    def update_state(self, y_true, y_pred, sample_weight=None):\n        y_true = tf.cast(tf.reshape(y_true, shape=(y_true.shape[0], -1)), dtype=tf.float32)\n        y_pred = tf.reshape(y_pred, shape=(y_pred.shape[0], -1))\n        metric = self.f1_score_fn(y_true, y_pred)\n        self.total.assign_add(metric)\n        self.count.assign_add(tf.cast(1, tf.float32))\n    def result(self):\n        return self.total / self.count","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:02.595939Z","iopub.execute_input":"2026-03-08T09:43:02.596187Z","iopub.status.idle":"2026-03-08T09:43:02.603974Z","shell.execute_reply.started":"2026-03-08T09:43:02.596164Z","shell.execute_reply":"2026-03-08T09:43:02.603226Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Weighted BCE function to handle imbalanced label classes\n\ntrue_freq = train_labels.sum()/np.size(train_labels)\n\n@tf.keras.utils.register_keras_serializable()\ndef weighted_binary_crossentropy(y_true, y_pred, zero_weight=true_freq, one_weight=1-true_freq):\n    y_true = tf.cast(y_true, dtype=tf.float32)\n    y_pred = tf.cast(y_pred, dtype=tf.float32)\n    # Clip predictions to avoid log(0)\n    epsilon = K.epsilon()\n    y_pred = K.clip(y_pred, epsilon, 1 - epsilon)\n    \n    # Compute binary cross-entropy\n    bce = -(y_true * K.log(y_pred) + (1 - y_true) * K.log(1 - y_pred))\n    \n    # Apply weights\n    weights = y_true * one_weight + (1 - y_true) * zero_weight\n    weighted_bce = weights * bce\n    return K.mean(weighted_bce)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:02.604766Z","iopub.execute_input":"2026-03-08T09:43:02.605026Z","iopub.status.idle":"2026-03-08T09:43:02.692090Z","shell.execute_reply.started":"2026-03-08T09:43:02.605002Z","shell.execute_reply":"2026-03-08T09:43:02.691343Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Model layers\n\n# Custom layer for cast and rescale images from 0-255 uint8 to 0-1 float32\n@tf.keras.utils.register_keras_serializable()\nclass Rescale(tf.keras.layers.Layer):\n    def __init__(self, **kwargs):\n        super(Rescale, self).__init__(**kwargs)\n    def call(self, inputs):\n        x = tf.cast(inputs, tf.float32)/255\n        return x\n\n@tf.keras.utils.register_keras_serializable()\nclass PreProcess(tf.keras.layers.Layer):\n    def __init__(self, name, **kwargs):\n        super(PreProcess, self).__init__(**kwargs)\n        self.name = name\n        self.preprocess_input = vit.preprocess_inputs\n    def call(self, inputs):\n        return self.preprocess_input(inputs)\n\n@tf.keras.utils.register_keras_serializable()\nclass NormLayer(tf.keras.layers.Layer):\n    \"\"\"Custom TensorFlow layer that normalizes inputs using tf.norm. Supports L1, L2, or other p-norms.\"\"\"\n    def __init__(self, ord='euclidean', axis=-1, epsilon=1e-7, **kwargs):\n        super(NormLayer, self).__init__(**kwargs)\n        self.ord = ord\n        self.axis = axis\n        self.epsilon = epsilon\n    def call(self, inputs):\n        norm = tf.norm(inputs, ord=self.ord, axis=self.axis, keepdims=True)\n        norm = tf.maximum(norm, self.epsilon)\n        return inputs / norm, norm\n\n# Downsampling block\ndef downsample(input, filters, size, do=0.2, name=None):\n    x = tf.keras.layers.Conv2D(filters, size, strides=1, padding='same', kernel_initializer='he_normal', name=name+'_conv2d')(input)\n    x = tf.keras.layers.BatchNormalization(name=name+'_bn')(x)\n    x = tf.keras.layers.Dropout(do, name=name+'_do')(x)\n    x = tf.keras.layers.MaxPool2D(pool_size=(2, 2), name=name+'_mp2d')(x)\n    return x\n\n# Upsampling block\ndef upsample(filters, size, do=0.2, name=None):\n    x = tf.keras.layers.Conv2DTranspose(filters, size, activation='relu', strides=2, padding='same',\n                                        kernel_initializer='he_normal', name=name+'_conv2dt')(input)\n    x = tf.keras.layers.BatchNormalization(name=name+'_bn')(x)\n    out = tf.keras.layers.Dropout(do, name=name+'_do')(x)\n    #model = tf.keras.Model(inputs=input, outputs=out, name=name)\n    return out #model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:02.692874Z","iopub.execute_input":"2026-03-08T09:43:02.693494Z","iopub.status.idle":"2026-03-08T09:43:02.703085Z","shell.execute_reply.started":"2026-03-08T09:43:02.693474Z","shell.execute_reply":"2026-03-08T09:43:02.702513Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def upsample(filters, size, norm_type='batchnorm', apply_dropout=False, name=None):\n  \"\"\" Upsamples an input: Conv2DTranspose => Batchnorm => Dropout => Relu\n      Args:\n        filters: number of filters\n        size: filter size\n        norm_type: Normalization type; either 'batchnorm' or 'instancenorm'.\n        apply_dropout: If True, adds the dropout layer\n    \n      Returns:\n        Upsample Sequential Model\"\"\"\n\n  initializer = tf.random_normal_initializer(0., 0.02)\n  result = tf.keras.Sequential(name=name)\n  result.add(\n      tf.keras.layers.Conv2DTranspose(filters, size, strides=2,\n                                      padding='same',\n                                      kernel_initializer=initializer,\n                                      use_bias=False))\n\n  if norm_type.lower() == 'batchnorm':\n    result.add(tf.keras.layers.BatchNormalization())\n  elif norm_type.lower() == 'instancenorm':\n    result.add(InstanceNormalization())\n\n  if apply_dropout:\n    result.add(tf.keras.layers.Dropout(0.5))\n\n  result.add(tf.keras.layers.ReLU())\n  return result","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:11.894488Z","iopub.execute_input":"2026-03-08T09:43:11.894788Z","iopub.status.idle":"2026-03-08T09:43:11.900236Z","shell.execute_reply.started":"2026-03-08T09:43:11.894770Z","shell.execute_reply":"2026-03-08T09:43:11.899618Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Transformer architecture elements\n\nclass PositionalEncoding(tf.keras.layers.Layer):\n    def __init__(self, max_length, embed_size, dtype=tf.float32, **kwargs):\n        super().__init__(dtype=dtype, **kwargs)\n        assert embed_size % 2 == 0, \"embed_size must be even\"\n        p, i = np.meshgrid(np.arange(max_length),\n                           2 * np.arange(embed_size // 2))\n        pos_emb = np.empty((1, max_length, embed_size))\n        pos_emb[0, :, ::2] = np.sin(p / 10_000 ** (i / embed_size)).T\n        pos_emb[0, :, 1::2] = np.cos(p / 10_000 ** (i / embed_size)).T\n        self.pos_encodings = tf.constant(pos_emb.astype(self.dtype))\n        self.supports_masking = True\n\n    def call(self, inputs):\n        batch_max_length = tf.shape(inputs)[1]\n        return inputs + self.pos_encodings[:, :batch_max_length]\n\ndef attention_encoder(encoder_in, embed_size, N=2, num_heads=8, dropout_rate = 0.1, n_units = 256):\n    Z = encoder_in\n    for _ in range(N):\n        skip = Z\n        Z = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate)(Z, value=Z)\n        Z = tf.keras.layers.LayerNormalization()(tf.keras.layers.Add()([Z, skip]))\n        skip = Z\n        Z = tf.keras.layers.Dense(n_units, activation=\"relu\")(Z)\n        Z = tf.keras.layers.Dense(embed_size)(Z)\n        Z = tf.keras.layers.Dropout(dropout_rate)(Z)\n        Z = tf.keras.layers.LayerNormalization()(tf.keras.layers.Add()([Z, skip]))\n    return Z\n\ndef attention_decoder(decoder_in, encoder_out, embed_size, N=2, num_heads=8, dropout_rate = 0.1, n_units = 256):\n    encoder_outputs = encoder_out \n    Z = decoder_in  # the decoder starts with its own inputs\n    for _ in range(N):\n        skip = Z\n        Z = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate)(Z, value=Z)\n        Z = tf.keras.layers.LayerNormalization()(tf.keras.layers.Add()([Z, skip]))\n        skip = Z\n        Z = tf.keras.layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_size, dropout=dropout_rate)(Z, value=encoder_outputs, attention_mask=encoder_pad_mask)\n        Z = tf.keras.layers.LayerNormalization()(tf.keras.layers.Add()([Z, skip]))\n        skip = Z\n        Z = tf.keras.layers.Dense(n_units, activation=\"relu\")(Z)\n        Z = tf.keras.layers.Dense(embed_size)(Z)\n        Z = tf.keras.layers.LayerNormalization()(tf.keras.layers.Add()([Z, skip]))\n    return Z","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:15.632848Z","iopub.execute_input":"2026-03-08T09:43:15.633793Z","iopub.status.idle":"2026-03-08T09:43:15.645095Z","shell.execute_reply.started":"2026-03-08T09:43:15.633764Z","shell.execute_reply":"2026-03-08T09:43:15.644356Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# base_model = tf.keras.applications.EfficientNetV2B0(include_top=False, include_preprocessing=True,  input_shape=[image_size, image_size, 3],\n#                                                pooling=None, weights='imagenet')\n# #base_model = tf.keras.applications.EfficientNetV2B3(include_top=False, include_preprocessing=True,  input_shape=[image_size, image_size, 3],\n# #                                               pooling=None, weights='imagenet')\n\n# #base_model = tf.keras.Model(inputs=base_model.input, outputs=base_model.layers[9].output, name='ConvNeXtBase')\n# base_model.summary(line_length=110)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:22.121791Z","iopub.execute_input":"2026-03-08T09:43:22.122033Z","iopub.status.idle":"2026-03-08T09:43:22.125819Z","shell.execute_reply.started":"2026-03-08T09:43:22.122019Z","shell.execute_reply":"2026-03-08T09:43:22.125082Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Build UNet Architecture with pre-trained EfficientNet encoder and pix2pix decoder\n\ndef build_network(hp):\n    # define the sets of inputs\n    input_img = tf.keras.Input(shape=(image_size, image_size, 3), name='input_img')\n\n    # Cast, rescale and preprocess image tensors\n    #x = Rescale(name='rescaling')(input_img)\n\n    # Load a pretrained Vision Transformer (ViT-B16, ImageNet-1k)\n    base_model = tf.keras.applications.EfficientNetV2B0(include_top=False, include_preprocessing=True,  input_shape=[image_size, image_size, 3],\n                                               pooling=None, weights='imagenet')\n    #base_model = tf.keras.Model(inputs=base_model.input, outputs=base_model.layers[9].output, name='ConvNeXtBase')\n    base_model.trainable = False\n\n    # Create the feature extraction model\n    layer_names = ['block1a_project_bn',   # 112x112x16\n                   'block2b_add',          # 56x56x24\n                   'block3b_add',          # 28x28x40\n                   'block5c_add',          # 14x14x112\n                   #'block6h_add',          # 7x7x192\n                   'top_activation',       # 7x7x1280\n                   ]\n    base_model_outputs = [base_model.get_layer(name).output for name in layer_names]\n    down_stack = tf.keras.Model(inputs=base_model.input, outputs=base_model_outputs, name='downsampling')\n    \n    # Create the upsampling model\n    up_stack = [upsample(112, 3, name='upsampling_block1'),  # 7x7 -> 14x14\n                upsample(56, 3, name='upsampling_block2'),  # 14x14 -> 28x28\n                upsample(28, 3, name='upsampling_block3'),  # 28x28 -> 56x56\n                upsample(14, 3, name='upsampling_block4'),   # 56x56 -> 112x112\n               ]\n\n    # Downsampling through the model\n    skips = down_stack(input_img)\n    x = skips[-1]\n    skips = reversed(skips[:-1])\n\n    # Self attention\n    orig_shape = x.shape\n    x = tf.keras.layers.Reshape((-1, orig_shape[-1]), name=f'reshape_preAtt')(x)\n    x_normed, norm = NormLayer()(x)\n    #x = tf.keras.layers.Attention()([x_normed, x, x_normed]) #np.ones((1,4096,1))\n    x = tf.keras.layers.MultiHeadAttention(num_heads=1, key_dim=orig_shape[-1], dropout=0.1)(x_normed, x, x_normed)\n    x = tf.keras.layers.Reshape((orig_shape[-3], orig_shape[-2], orig_shape[-1]), name=f'reshape_postAtt')(x)\n    \n    # Upsampling and establishing the skip connections\n    for i, (up, skip) in enumerate(zip(up_stack, skips)):\n        x = up(x)\n        x = tf.keras.layers.Concatenate(name=f'upsampling_concat{i+1}')([x, skip])\n\n    # This is the last layer of the model\n    out = tf.keras.layers.Conv2DTranspose(filters=1, activation='sigmoid', kernel_size=3,\n                                          strides=2, padding='same', name='conv2dtrans')(x) # 122x122 -> 244x244\n    \n    # define model\n    model = tf.keras.Model(inputs=input_img, outputs=out, name='LUC_UNET')\n\n    # define optimizer/loss and compile model\n    lr_tune = 1e-3 #hp.Float(name='learning_rate', min_value=1e-4, max_value=1e-2, sampling='log', default=1e-3)\n    optimizer = tf.keras.optimizers.AdamW(lr_tune)\n    loss = weighted_binary_crossentropy\n    metrics = CustomF1()\n    model.compile(optimizer=optimizer, loss=loss, metrics=[metrics], run_eagerly=False)\n    return model\n\nif not SUBMISSIONING:\n    with strategy.scope():\n        model = build_network(kt.HyperParameters())\nelse:\n    # Load pre-trained model for submission\n    model = tf.keras.models.load_model('/kaggle/input/luc-1xx/luc_1_3_0.h5')\n    print('Model weights have been loaded!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:35.546512Z","iopub.execute_input":"2026-03-08T09:43:35.547218Z","iopub.status.idle":"2026-03-08T09:43:37.818684Z","shell.execute_reply.started":"2026-03-08T09:43:35.547194Z","shell.execute_reply":"2026-03-08T09:43:37.817929Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Explore model architecture\n\nmodel.summary(line_length=110)\n# tf.keras.utils.plot_model(model, to_file='model_architecture.png', show_shapes=True, show_dtype=False,\n#                           show_layer_names=True, show_layer_activations=True, show_trainable=False)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:40.886116Z","iopub.execute_input":"2026-03-08T09:43:40.886952Z","iopub.status.idle":"2026-03-08T09:43:40.918188Z","shell.execute_reply.started":"2026-03-08T09:43:40.886920Z","shell.execute_reply":"2026-03-08T09:43:40.917655Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 5. Training","metadata":{}},{"cell_type":"code","source":"## Training parameters\n\nepochs = 100\nsteps_per_epoch = len(train_labels)//batch_size * 10\nTUNING = False and not SUBMISSIONING\nTRAINING = True and not SUBMISSIONING\nFINETUNING = False and not SUBMISSIONING","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:47.408402Z","iopub.execute_input":"2026-03-08T09:43:47.408682Z","iopub.status.idle":"2026-03-08T09:43:47.412619Z","shell.execute_reply.started":"2026-03-08T09:43:47.408662Z","shell.execute_reply":"2026-03-08T09:43:47.412062Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Tuner configurations\n\nif TUNING:\n    i_TunerTyp = 1 # Choose desired tuner type: {1: 'grid', 2: 'random', 3: 'hyper'}\n    TunerStr = {1: 'grid', 2: 'random', 3: 'hyper'}\n    \n    tuner_grid = kt.GridSearch(hypermodel=build_network, objective=kt.Objective(\"val_cf1_score\", direction=\"max\"),\n                               max_trials=15, max_consecutive_failed_trials=1,\n                               overwrite=True, directory=\"tuner\", project_name=\"LUC\", distribution_strategy = strategy)\n    \n    tuner_random = kt.RandomSearch(hypermodel=build_network, objective=kt.Objective(\"val_cf1_score\", direction=\"max\"),\n                                   max_trials=10, executions_per_trial=1,\n                                   overwrite=True, directory=\"tuner\", project_name=\"LUC\", distribution_strategy = strategy)\n    \n    tuner_hyper = kt.Hyperband(hypermodel=build_network, objective=kt.Objective(\"val_cf1_score\", direction=\"max\"),\n                               max_epochs=60, factor=4, hyperband_iterations=1,\n                               overwrite=True, directory=\"tuner\", project_name=\"LUC\", distribution_strategy = strategy)\n    \n    tuner = globals()[f'tuner_{TunerStr[i_TunerTyp]}']\n    tuner.search_space_summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:49.086997Z","iopub.execute_input":"2026-03-08T09:43:49.087609Z","iopub.status.idle":"2026-03-08T09:43:49.092553Z","shell.execute_reply.started":"2026-03-08T09:43:49.087588Z","shell.execute_reply":"2026-03-08T09:43:49.091948Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Train or tune model\n\n# Callback functions\nlr_scheduler = tf.keras.callbacks.ReduceLROnPlateau(factor=0.2, patience=10, verbose=1, monitor='val_cf1_score', mode='max')\nearly_stopping_cb = tf.keras.callbacks.EarlyStopping(patience=20, verbose=1, monitor='val_cf1_score', mode='max', restore_best_weights=True)\nlr_schedule = tf.keras.callbacks.LearningRateScheduler(lambda epoch: 1e-5 * 10**(epoch / 10)) # Find starting learning\n\n# Training\nif TRAINING or FINETUNING:\n    history = model.fit(train_ds, validation_data=val_ds, epochs=epochs, steps_per_epoch=steps_per_epoch,\n                        callbacks=[lr_scheduler, early_stopping_cb])\n\n# Tuning\nif TUNING:\n    tuner.search(train_ds, validation_data=val_ds, epochs=epochs, steps_per_epoch=steps_per_epoch,\n                 callbacks=[lr_scheduler, early_stopping_cb])\n    best_models = tuner.get_best_models(num_models=2)\n    model = best_models[0]\n    model.summary()\n    tuner.results_summary()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:50.894059Z","iopub.execute_input":"2026-03-08T09:43:50.894767Z","iopub.status.idle":"2026-03-08T09:43:50.899935Z","shell.execute_reply.started":"2026-03-08T09:43:50.894743Z","shell.execute_reply":"2026-03-08T09:43:50.899358Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Save weights of model after training/tuning/finetuning\n\nif TRAINING or TUNING or FINETUNING:\n    model.save('luc_1_4_0.h5', include_optimizer=False)\n    print('Model weights have been saved!')","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:52.996153Z","iopub.execute_input":"2026-03-08T09:43:52.996588Z","iopub.status.idle":"2026-03-08T09:43:53.001619Z","shell.execute_reply.started":"2026-03-08T09:43:52.996562Z","shell.execute_reply":"2026-03-08T09:43:53.000624Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 6. Evaluation","metadata":{}},{"cell_type":"code","source":"## Plot learning curves\n\nif TRAINING or FINETUNING:\n    history_fil = {key: history.history[key] for key in ['cf1_score', 'val_cf1_score']}\n    history_fil2 = {key: history.history[key] for key in ['loss', 'val_loss']}\n    history_fil3 = {key: history.history[key] for key in ['learning_rate']}\n    \n    pd.DataFrame(history_fil).plot()\n    plt.ylabel(\"Accuracy\")\n    plt.xlabel(\"epochs\")\n    pd.DataFrame(history_fil2).plot()\n    plt.ylabel(\"Loss\")\n    plt.xlabel(\"epochs\")\n    #plt.axis([10, len(history_fil2['val_loss']), 0, history_fil2['val_loss'][10]+0.1*history_fil2['val_loss'][10]])\n    pd.DataFrame(history_fil3).plot()\n    plt.ylabel(\"Learning rate\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:55.473297Z","iopub.execute_input":"2026-03-08T09:43:55.473885Z","iopub.status.idle":"2026-03-08T09:43:55.478660Z","shell.execute_reply.started":"2026-03-08T09:43:55.473861Z","shell.execute_reply":"2026-03-08T09:43:55.477922Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Compare predicted with ground true masks\n\ndef display(display_list):\n    plt.figure(figsize=(15, 15))\n    title = ['Input Image', 'True Mask', 'Predicted Mask']\n\n    for i in range(len(display_list)):\n        plt.subplot(1, len(display_list), i+1)\n        state = str(tf.reduce_sum(tf.cast(display_list[i], dtype=tf.float32)).numpy())\n        plt.title(title[i]+ ' : ' + state)\n        plt.imshow(tf.keras.utils.array_to_img(display_list[i]))\n        #plt.axis('off')\n    plt.show()\n\ndef create_mask(pred_mask):\n    pred_mask = tf.cast(tf.math.greater(pred_mask, 0.75), dtype=tf.int8)\n    return pred_mask[0]\n\ndef show_predictions(dataset=None, num=1):\n    if dataset:\n        for image, mask in dataset.take(num):\n          pred_mask = model.predict(image, verbose=0)\n          display([image[0], mask[0], create_mask(pred_mask)])\n    else:\n        display([sample_image, sample_mask,\n                 create_mask(model.predict(sample_image[tf.newaxis, ...]))])\n\nshow_predictions(val_ds, 3)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:43:57.444088Z","iopub.execute_input":"2026-03-08T09:43:57.444635Z","iopub.status.idle":"2026-03-08T09:43:57.518709Z","shell.execute_reply.started":"2026-03-08T09:43:57.444615Z","shell.execute_reply":"2026-03-08T09:43:57.517871Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 7. Submission","metadata":{}},{"cell_type":"code","source":"## Utility: RLE encode \n\ndef _rle_one(arr):\n    \"\"\"Encode a single 2D binary mask into RLE list of pairs.\"\"\"\n    dots = np.where(arr.T.flatten() == 1)[0]\n    if len(dots) == 0:\n        return []\n    run_lengths = []\n    prev = -2\n    for b in dots:\n        if b > prev + 1:\n            run_lengths.extend((int(b) + 1, 0))\n        run_lengths[-1] += 1\n        prev = b\n    return run_lengths\n\ndef rle_encode(masks, fg_val=1):\n    return ';'.join(json.dumps(_rle_one(m)) for m in masks)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:44:10.778639Z","iopub.execute_input":"2026-03-08T09:44:10.779137Z","iopub.status.idle":"2026-03-08T09:44:10.784117Z","shell.execute_reply.started":"2026-03-08T09:44:10.779114Z","shell.execute_reply":"2026-03-08T09:44:10.783585Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"## Test prediction & submission \n\nif SUBMISSIONING:\n    folder_path_test = Path(\"/kaggle/input/recodai-luc-scientific-image-forgery-detection/test_images\")\n    test_pred = {}\n    \n    for file_path in natsorted(folder_path_test.glob(\"*.png\")):\n        image_id = os.path.splitext(os.path.basename(file_path))[0]\n        image = cv2.cvtColor(cv2.imread(str(file_path)), cv2.COLOR_BGR2RGB)\n        orig_sizes = image[:,:,0:1]\n        image = preprocess_images(image, False)\n        image_tensor = tf.expand_dims(tf.convert_to_tensor(image), 0)\n        test_probs = model.predict(image_tensor, verbose=0)[0]\n        test_preds = (test_probs>0.99).astype(dtype=np.uint8)*255\n        pred_np = postprocess_images(test_preds, orig_sizes)[:,:,0].numpy()\n        pred_np = (pred_np>122).astype(dtype=np.uint8)\n        if pred_np.sum() == 0:\n            test_pred[image_id] = \"authentic\"\n        else:\n            rle = rle_encode([pred_np], fg_val=1)\n            test_pred[image_id] = rle\n    \n    submission_df = pd.DataFrame([{\"case_id\": k, \"annotation\": v} for k, v in test_pred.items()])\n    submission_df.to_csv(\"submission.csv\", index=False)\n    print(\"✅ submission.csv saved!\")\n    print(submission_df.head())   ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-03-08T09:44:15.485276Z","iopub.execute_input":"2026-03-08T09:44:15.486014Z","iopub.status.idle":"2026-03-08T09:44:23.386386Z","shell.execute_reply.started":"2026-03-08T09:44:15.485992Z","shell.execute_reply":"2026-03-08T09:44:23.385498Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 8. Experimental code (e.g. for debugging)","metadata":{}},{"cell_type":"code","source":"# ## Plot learning curves for definition of start leraning rate\n# lrs = 1e-5 * (10 ** (np.arange(len(history.history[\"loss\"])) / 10)) # Define the learning rate array\n# plt.figure(figsize=(10, 6)) # Set the figure size\n# plt.grid(True) # Set the grid\n# plt.semilogx(lrs, history.history[\"loss\"]) # Plot the loss in log scale\n# plt.tick_params('both', length=10, width=1, which='both') # Increase the tickmarks size\n# #plt.axis([1e-5, 1e-0, 0, 10]) # Set the plot boundaries","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# max_x = 0\n# max_y = 0\n# max_z = 0\n# for image in labels_fo:\n#     if image.shape[0] > max_x:\n#         max_x = image.shape[0]\n#     if image.shape[1] > max_y:\n#         max_y = image.shape[1]  \n#     if image.shape[2] > max_z:\n#         max_z = image.shape[2]\n# print(max_x)\n# print(max_y)\n# print(max_z)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# base_model = tf.keras.applications.MobileNetV2(include_top=False, input_shape=[image_size, image_size, 3], weights='imagenet')\n\n# # Choose base model layers to be trained\n# base_model.trainable = False # freeze base model layers\n# max_layer_nr = len(base_model.layers)\n\n# layer_names = [#'block_1_expand_relu',   # 64x64x96\n#                #'block_3_expand_relu',   # 32x32x144\n#                'block_6_expand_relu',   # 16x16x192\n#                #'block_13_expand_relu',  # 8x8x576\n#                #'block_16_project',      # 4x4x320\n#                 ]\n\n# base_model_outputs = [base_model.get_layer(name).output for name in layer_names]\n# down_stack = tf.keras.Model(inputs=base_model.input, outputs=base_model_outputs, name='downsampling')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# image = cv2.imread('/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_images/forged/10138.png')\n# mask_raw = np.load('/kaggle/input/recodai-luc-scientific-image-forgery-detection/train_masks/10138.npy')\n# label = np.transpose(mask_raw, axes=[1,2,0])\n# image_lr = preprocess_images(image)\n# label_lr = preprocess_images(label)\n\n# plt.figure(figsize=(15, 15))\n# title = ['Input Image', 'True Mask']\n# display_list = [image_lr, label_lr]\n\n# for i in range(len(display_list)):\n#     plt.subplot(1, len(display_list), i+1)\n#     plt.title(title[i])\n#     plt.imshow(tf.keras.utils.array_to_img(display_list[i]))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# # Load a pretrained Vision Transformer (ViT-B16, ImageNet-1k)\n# model = vit.vit_b16(image_size=224,        # Input image size\n#                     pretrained=True,       # Load pretrained weights\n#                     include_top=False,      # Include classification head\n#                     pretrained_top=False)   # Use pretrained classifie)\n\n# model = tf.keras.Model(inputs=model.input, outputs=model.layers[-2].output, name='ViT')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# multi_out[:,1:,:].shape","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# attention_scores.numpy()[0, 1:, :].shape","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# image_lr_pp = PreProcess(name='preprocess_ViT')(tf.cast(image_lr, dtype=tf.float32))\n# featmap96_lr = model(np.expand_dims(image_lr_pp, axis=0))\n# norm_featmap96_lr = tf.norm(featmap96_lr, axis=-1, keepdims=True)\n# featmap96_lr_normed = featmap96_lr/norm_featmap96_lr\n# seq_image_lr = ((np.reshape(featmap96_lr_normed, (1,-1,768))).astype(dtype=np.float32))\n# seq_norm_image_lr = ((np.reshape(norm_featmap96_lr, (1,-1,1))).astype(dtype=np.float32))\n# multi_out, attention_scores = tf.keras.layers.Attention()([seq_image_lr, seq_norm_image_lr, seq_image_lr], return_attention_scores=True) #np.ones((1,197,1))\n# multi_out_lr = np.reshape(multi_out[:,1:,:], (14,14,1))\n# multi_out_scale = (multi_out_lr - multi_out_lr.min())/(multi_out_lr.max()-multi_out_lr.min())\n# multi_out_scale = (multi_out_scale>0.97).astype(dtype=np.float32)*multi_out_scale\n\n# att_map = np.reshape(attention_scores.numpy()[0, 1:, 1:].sum(axis=0), (14,14,1))\n# att_map_scale = (att_map - att_map.min())/(att_map.max()-att_map.min())\n\n# plt.figure(figsize=(10, 10))\n# title = ['Attention output', 'Attention scores map']\n# display_list = [multi_out_scale, att_map_scale]\n\n# for i in range(len(display_list)):\n#     plt.subplot(1, len(display_list), i+1)\n#     plt.title(title[i])\n#     plt.imshow(tf.keras.utils.array_to_img(display_list[i]))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# sim_score = ((seq_image_lr[0]@seq_image_lr[0].T))\n# sim_score_fil = ((sim_score>0.9) & (sim_score<0.95)).astype(np.float32)\n# multi_out_lr = np.reshape(sim_score, (4096,4096,1))\n# multi_out_lr_fil = np.reshape(sim_score_fil, (4096,4096,1))\n\n# sim_map = np.reshape(sim_score.mean(axis=0), (64,64,1))\n# sim_map_fil = np.reshape(sim_score_fil.mean(axis=0), (64,64,1))\n\n# plt.figure(figsize=(15, 15))\n# title = ['Similiraty score', 'Similiraty score filtered', 'Similiraty map', 'Similiraty map filtered']\n# display_list = [multi_out_lr, multi_out_lr_fil, sim_map, sim_map_fil]\n\n# for i in range(len(display_list)):\n#     plt.subplot(1, len(display_list), i+1)\n#     plt.title(title[i])\n#     plt.imshow(tf.keras.utils.array_to_img(display_list[i]))","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}